SpringDATAJPA封装JPA操作
依赖
spring-data-jpa
Hibernate相关依赖
c3p0依赖
mysql驱动
<properties>
<spring.version>5.0.2.RELEASE</spring.version>
<hibernate.version>5.0.7.Final</hibernate.version>
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.12</log4j.version>
<c3p0.version>0.9.1.2</c3p0.version>
<mysql.version>5.1.6</mysql.version>
</properties>
<dependencies>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
</dependency>
<!--spring beg-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring对orm框架支持包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!--spring end-->
<!--hibernate-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.2.1.Final</version>
</dependency>
<!--hibernate end-->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>${c3p0.version}</version>
</dependency>
<!--日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!--真正用到SpringDATAJPA的的包-->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.9.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!--el 使用SpringDATAJPA必须引入-->
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.4</version>
</dependency>
</dependencies>
spring配置文件
applicationContext.xml
约束头
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd"
>
</beans>
spring和SpringDATAJPA的配置
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd"
>
<!--LocalContainerEntityManagerFactoryBean是spring提供的entityManagerFactoty实现类-->
<bean id="entityManagerFactoty" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!--连接池-->
<property name="dataSource" ref="dataSource"/>
<!--实体类的包 自动去扫描包下所有类-->
<property name="packagesToScan" value="cn.vivi.domain"/>
<property name="persistenceProvider" >
<!--jpa实现的提供-->
<bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
</property>
<!--jpa提供商的适配器-->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!--自动创建数据库表-->
<property name="generateDdl" value="false"/>
<!--指定数据库类型-->
<property name="database" value="MYSQL"/>
<!--显示sql-->
<property name="showSql" value="true"/>
<!--数据库方言:支持的特有语法-->
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>
</bean>
</property>
<!--jpa方言:高级特性-->
<property name="jpaDialect" >
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
</property>
</bean>
<!--数据库连接池-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="root"/>
<property name="password" value="root"/>
<property name="jdbcUrl" value="jdbc:mysql:///jpa?characterEncoding=utf8"/>
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
</bean>
<!--整合SpringDATAJPA-->
<jpa:repositories base-package="cn.vivi.dao" transaction-manager-ref="transactionManager" entity-manager-factory-ref="entityManagerFactoty"/>
<!--事务管理器-->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactoty"/>
</bean>
<!--声明式事务-->
<!--扫描注解-->
<context:component-scan base-package="cn.vivi"/>
</beans>
编写实体类
编写dao层几口,不需要编写dao层捷库的实现类。
规范
1.继承两个接口 JpaRepository,JpaSpecificationExecutor.
2 提供相应的泛型
JpaRepository<操作的实体类型,主键属性的类型>
JpaSpecificationExecutor<操作的实体类型>
此时dao已经封装了基本的crud操作
测试查询:
基本方法
findOne(),根据主键查询一个
save()保存或更新,根据传递的对象是否有id主键,如果有id代表更新,否则为保存
delete()根据主键删除
findAll()查询全部
查询全部:count();
是否存在:boolean exists(id):
原理分析
dao接口继承JpaRepository,JpaSpecificationExecutor。
动态代理基于接口生成实现类对象。
Spring借助JdkDynamicAopProxy对象创建实体类
实现InvocationHandler接口的invoke方法,生成SimpleJpaRepository对象,即dao的实现类。
完整逻辑
1 通过Spring借助JdkDynamicAopProxy对象动态代理创建实体类SimpleJpaRepository对象
2 SimpleJpaRepository实现JpaRepository,JpaSpecificationExecutor接口,实现了调用jpa接口
3 jpa指示hibernate完成增删改查,hibernate底层封装了jdbc。
getOne与findOne区别
findOne():底层调用entityManager.find(); 立即加载
getOne():底层调用entityManager.getReference(Class,id);延迟加载
getOne返回的是一个动态代理对象,使用的时候才加载。
JPQL查询
需要将jpql语句配置到接口方法上
1.特有的查询:需要在dao接口上配置方法
2.在新添加的方法上,使用注解的形式配置jpql查询语句
3.注解:@query
之后就可以直接调用
多占位符与参数顺序有关,与形参名无关。
占位符后加数字可以指定形参取的位置。
更新操作需要加入@Modifying注解,query不支持DML操作。且返回值类型void or int/Integer
调用的时候需要加入事务以及rollback回滚false,默认为true
SQL查询
注解:@query,属性nativequery默认为false,改为true即为sql语句查询。
sql查询的返回值为List<Object []> 查询到之后遍历也需要对数组进行Arrays.toString
方法名称规则查询 类似于通用mapper,区别是需要提前在dao接口声明。
对jpql查询更加深入的封装,
只需要按照SpringDATAJPA提供的方法名称规则定义的方法,不需要再去写jpql;
方法名的约定:
findBy:查询
对象中的属性名,查询的条件。
findByCustName(),根据客户名查询
Specification动态查询
动态构建相应的查询语句
方法列表:
- T findOne(Specification spec); //查询单个
- List findAll(Specification spec); //查询多个结果
- Page findAll(Specification spec,Pageable pageable);//分页查询,加入分页参数
- List findAll(Specification spec,Sort sort);//排序查询,加入排序参数
- long count(Specification spec);//统计数量查询
Specification:代表查询条件
自定义Specification实现类,实现:toPredicate,封装查询条件
root:查询的根对象,查询的任何属性都可以从根对象中获取
CriteriaQuery:顶层查询对象,自定义查询方式(一般不用)
CriteriaBuilder:查询构造器,封装了很多查询条件(模糊查询,精准匹配等)
查询条件:
1.查询方式
cb对象
2.比较的属性名称
root对象
多条件查询
所有的条件构建都使用cb。
常见cb里面的方法:
gt,lt,ge,le,like等方法 需要得到path对象后,指定比较的参数类型,再去比较
as.class。
多结果就用findAll
排序查询,
- List findAll(Specification spec,Sort sort);//排序查询,加入排序参数
需要new一个sort
构造方法中:
参数1,正序还是倒序(枚举类型Sort.Direction.)。
参数2,排序的属性。实体类中的属性名称。
分页查询
- Page findAll(Specification spec,Pageable pageable);//分页查询,加入分页参数
返回一个Page对象,里面有封装好的总页数和总条数以及数据列表map。
pageable是一个接口,实现类PageRequest构造传入两个参数,页码与每页条数。页码从0开始
即分页又排序的查询
//实现可以多种查询方式结合:模糊搜索,排序,分页
@Test
public void testFindCustomerWithSort2() {
Page<Customer> page = customerDao.findAll(new Specification<Customer>() {
@Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
Predicate predicate1 = criteriaBuilder.like(root.get("custName"), "%未来%");
Predicate predicate = criteriaQuery.where(predicate1).orderBy(criteriaBuilder.asc(root.get("custId"))).getRestriction();
return predicate;
}
}, new PageRequest(0, 10));
System.out.println(page.getTotalElements());
System.out.println(page.getTotalPages());
List<Customer> list = page.getContent();
for (Customer customer : list) {
System.out.println(customer);
}
}
多表操作
表关系
- 一对一:
- 一对多:
- 一的一方:主表
- 多的一方:从表
- 外键:需要再从表上新建一列作为外键,他的取值来源于主表的主键 - 多对多:
- 中间表:最少两个字段组成,分别是外键指向两个表的主键,有组成了联合主键
实体类的关系
- 包含关系:可以通过实体类中的包含关系来描述表关系
分析步骤
1.明确表关系
2.确定表关系(描述 外键|中间表)
3.编写实体类,在实体类中描述表关系(包含关系)
4.配置映射关系
一对多
客户与联系人
- 一个客户可以具有多个联系人
- 一个联系人属于一家客户公司
表关系: - 主表:客户表
- 从表:联系人表,再从表上添加外键
实体类:
客户:实体类中应该包含一个联系人的聚合
联系人:实体类包含一个客户对象
映射关系:
使用jpa注解配置一对多的映射
主表实体类
- @OneToMany 配置实体类参数targetEntity对方的实体类
- @JoinColumn 配置外键,参数name表示从表的外键名,referencedColumnName表示主表对应的键,配置多的一方的也要配置@JoinColumn
从表实体类
- @ManyToOne 配置实体类参数targetEntity对方的实体类
- @JoinColumn 配置外键,参数name表示从表的外键名,referencedColumnName表示主表对应的键,
数据库保存
双向均可维护,推荐从多对一的发送,如果同时发送的话,会多发一条update。
一的一方放弃外键维护,
主表(一的一方)
@OneToMany(mappedBy = “customer”) 参数为对方配置外键用的的属性名称
主表放弃外键维护后,不能随便删除主表数据,因为他不会去修改从表中的外键字段了,想删除使用级联删除引用。
如果没有外键维护。默认情况下删除后从表的外键会变为null。前提是外键可以为null。
级联操作
级联:操作一个对象同时操作他的关联对象
- 级联添加,eg:当我保存一个客户的同时保存联系人
- 级联删除,eg:当我删除一个客户的同时删除此客户的所有联系人
级联操作的步骤
- 区分操作主体
- 需要在操作主体的实体类上,添加级联属性(需要添加到映射关系的注解上)
- cascade(配置级联)
级联添加:
级联删除
多对多操作
案例:
用户和角色
- 用户:一个用户可以是多个角色
- 角色:一个角色可以分配给多个用户
分析步骤
- 确定表关系
中间表 - 编写实体类
用户:包含角色的集合
角色:包含用户的集合 - 配置映射关系
@ManyToMany注解配置在set集合上,参数targetEntity表示对方的实体类,设置级联cascade
@JoinTablez注解表示中间表的配置:
- 参数name:表示中间表名字
- 参数joinColumns表示配置当前对象在中间表的外键
JoinColumn中name表示外键名,referencedColumnName表示参照主表中的主键名 - 参数inverseJoinColumns表示对方对象再中间表的外键
JoinColumn与joinColumns中的的正好相反
实体类user
实体类role
多对多添加:
放弃外键维护
多对多通常是被动选择的一方放弃维护权利
@ManyToMany(mapped=“”)参数为对方外键的属性名称
role实体类:
多对多级联与一对多一样
多表的查询
- 对象导航查询
查询一个对象的同时,通过此对象查询他的关联对象。
默认使用的延迟加载来查询,调用get方法不会立即发送查询。
如果不想用延迟加载修改配置。fetch,需要配置到多表映射关系的注解上
FetchType.EAGER立即加载。不推荐立即加载,因为第二次加载的时候用的左连接,两个表一起加载,而第一个表已经查询过一次了。所以性能变慢。
而从多的一方查一的一方,默认会在第一次就使用了左连接查了两个表。
总结:
从一方查多方默认使用延迟加载
从多方查一方默认使用立即加载
所以fetch一般不用改
可能的问题
1.no session错误。
解决方案 加上@Transactional注解