JPA的学习和理解

JPA的学习和理解

Scroll Down

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.数据里不能超过内存容量,数据量特别大时,不适合缓存(可能数据会钝化)

祝君好梦!