spring学习:spring data jpa
2018-01-15 00:00
435 查看
工程改进
在前一篇文章中,我们讨论过对工程的改进。在最开始的思路里,我们是定义了ContactService接口,在具体的ContactServiceImpl里直接引用sessionFactory或者EntityManager来操作数据。在这种情况下,我们的实际业务逻辑却直接和数据操作部分耦合起来了。于是,为了使得这部分不是紧密耦合的。我们定义了dao包,将真正数据访问的部分放在dao的实现里。另外,考虑到对数据的基本增删查改算是最常用的操作。几乎每个对象都需要,我们完全可以定义一个通用的给其他dao实现来集成。因为要足够通用,我们采用泛型的参数接口。于是就得出了一个如下图的设计思路:
![](https://oscdn.geek-share.com/Uploads/Images/Content/201910/20/ec4c1fb958ecc77a4fc7c27719979574.png)
在这种实现里,我们定义了dao的抽象接口。理论上它可以进行CRUD操作的数据可以是继承自Object的任何对象。然后我们需要继承dao接口来定义自己的特定dao操作部分。而针对dao本身这些基础的crud操作就都由AbstractHbnDao实现了。我们定义自己的实现时只需要继承AbstractHbnDao,然后实现自定义的那部分接口就可以了 。
这种方式带来了不少的便利。当然,也带来了一个可以改进的地方。既然这个dao是用来针对通用的对象操作的。而且AbstractHbnDao也是定义的一个通用操作。那么可不可以直接将它们实现好作为一个通用的类库呢?这样以后我们就连这部分的定义都省了。真是基于这个观点,我们找到了spring data jpa。
引入spring data jpa
spring data jpa是一个通用的DAO框架。它除了支持基础的crud操作之外,还分页、排序和批处理操作。我们原来很多需要手工定义的dao类它都通过动态代理生成了。spring data jpa的类结构图如下:
![](https://oscdn.geek-share.com/Uploads/Images/Content/202001/05/1b06abecc247941321031ab1d0b474c9.png)
我们在工程中使用它们就比较简单了。首先定义一个自定义的dao接口。比如本例中的ContactDao:
Java代码
![](https://oscdn.geek-share.com/Uploads/Images/Content/201704/134beedad545e7b5f4a9bd2dd4de7500.png)
package com.yunzero.dao;
import java.util.List;
import org.springframework.data.repository.CrudRepository;
import com.yunzero.model.Contact;
public interface ContactDao extends CrudRepository<Contact, Long> {
List<Contact> findByEmailLike(String email);
}
在这里我们可以看到,我们自定义的方法findByEmailLike。按照以往的思路,既然这是我们定义的方法,总该要我们自己去实现它吧?可是在这里,居然不用自己去实现它。我们看后续的代码:
ContactService:
Java代码
![](https://oscdn.geek-share.com/Uploads/Images/Content/201704/134beedad545e7b5f4a9bd2dd4de7500.png)
package com.yunzero.service;
import java.util.List;
import com.yunzero.model.Contact;
public interface ContactService {
void createContact(Contact contact);
List<Contact> getContacts();
List<Contact> getContactsByEmail(String email);
Contact getContact(Long id);
void updateContact(Contact contact);
void deleteContact(Long id);
}
真正重点的是ContactServiceImpl:
Java代码
![](https://oscdn.geek-share.com/Uploads/Images/Content/201704/134beedad545e7b5f4a9bd2dd4de7500.png)
package com.yunzero.service.impl;
import static org.springframework.util.Assert.notNull;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.inject.Inject;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.yunzero.dao.ContactDao;
import com.yunzero.model.Contact;
import com.yunzero.service.ContactService;
@Service
@Transactional
public class ContactServiceImpl implements ContactService {
@Inject private ContactDao contactDao;
@Override
public void createContact(Contact contact) {
notNull(contact, "contact can't be null");
contactDao.save(contact);
}
@Override
public List<Contact> getContacts() {
Iterable<Contact> iterable = contactDao.findAll();
Iterator<Contact> iterator = iterable.iterator();
List<Contact> contacts = new ArrayList<Contact>();
while (iterator.hasNext()) {
contacts.add(iterator.next());
}
return contacts;
}
@Override
public List<Contact> getContactsByEmail(String email) {
notNull(email, "email can't be null");
return contactDao.findByEmailLike("%" + email + "%");
}
@Override
public Contact getContact(Long id) {
notNull(id, "id can't be null");
return contactDao.findOne(id);
}
@Override
public void updateContact(Contact contact) {
notNull(contact, "contact can't be null");
contactDao.save(contact);
}
@Override
public void deleteContact(Long id) {
notNull(id, "id can't be null");
contactDao.delete(id);
}
}
这里最玄乎的就是contactDao有了save, delete等方法了。而实际上ContactDao继承的不是接口吗?而且更猛的是,我在ContactDao里定义的方法findByEmailLike没有定义实现居然可以拿来直接用!这一切看起来像魔法一样,实在是太不可思议了。
实际上,这一切就是spring data jpa的动态代理方法带来的好处。我们定义的方法只要按照它定义的规则来命名,它就可以通过反射将名字和各种操作方法和映射起来。有点convention over configuration的感觉哈。对于具体spring data jpa是怎么实现这些的,这里不再赘述,在后面的文章中会详细讨论。
配置
上面就是所有的代码了。除了前面代码部分,还要一个需要注意的就是配置。这里一个典型的配置如下:Java代码
![](https://oscdn.geek-share.com/Uploads/Images/Content/201704/134beedad545e7b5f4a9bd2dd4de7500.png)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.8.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">
<context:property-placeholder location="classpath:/environment.properties" />
<bean id="dataSource"
class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close"
p:driverClassName="${dataSource.driverClassName}"
p:url="${dataSource.url}"
p:username="${dataSource.username}"
p:password="${dataSource.password}" />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource"
p:packagesToScan="com.yunzero.model">
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.show_sql">false</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory" />
<tx:annotation-driven />
<jpa:repositories base-package="com.yunzero.dao" />
<context:component-scan base-package="com.yunzero.service.impl" />
</beans>
看起来和前面的配置没什么差别。唯一的一点就是引入了jpa的命名空间,并引入了这么一个配置。<jpa:repositories base-package="com.yunzero.dao" />
当然,在我们依赖的包里也需要引入spring-data-jpa,如下是一个典型的引用内容:
Java代码
![](https://oscdn.geek-share.com/Uploads/Images/Content/201704/134beedad545e7b5f4a9bd2dd4de7500.png)
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.8.0.RELEASE</version>
</dependency>
就只要这么几步,整个数据操作的部分就完成了。详细的实现可以参考后面的附件。
总结
spring data jpa是对dao方式操作数据的进一步优化。它带来的不仅仅是一个抽象的通用数据访问接口,更多的是一个按照命名规范自动生成的数据访问实现。这些玄妙之处值得后续好好学习。相关文章推荐
- jpa学习3-spring data jpa 入门环境搭建
- 【SpringBoot学习笔记】SpringBoot_03_SpringData—JpaRepository部分字段查询功能
- 深入学习spring-boot系列(二)--使用spring-data-jpa
- SpringDataJPA学习记录(一)--环境配置
- Spring boot学习之spring-data-jpa的使用(一)
- SpringDataJPA学习记录(三)--复杂查询的封装
- 深入学习spring-boot系列(二)--使用spring-data-jpa
- Spring boot学习之spring-data-jpa的使用(一)
- SpringData JPA的学习笔记之环境搭建
- 深入学习spring-boot系列(二)--使用spring-data-jpa
- 深入学习spring-boot系列(二)--使用spring-data-jpa
- SpringBoot学习(五)操作数据库Spring-Data-JPA
- spring-data-jpa学习(一)环境配置1.3
- jpa学习4--spring data jpa 中的JpaRepository 方法定义规范
- springboot学习三(springboot结合spring data jpa和freemarker显示)
- 深入学习spring-boot系列(二)--使用spring-data-jpa
- 深入学习spring-boot系列(二)--使用spring-data-jpa
- springboot 学习笔记【4】Spring Boot中使用Spring-data-jpa
- 跟着问题学习Spring Data Jpa---Jpa是什么
- Java学习笔记13——Spring Data JPA