JPA的学习和理解
1.jpa和hibernate的关系
jpa:对象关系映射规范
jpa是规范,hibernate是实现,都是为了解决传统jdbc操作复杂,开发效率低而提供的操作数据库的框架
2.jpa和jdbc的区别
jdbc:
本质:处理Java对象和关系型数据库表之间的转换
优点:操作数据库最底层,性能可控
缺点:开发效率低。移植数据库麻烦,没有提供存储数据的缓存,面向SQL语句操作。
jpa:
本质:处理Java对象和关系型数据库表之间的转换,只是对JDBC再次做了一层封装
优点:操作简便,面向持久对象操作,提供缓存(一级缓存、二级缓存、查询缓存),数据库移植性强(数据库方言确定)
缺点:不能干预SQL语句的生成,上亿数据量,不适用jpa,运行效率相对于jdbc更低
3.jpa的配置和CRUD
1.pom.xml配置
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.xxx</groupId>
<artifactId>jpa-demo</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>jpaday01</module>
</modules>
<dependencies>
<!-- hibernate的包 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.3.8.Final</version>
</dependency>
<!-- hibernate对于jpa的支持包 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.3.8.Final</version>
</dependency>
<!-- mysql的驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!-- junit的测试包 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
<build>
<finalName>pss</finalName>
<plugins>
<plugin>
<!-- Maven的编译插件-->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
注意事项:
hibernate的核心包与hibernate对jpa的支持包需要版本一致
2.persistence.xml配置
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="cn.xp.jpa" transaction-type="RESOURCE_LOCAL">
<properties>
<!-- 必须配置4个连接数据库属性 -->
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
<property name="hibernate.connection.url" value="jdbc:mysql:///jpa" />
<property name="hibernate.connection.username" value="root" />
<property name="hibernate.connection.password" value="admin" />
<!-- 必须配置1个方言属性 -->
<!-- 实现跨数据库关键类 :查询MySQLDialect的getLimitString方法 -->
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
<!-- 可选配置 建表策略-->
<!-- 是否自动生成表 -->
<property name="hibernate.hbm2ddl.auto" value="create" />
<!-- 是否显示sql -->
<property name="hibernate.show_sql" value="true" />
<!-- 格式化sql -->
<!-- <property name="hibernate.format_sql" value="true" /> -->
</properties>
</persistence-unit>
</persistence>
相关配置说明:
配置相关性信息可在hibernate.properties找到
1.transaction-type="RESOURCE_LOCAL"
事务类型:RESOURCE_LOCAL 本地事务 JTA分布式事务
2.数据库方言:jpa通过方言的配置,确定你的数据库类型,实现兼容性
3.数据库引擎:InnoDB:支持外键 支持事物
MyISAM:不支持外键 不支持事物(效率高)
MySQLDialect:两者都支持
3.jpa的CRUD
注意jpa的增加、删除、修改都需要事务完成
domain:
package cn.xxx.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
/*
* @Entity 告诉jpa需要实体化
* @Table(name="t_employee") 自动创建表且表名为t_employee 如果不写name那么名字就未employee
* 申明主键 且主键规则
* 提供get set方法
* */
@Entity
@Table(name="t_employee")
public class Employee {
/*@Id这是主键 @GeneratedValue自增规则*/
@Id
@GeneratedValue
private Long id;
private String employeeName;
private String employeePassword;
public String getEmployeePassword() {
return employeePassword;
}
public void setEmployeePassword(String employeePassword) {
this.employeePassword = employeePassword;
}
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
IEmployeeDao
package cn.xxx.dao;
import cn.xxx.domain.Employee;
import java.util.List;
public interface IEmployeeDao {
void add(Employee employee);
void update(Employee employee);
Employee queryOne(Long id);
List<Employee> queryAll();
void delete(Long id);
}
EmployeeDaoImpl
package cn.xxx.dao.impl;
import cn.xxx.dao.IEmployeeDao;
import cn.xxx.domain.Employee;
import cn.xxx.util.JpaUtil;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.util.List;
public class EmployeeDaoImpl implements IEmployeeDao {
@Override
public void add(Employee employee) {
EntityManager entityManager = null;
try {
/*拿到实例管理*/
entityManager = JpaUtil.getEntityManager();
/**拿到事物,开启事物*/
entityManager.getTransaction().begin();
/**持久化对象*/
entityManager.persist(employee);
/**提交事物*/
entityManager.getTransaction().commit();
} catch (Exception e) {
/*失败回滚事物*/
entityManager.getTransaction().rollback();
e.printStackTrace();
}finally {
/**管理者关闭*/
entityManager.close();
}
}
@Override
public void update(Employee employee) {
EntityManager entityManager=null;
try {
entityManager= JpaUtil.getEntityManager();
entityManager.getTransaction().begin();
entityManager.merge(employee);
entityManager.getTransaction().commit();
} catch (Exception e) {
entityManager.getTransaction().rollback();
e.printStackTrace();
} finally {
entityManager.close();
}
}
@Override
public Employee queryOne(Long id) {
EntityManager entityManager =null;
try {
entityManager=JpaUtil.getEntityManager();
return entityManager.find(Employee.class, id);
} catch (Exception e) {
e.printStackTrace();
} finally {
entityManager.close();
}
return null;
}
@Override
public List<Employee> queryAll() {
EntityManager entityManager=null;
try {
entityManager=JpaUtil.getEntityManager();
entityManager.getTransaction().begin();
String jql ="select o from cn.xxx.domain.Employee o" ;
Query query = entityManager.createQuery(jql);
return query.getResultList();
} catch (Exception e) {
e.printStackTrace();
} finally {
entityManager.close();
}
return null;
}
@Override
public void delete(Long id) {
EntityManager entityManager =null;
try {
entityManager=JpaUtil.getEntityManager();
entityManager.getTransaction().begin();
Employee employee = entityManager.find(Employee.class, id);
entityManager.getTransaction().commit();
if (employee!=null){
entityManager.remove(employee);
}
} catch (Exception e) {
entityManager.getTransaction().rollback();
e.printStackTrace();
} finally {
entityManager.close();
}
}
}
test:
package cn.xxx.dao.impl;
import cn.xxx.domain.Employee;
import org.junit.Test;
public class EmployeeDaoImplTest {
private EmployeeDaoImpl employeeDao=new EmployeeDaoImpl();
@Test
public void add() {
Employee employee = new Employee();
employee.setEmployeeName("老子何旭东");
employee.setEmployeePassword("6666");
employeeDao.add(employee);
}
@Test
public void update() {
Employee employee2 = new Employee();
employee2.setEmployeeName("老子何旭东888");
employeeDao.update(employee2);
}
@Test
public void queryOne() {
Employee employee = new Employee();
employee.setEmployeeName("老子何旭东");
employee.setEmployeePassword("6666");
employeeDao.add(employee);
System.out.println( employeeDao.queryOne(2L));
}
@Test
public void queryAll() {
add();
add();
add();
employeeDao.queryAll().forEach(e->System.out.println(e));
}
@Test
public void delete() {
add();
employeeDao.delete(2L);
}
}
4.jpa的相关策略
1.建表策略
配置位置:persistence.xml
<!--配置建表策略 自动创建-->
<property name="hibernate.hbm2ddl.auto" value="update"></property>
策略类型:
create drop:先删除原表,在创建在删除(测试使用)
create:先删除后创建,每次都是新表(测试使用)
update:对原来的表更改表结构,只增加数据不减少数据,不改变类型,没表就创建,有表就修改
validate:验证,只验证domain中含有的字段,不验证表
none:什么都不做
2.主键生成策略
配置位置:domain主键中
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
Sequence:
序列策略(性能高):存在序列对象,对象存在ID,序列对象存于缓存中,每次通过寻找序列对象来找主键
要求:1.主键为数字2.数据库支持序列策略
常用数据库:Oracle
Identity:
自增策略(性能高):主键自动递增
要求:1.主键为数字2.数据库支持序列策略
常用数据库:MySQL
auto:
自动策略(默认):自动根据我们配置的方言进行适应
Table:
表策略(性能最低,兼容性最好):帮我们模拟序列
3.外键策略
配置位置:domain外键位置
@ManyToOne(fetch = FetchType.LAZY)
private Department department;
@ManyToOne(fetch = FetchType.EAGER)及时抓取(默认)
@ManyToOne(fetch = FetchType.LAZY)延时抓取(懒加载),需要时在帮你抓取
获取持久状态对象之后,还需要获取关联对象(即使用值),此时才会真正发出sql,获取值
好处:提高性能,但是如果把entityManager 提前关闭,会出现(延迟加载)延迟初始化异常org.hibernate.LazyInitializationException: could not initialize proxy - no Session
5.jpa的相关对象和状态
1.jpa的核心对象
persistence:
作用:创建EntityManagerFactory,解析核心配置文件persistence.xml
EntityManagerFactory:实例化管理工厂
重量级对象:连接池,二级缓存,预定义的SQL和jpql,所有对象的配置关系
1:1:1配置:一个项目一个EntityManagerFactory一个数据库
线程安全对象
作用:创建EntityManager对象
EntityManager:实例化管理对象
轻量级对象:一级缓存,连接对象
线程不安全
作用:完成CRUD功能
entityTransaction:实例化事务
2.jpa的对象状态
临时状态、持久状态、删除状态、游离状态
临时状态:刚创建出来,未于EntityManager发生关系
持久状态:与EntityManager发生关系
删除状态:执行remove方法,事物提交之前
游离状态:持久化数据之后,但不存在EntityManager中
3.脏数据更新
持久状态一但修改后,提交事务也会自动修改
流程分析:
对象发生持久化操作,首先一级缓存就会有一个对象,同时有一个与对象一致的快照,当修改了持久化对象,提交事务时,会与快照作比较,如果不一致,会自动更新数据库
6.域对象关系
1.依赖关系
Controller依赖于service service依赖于dao
2.关联关系
多重性:
一对一 :共享主键,唯一外键
一对多 多对一:有外键的为多方
多对多:有中间表
导航性:单向关联 双向关联
注意:表没有导航性,只有多重性
3.聚合关系
双向的多对一和一对多就是聚合关系,关系是可以解除的
4.组合关系
组合就是强聚合,关系是不可以解除的
注意事项:多对一的关系中,应当先保存一方后保存多方,效率更高
分析:如商品(多方)和商品分类(一方)的持久化,先保存多方后一方情况时:
1.保存第一个多方时,执行一条SQL,维护他和类别的关系,类别没出现维护失败
2.保存第二个多方时,执行一条SQL,维护他和类别的关系,类别没出现维护失败
3.保存一个一方是,执行一条SQL,不维护关系
4.提交事物,发现维护失败,发现有关系而没维护就会重复1,2执行,5条SQL
如商品(多方)和商品分类(一方)的持久化,先保存一方方后多方情况时:
1.保存一个一方是,执行一条SQL,不维护关系
2.保存第一个多方时,执行一条SQL,维护他和类别的关系,类别出现维护成功
3.保存第二个多方时,执行一条SQL,维护他和类别的关系,类别出现成功
4.提交事务,三条SQL
6.一级缓存和二级缓存
1.一级缓存:
jpa自动支持一级缓存
使用jpa查询数据时,存在一个一级缓存,它会让查询效率变高,先找缓存区,缓存区采用map的方式,键为OId(完全限定名#id) ,值为对象存储数据,如果缓存区没有,就在数据库找,找到然后存入缓存区,有直接返回
一级缓存命中:
同一个EntityManagerFactory 同一个EntityManager同一个OID
2.二级缓存:
jpa不自动支持二级缓存,需要配置
1.添加二级缓存jar文件
hibernate-release-4.3.8.Final\lib\optional\ehcache\3个jar文件
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>4.3.8.Final</version>
</dependency>
2.搜索配置信息
Cache hibernate-release-4.3.8.Final\project\etc\hibernate.properties Second-level Cache
3.添加persistence.xml配置信息
<!-- 启用二级缓存 -->
<property name="hibernate.cache.use_second_level_cache" value="true" />
<!-- 二级缓存的实现类,文档里面的包名有错的 -->
<!-- 错误 org.hibernate.cache.internal.EhCacheRegionFactory -->
<!-- 正确的 org.hibernate.cache.ehcache.EhCacheRegionFactory -->
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory" />
<!-- 启用查询缓存 -->
<property name="hibernate.cache.use_query_cache" value="true" />
4.在
<!-- ALL:所有的实体类都被缓存 -->
<!-- NONE:所有的实体类都不被缓存. -->
<!-- ENABLE_SELECTIVE:标识 @Cacheable(true) 注解的实体类将被缓存 -->
<!-- DISABLE_SELECTIVE:缓存除标识 @Cacheable(false) 以外的所有实体类 -->
<!-- UNSPECIFIED:默认值,JPA 产品默认值将被使用 -->
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
5.domain类的二级缓存
二级缓存类配置:命中条件:同一个SessionFactory,不同的entityManager,OID相同
// 一条sql
// @Entity
// @Cacheable(true)
// public class Department
@Test
public void test2() throws Exception {
EntityManager entityManager = JPAUtils.getEntityManager();
//先从一级缓存,二级缓存取数据?取不到,发出sql去获取,填充一级缓存,二级缓存
Department department1 = entityManager.find(Department.class, 1L);
//先从一级缓存,二级缓存取数据?一级缓存取到了,返回
Department department2 = entityManager.find(Department.class, 1L);// 一级缓存命中
entityManager.close();
EntityManager entityManager2 = JPAUtils.getEntityManager();
//先从一级缓存,二级缓存取数据?一级缓存没有取到,但是二级缓存取到了,返回
Department department3 = entityManager2.find(Department.class, 1L);// 二级缓存命中
//先从一级缓存,二级缓存取数据?一级缓存取到了,返回
Department department4 = entityManager2.find(Department.class, 1L);// 一级缓存命中
entityManager2.close();
}
二级缓存命中:同一个EntityManagerFactory 多个EntityManager 同一个oid
3.缓存的使用场景
1.查询岛屿修改
2.对数据有独享控制,数据不会被第三方修改
3.容忍出现无效数据,非关键数据(财务数据不允许使用缓存)
4.数据里不能超过内存容量,数据量特别大时,不适合缓存(可能数据会钝化)
祝君好梦!