SpringDataJp
1. 集成Spring+SpringDataJpa
1)引入相关jar包(项目所有包)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.itsource</groupId>
<artifactId>springdatajpa</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<org.springframework.version>4.2.5.RELEASE</org.springframework.version>
<org.hibernate.version>4.3.8.Final</org.hibernate.version>
<spring-data-jpa.version>1.9.0.RELEASE</spring-data-jpa.version>
<com.fasterxml.jackson.version>2.5.0</com.fasterxml.jackson.version>
</properties>
<dependencies>
<!-- Spring的支持包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version}</version>
<scope>test</scope>
</dependency>
<!-- 引入web前端的支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!-- SpringMCV上传需要用到io包-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 文件上传用到的包 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.2</version>
</dependency>
<!-- SpringMVC的json支持包 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${com.fasterxml.jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${com.fasterxml.jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${com.fasterxml.jackson.version}</version>
</dependency>
<!-- hibernate的支持包 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${org.hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${org.hibernate.version}</version>
</dependency>
<!-- SpringData的支持包 -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>${spring-data-jpa.version}</version>
</dependency>
<!-- SpringData的擴展包 -->
<dependency>
<groupId>com.github.wenhao</groupId>
<artifactId>jpa-spec</artifactId>
<version>3.1.1</version>
<!-- 把所有的依賴都去掉 -->
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
<!-- 測試包 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<!-- 这个scope 只能作用在编译和测试时,同时没有传递性。表示在运行的时候不添加此jar文件 -->
<scope>provided</scope>
</dependency>
</dependencies>
</project>
2)配置applicationContext.xml
<!-- 扫描service部分的包 -->
<context:component-scan base-package="cn.itsource.aisell.service" />
<context:property-placeholder location="classpath:jdbc.properties" />
<!-- 配置连接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<!--连接数据4个属性 -->
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<!--maxActive: 最大连接数量 -->
<property name="maxActive" value="150" />
<!--minIdle: 最小空闲连接 -->
<property name="minIdle" value="5" />
<!--maxIdle: 最大空闲连接 -->
<property name="maxIdle" value="20" />
<!--initialSize: 初始化连接 -->
<property name="initialSize" value="30" />
<!-- 用来配置数据库断开后自动连接的 -->
<!-- 连接被泄露时是否打印 -->
<property name="logAbandoned" value="true" />
<!--removeAbandoned: 是否自动回收超时连接 -->
<property name="removeAbandoned" value="true" />
<!--removeAbandonedTimeout: 超时时间(以秒数为单位) -->
<property name="removeAbandonedTimeout" value="10" />
<!--maxWait: 超时等待时间以毫秒为单位 1000等于60秒 -->
<property name="maxWait" value="1000" />
<!-- 在空闲连接回收器线程运行期间休眠的时间值,以毫秒为单位. -->
<property name="timeBetweenEvictionRunsMillis" value="10000" />
<!-- 在每次空闲连接回收器线程(如果有)运行时检查的连接数量 -->
<property name="numTestsPerEvictionRun" value="10" />
<!-- 1000 * 60 * 30 连接在池中保持空闲而不被空闲连接回收器线程 -->
<property name="minEvictableIdleTimeMillis" value="10000" />
<property name="validationQuery" value="SELECT NOW() FROM DUAL" />
</bean>
<!-- 集成hibernate的jpa功能 -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--待扫描的实体类包,不再需要persistence.xml了 -->
<property name="packagesToScan" value="cn.itsource.aisell.domain" />
<!-- 3.配置JPA的实现 -->
<!-- private JpaVendorAdapter jpaVendorAdapter; -->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!-- org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter -->
<!-- private boolean showSql = false;是否显示sql语句 -->
<property name="showSql" value="true" />
<!-- private boolean generateDdl = false;是否建表 -->
<property name="generateDdl" value="false" />
<!-- private String databasePlatform;原来方言 -->
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
</bean>
</property>
</bean>
<!-- Jpa 事务配置 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- 注解声明式事务管理 -->
<tx:annotation-driven />
<!-- Spring Data Jpa配置 ********************************************-->
<!-- base-package:扫描的包 -->
<jpa:repositories base-package="cn.itsource.pss.repository" transaction-manager-ref="transactionManager"
entity-manager-factory-ref="entityManagerFactory" /><beans>
3)准备Domain
@Entity
@Table(name="employee")
public class Employee extends BaseDomain {
private String username;
private String password;
private String email;
private Integer age;
//省略getter,setter与toString(注意:Alt+Insert可以自动生成) }
4)完成Repository的功能
package cn.itsource.pss.repository;
import cn.itsource.pss.domain.Employee; import org.springframework.data.jpa.repository.JpaRepository;
/** * 必需继续JpaRepository<1v,2v> * 1v:代表你要操作的是哪一个domain对象 * 2v:这个domain对象的主键的类型 */
public interface EmployeeRepository extends JpaRepository<Employee, Long> { }
2. SpringDataJpa认识使用
1) SpringDataJpa是JPA规范的再次封装底层还是使用了Hibernate的JPA技术实现,引用JPQL的查询语句
2) 简化创建 JPA 数据访问层和跨存储的持久层功能
3)ORM框架:对象关系映射(JPA)
4) SimpleJpaRepository类实现了JpaRepository接口和JpaSpecificationExecutor接口,
而SpringDataJpa自动创建了一个代理类proxy,实现了我们自己写的EmployeeRepository接口,同时继承了SimpleJpaRepository类
3. JpaRepository基本功能
1)分页:
Pageable page = new PageRequest(page,size);
employeeRepository.findAll(page);
2)排序:
Sort sort = new Sort(Sort.Direction.DESC,"age");
employeeRepository.findAll(sort);
3)分页排序:
Pageable page = new PageRequest(page,size,sort);
employeeRepository.findAll(sort);
4)高级查询(继承了JpaRepository的接口中):
① List findByUsername(String username); //根据用户名Username查询
② List findByUsernameLike(String username);//根据用户名模糊查询
③ List findByUsernameLikeAndEmailLike(String username,String email);//多条件模糊查询
之所以可以用以上方法查询,是因为SpringDataJpa准备的一套规范
5) 复杂的查询SpringDataJpa规范无法使用,需要自己写sql
@Query("select o from Employee o where o.username like ?1")
List<Employee> query01(String username); //按参数位置赋值
@Query("select o from cn.itsource.pss.domain.Employee o where o.username like :username and o.email like :email")
List<Employee> query03(@Param("username") String username,@Param("email") String email);//按名称赋值
4. JpaSpecificationExecutor接口的认识
1)jpa使用paSpecificationExecutor技术完成了Criteria标准(一种比JQPL,HQL更面向对象的语言),
2)使用
Root:查询哪个表(定位到表和字段-> 用于拿到表中的字段)
* 可以查询和操作的实体的根
* Root 相当于 from Employee
* Root 相当于 from Product
CriteriaQuery:查询哪些字段,排序是什么(主要是把多个查询的条件连系起来)
CriteriaBuilder:字段之间是什么关系,如何生成一个查询条件,每一个查询条件都是什么方式
* 主要判断关系(和这个字段是相等,大于,小于like等)
Predicate(Expression):单独每一条查询条件的详细描述 整个 where xxx=xx and yyy=yy …
① 单个条件
List<Employee> emps = employeeRepository.findAll(
new Specification<Employee>() {
@Override
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Path path = root.get("username");//拿到要做查询的字段
Predicate p = cb.like(path, "%1%");//like代表做模糊查询,后面就是它的条件值
return p;
}
}
);
for (Employee emp : emps) {
System.out.println(emp);
} }
②多个条件
public void testFind02() {
Specification spec = new Specification<Employee>() {
@Override
public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {
//加上第一个条件: username like '%1%'
Path path1 = root.get("username");
Predicate p1 = cb.like(path1, "%1%");
//加上第二个条件: email like '%2%'
Path path2 = root.get("email");
Predicate p2 = cb.like(path2,"%2%");
//加上第二个条件: age < 20
Path path3 = root.get("age");
Predicate p3 = cb.lt(path3, 20);
//下面是加上or的条件的方案
//Predicate p3 = cb.or(p1,p2);
//把两个查询条件放到query对象中去(条件使用where)
CriteriaQuery where = query.where(p1, p2, p3);//同样可以使用:cb.add(p1,p2,p3)
//返回查询条件
return where.getRestriction();
}
};
List<Employee> emps = employeeRepository.findAll(spec);
for (Employee emp : emps) {
System.out.println(emp);
}
}
③ 既分页有排序
Sort sort = new Sort(Sort.Direction.DESC,"age");
Pageable page = new PageRequest(page,size,sort);
employeeRepository.findAll(p,page);//p 为Predicate类型值
5. jpa-spec插件
1)主要是用于简化JpaSpecificationExecutor
2) 插件地址:(https://github.com/wenhao/jpa-spec)
3) Maven导包
<dependency>
<groupId>com.github.wenhao</groupId>
<artifactId>jpa-spec</artifactId>
<version>3.2.3</version>
</dependency>
- 使用
public void testSpecFind01() { Specification<Employee> spec = Specifications.<Employee>and() .like("username", "%1%") .build(); List<Employee> emps = employeeRepository.findAll(spec); }
6. query查询条件
1)准备一个对象Query接受前台传来的查询数据,
2)代码
public void testSpecFindByQuery() {
//创建模块一个Query对象
EmployeeQuery baseQuery = new EmployeeQuery();
//下面的都是测试的条件,可以任何放开进行测试
//baseQuery.setUsername("1");
//baseQuery.setAge(20);
//baseQuery.setEmail("2");
//baseQuery.setOrderByName("username");
//baseQuery.setOrderByType("DESC");
//baseQuery.setCurrentPage(2);
//baseQuery.setPageSize(5);
//like(条件boolean值,字段,值)
//StringUtils.isNotBlank(baseQuery.getUsername()):判断参数不为空且不为空字符串
Specification<Employee> spec = Specifications.<Employee>and()
.like(StringUtils.isNotBlank(baseQuery.getUsername()), "username","%"+baseQuery.getUsername()+"%")
.like(StringUtils.isNotBlank(baseQuery.getEmail()), "email","%"+baseQuery.getEmail()+"%")
.lt(baseQuery.getAge()!=null, "age",baseQuery.getAge())
.build();
//这里确定是否需要排序
Sort sort = null;
if(baseQuery.getOrderByName()!=null){
Sort.Direction desc = "DESC".equals(baseQuery.getOrderByType())?Sort.Direction.DESC:Sort.Direction.ASC;
sort = new Sort(desc,baseQuery.getOrderByName());
}
Pageable pageable = new PageRequest(baseQuery.getJpaPage(), baseQuery.getPageSize(),sort);
Page<Employee> page = employeeRepository.findAll(spec, pageable);
for (Employee emp : page) {
System.out.println(emp);
}
}
7.SpringDataJpa扩展
1)创建一个BaseRepository接口,继承JpaSpectfifcationExecutor,JpaRepository接口。接口中可以定义自己的方法。
SimpleJpaRepository类实现了JpaSpectfifcationExecutor,JpaRepository接口,SpringDataJpa自动创建的实现类继承
SimpleJpaRepository类和EmployeeRepository接口,而BaseRepository中的方法没有实现,所以应该创建一个BaseRepostoryImpl
来实现BaseRepository接口并继承SimpleJpaRepository
2)代码
BaseRepository:
@NoRepositoryBean
public abstract interface BaseRepository<T,ID extends Serializable>
extends JpaRepository<T,ID>,JpaSpecificationExecutor<T> {
//根据Query拿到分页对象(分页)
Page findPageByQuery(BaseQuery baseQuery);
//根据Query拿到对应的所有数据(不分页)
List<T> findByQuery(BaseQuery baseQuery);
//根据jpql与对应的参数拿到数据
List findByJpql(String jpql, Object... values);
}
BaseRepositoryImpl:
public class BaseRepositoryImpl<T,ID extends Serializable> extends SimpleJpaRepository<T,ID> implements BaseRepository<T,ID> {
private final EntityManager entityManager;
//必需要实现父类的这个构造器
public BaseRepositoryImpl(Class<T> domainClass, EntityManager em) {
super(domainClass, em);
this.entityManager = em;
}
@Override
public Page findPageByQuery(BaseQuery baseQuery) {
//第一步:拿到所有高级查询条件
Specification spec = baseQuery.specification();
//第二步:拿到排序的值
Sort sort = baseQuery.sort();
//第三步:根据条件查询分页数据并且返回
Pageable pageable = new PageRequest(baseQuery.getNewcurrentPage(), baseQuery.getPageSize(),sort);
Page<T> page = super.findAll(spec, pageable);
return page;
}
@Override
public List<T> findByQuery(BaseQuery baseQuery) {
//第一步:拿到所有高级查询条件
Specification spec = baseQuery.specification();
//第二步:拿到排序的值
Sort sort = baseQuery.sort();
//第三步:拿到数据返回
return findAll(spec, sort);
}
@Override
public List findByJpql(String jpql, Object... values) {
//第一步:创建Query对象
Query query = entityManager.createQuery(jpql);
//第二步:把值设置到Query对象中去
if (values!=null) {
for (int i = 0; i < values.length; i++) {
query.setParameter(i + 1, values[i]);
}
}
//第三步:返回数据
return query.getResultList();
}
}
同时还要告诉SpringDataJpa在创建对象是不使用SimpleJpaRepository,而使用自己写的BaseRepositoryImpl
① 先创建一个BaseRepositoryFactoryBean类
public class BaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> extends JpaRepositoryFactoryBean<T,S,ID> {
@Override
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new MyRepositoryFactory<T,ID>(entityManager); //注:这里创建是我们的自定义类
}
//继承JpaRepositoryFactory后,把返回的对象修改成我们自己的实现
private static class MyRepositoryFactory<T,ID extends Serializable> extends JpaRepositoryFactory {
private final EntityManager entityManager;
/**
* Creates a new {@link JpaRepositoryFactory}.
*
* @param entityManager must not be {@literal null}
*/
public MyRepositoryFactory(EntityManager entityManager) {
super(entityManager);
this.entityManager = entityManager;
}
//这里返回最后的功能对象
@Override
protected Object getTargetRepository(RepositoryInformation information) {
return new BaseRepositoryImpl<T,ID>((Class<T>)information.getDomainType(),entityManager);
}
//确定功能对象的类型
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return BaseRepositoryImpl.class;
}
}
}
② 在applicationContext.xml中配置
③ EmployeeRepository继承BaseRepositoryImpl