Spring Data Jpa是Spring中为简化数据库操作,基于JPA封装的一套框架。这个框架的主要作用是为了把我们从反复,复杂的数据库操作中解放出来。
首先,Spring Data Jpa的官方学习地址:http://docs.spring.io/spring-data/jpa/docs/current/reference/html/
其次,该项目测试代码地址:https://github.com/mj3018/7-4test/tree/master/springjpaTest 这个项目上面测试了CRUDRepository接口,JpaRepository接口,JpaSpecificationExecutor接口中的
CRUD操作,多条件分页查询,自定义接口,自定义接口实现根据EntityManager进行数据库操作等
1.1 核心概念
- Spring Data Jpa的核心接口是Repository,这个接口是一个空接口,在使用的时候需要只需要指定该接口的Entity类(对应泛型T)及id类型(对应ID)即可。
public interface Repository<T, ID extends Serializable> {
}
- CRUDRepository接口,这个接口继承Repository接口,添加CRUD方法。
@NoRepositoryBean
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> {
<S extends T> S save(S entity);
<S extends T> Iterable<S> save(Iterable<S> entities);
T findOne(ID id);
boolean exists(ID id);
Iterable<T> findAll();
Iterable<T> findAll(Iterable<ID> ids);
long count();
void delete(ID id);
void delete(T entity);
void delete(Iterable<? extends T> entities);
void deleteAll();
}
- PagingAndSortingRepository 继承CRUDRepository接口,新增分页与排序的功能。
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
}
- JpaRepository 继承PagingAndSortingRepository接口,在PagingAndSortingRepository接口基础上新增了一些比较常用的数据库操作,比如:多条件查询。
@Override
<S extends T> List<S> findAll(Example<S> example);
- 另外在SpringDataJpa中一个比较重要的接口:JpaSpecificationExecutor。通过这个接口可以实现多条件分页查询。
//多条件查询某一个具体的entity
T findOne(Specification<T> spec);
//多条件查询满足条件的所有的entity
List<T> findAll(Specification<T> spec);
//多条件分页查询
Page<T> findAll(Specification<T> spec, Pageable pageable);
//多条件排序查询
List<T> findAll(Specification<T> spec, Sort sort);
//多条件统计满足条件的总数
long count(Specification<T> spec);
Specification这个是查询的条件,构造这个查询条件的方式为:User对应的是Entity类,UserDto是查询条件。
private Specification<User> buildSpecification(final UserDto userDto) {
return new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Predicate predicate = cb.conjunction();
if(userDto.getName() != null) {
predicate.getExpressions().add(
cb.like(root.<String>get("name"),userDto.getName()+"%")
);
}
if(userDto.getAddress() != null) {
predicate.getExpressions().add(
cb.like(root.<String>get("address"),userDto.getAddress()+"%")
);
}
if(userDto.getPhone() != null) {
predicate.getExpressions().add(
cb.like(root.<String>get("phone"),userDto.getPhone()+"%")
);
}
return predicate;
}
};
}
这里也把一个多条件分页查询的使用代码贴在这里吧:UserDto是查询的条件,Specification是根据UserDto封装出来的查询条件,Pageable对应的是分页条件。
@Test
public void testMultiQueryPage(){
UserDto userDto = new UserDto();
userDto.setName("name");
Specification<User> specs = buildSpecification(userDto);
Pageable pageable = new PageRequest(1,2);
Page<User> page = this.userService.findAdd(specs,pageable);
Iterator<User> iterator = page.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
System.out.println("************************************************");
}
System.out.println(page.getContent());
}
- spring Data Jpa配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache-4.1.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
http://www.springframework.org/schema/data/repository
http://www.springframework.org/schema/data/repository/spring-repository-1.5.xsd">
<!-- scan the package and the sub package -->
<context:component-scan base-package="com.spring"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp" />
</bean>
<!--配置数据库-->
<bean class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close" id="dataSource">
<property name="driverClassName" value="org.postgresql.Driver" />
<property name="url" value="jdbc:postgresql://localhost:5432/demo" />
<property name="username" value="demo" />
<property name="password" value="demo" />
<property name="testOnBorrow" value="true" />
<property name="testOnReturn" value="true" />
<property name="testWhileIdle" value="true" />
<property name="timeBetweenEvictionRunsMillis" value="1800000" />
<property name="numTestsPerEvictionRun" value="3" />
<property name="minEvictableIdleTimeMillis" value="1800000" />
<property name="validationQuery" value="SELECT version();" />
<property name="initialSize" value="1" />
<property name="maxActive" value="32" />
<property name="maxIdle" value="16" />
</bean>
<!-- 使用cglib进行动态代理 -->
<aop:aspectj-autoproxy proxy-target-class="true" />
<!-- 支持注解方式声明式事务 -->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
<!-- dao -->
<jpa:repositories base-package="com.spring.repository" repository-impl-postfix="Impl"
entity-manager-factory-ref="entityManagerFactory" transaction-manager-ref="transactionManager" />
<!-- 实体管理器 -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.spring" />
<property name="persistenceProvider">
<bean class="org.hibernate.ejb.HibernatePersistence" />
</property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="false" />
<property name="database" value="POSTGRESQL" />
<property name="databasePlatform" value="org.hibernate.dialect.PostgreSQLDialect" />
<property name="showSql" value="true" />
</bean>
</property>
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
</property>
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.query.substitutions" value="true 1, false 0" />
<entry key="hibernate.default_batch_fetch_size" value="16" />
<entry key="hibernate.max_fetch_depth" value="2" />
<entry key="hibernate.generate_statistics" value="true" />
<entry key="hibernate.bytecode.use_reflection_optimizer" value="true" />
<entry key="hibernate.cache.use_second_level_cache" value="false" />
<entry key="hibernate.cache.use_query_cache" value="false" />
</map>
</property>
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean class="org.springframework.data.web.config.SpringDataWebConfiguration"></bean>
</beans>
在上面这个配置文件中几个比较重要的配置有:
- 支持SpringDataJpa数据库操作的包采用base-package、配置自定义实现Dao操作的类采用repository-impl-postfix、指定EntityManagerFactory采用entity-manager-factory-ref、事物的指定采用transaction-manager-ref
- 接下来是实体管理器,这里需要指定persistenceProvider(就是使用SpringDataJpa的时候需要指定实现这个数据库操作的具体的实现ORM框架,这里采用Hibernate)的值为org.hibernate.ejb.HibernatePersistence。指定一个packageToScan指定扫描的包。接下来其他的配置基本上都是数据库的一些配置。
<!-- dao -->
<jpa:repositories base-package="com.spring.repository" repository-impl-postfix="Impl"
entity-manager-factory-ref="entityManagerFactory" transaction-manager-ref="transactionManager" />
<!-- 实体管理器 -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.spring" />
<property name="persistenceProvider">
<bean class="org.hibernate.ejb.HibernatePersistence" />
</property>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="false" />
<property name="database" value="POSTGRESQL" />
<property name="databasePlatform" value="org.hibernate.dialect.PostgreSQLDialect" />
<property name="showSql" value="true" />
</bean>
</property>
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
</property>
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.query.substitutions" value="true 1, false 0" />
<entry key="hibernate.default_batch_fetch_size" value="16" />
<entry key="hibernate.max_fetch_depth" value="2" />
<entry key="hibernate.generate_statistics" value="true" />
<entry key="hibernate.bytecode.use_reflection_optimizer" value="true" />
<entry key="hibernate.cache.use_second_level_cache" value="false" />
<entry key="hibernate.cache.use_query_cache" value="false" />
</map>
</property>
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>