JPA

一、JPA概述

Java Persistence API:用于持久化对象的API

Java EE5.0平台标准的ORM规范,使得应用程序以统一的方式访问持久层

JPA和Hibernate的关系:JPA是hibernate的一个抽象,JDBC和JDBC驱动的关系

JPA是规范:JPA本质上就是一种ORM规范,不是ORM框架。因为JPA并未提供ORM实现,它只是制定了一些规范,提供了一些编程的API接口,但具体实现则由ORM厂商提供实现

Hibernate实现:Hibernate作为ORM框架之外,它也是一种JPA实现

从功能上来说,JPA是Hibernate功能的一个子集

JAP的优势
标准化: 提供相同的 API,这保证了基于JPA 开发的企业应用能够经过少量的修改就能够在不同的 JPA 框架下运行。

简单易用,集成方便: JPA 的主要目标之一就是提供更加简单的编程模型,在 JPA 框架下创建实体和创建 Java 类一样简单,只需要使用 javax.persistence.Entity 进行注释;JPA 的框架和接口也都非常简单

可媲美JDBC的查询能力: JPA的查询语言是面向对象的,JPA定义了独特的JPQL,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。

支持面向对象的高级特性: JPA 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,最大限度的使用面向对象的模型

支持以下技术
ORM 映射元数据:JPA 支持 XML 和 JDK 5.0 注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中。
JPA 的 API:用来操作实体对象,执行CRUD操作,框架在后台完成所有的事情,开发者从繁琐的 JDBC和 SQL代码中解脱出来。
查询语言(JPQL):这是持久化操作中很重要的一个方面,通过面向对象而非面向数据库的查询语言查询数据,避免程序和具体的 SQL 紧密耦合。

二、使用步骤

1.创建 persistence.xml

创建 persistence.xml, 在这个文件中配置持久化单元
1.需要指定跟哪个数据库进行交互;
2.需要指定 JPA 使用哪个持久化的框架以及配置该框架的基本属性
创建实体类, 使用 annotation 来描述实体类跟数据库表之间的映射关系.
使用 JPA API 完成数据增加、删除、修改和查询操作
创建 EntityManagerFactory (对应 Hibernate 中的 SessionFactory);
创建 EntityManager (对应 Hibernate 中的Session);

2.创建JPA项目

1.在 IDEA 下创建一个 JPA 项目,并实现基本的 CRUD

在这里插入图片描述2.导入相应的 Maven 依赖
在这里插入图片描述

在这里插入图片描述
pom.xml

    <dependencies>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>5.4.1.Final</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
    </dependencies>

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<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="JPA_PRO" transaction-type="RESOURCE_LOCAL">
        <!--
                配置使用什么ORM 产品作为JPA 的实现
                1.实际上配置的是 javax.persistence.spi.PersistenceProvider 接口的实现类
                2.若JPA项目中只有一个JPA的实现产品,则也可以不配置该节点。
                -->
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

        <!-- 添加持久化类-->
 <class>com.ming.entity.Customer</class>
        <properties>
            <!-- 连接数据库的基本信息 -->
            <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3307/jpa?useSSL=false&amp;serverTimezone=Asia/Shanghai"/>
            <property name="hibernate.connection.driver_class" value="com.mysql.cj.jdbc.Driver"/>
            <property name="hibernate.connection.username" value="liming"/>
            <property name="hibernate.connection.password" value="liming"/>

            <property name="hibernate.archive.autodetection" value="class"/>

            <!-- 配置JPA 实现产品的基本属性,配置hibernate 的基本属性 -->
            <!-- 自动显示SQL -->
            <property name="hibernate.show_sql" value="true"/>
            <!-- 格式化sql -->
            <property name="hibernate.format_sql" value="true"/>
            <!--生成数据表的策略-->
            <!--注意这个属性,自动生成的文件前面没有 hibernate,要加上 hibernate -->
            <property name="hibernate.hbm2ddl.auto" value="update"/>
            <!-- 使用 MySQL8Dialect -->
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect"/>

        </properties>

    </persistence-unit>

</persistence>


    public static void main(String[] args) {
        //1.创建EntityManagerFactory
        String persistenceUnitName = "JPA_PRO";
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnitName);
        //2.创建EntityManager
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        //3.开启事物
        EntityTransaction transaction = entityManager.getTransaction();
        transaction.begin();
        //4.进行持久化操作
        Customer customer = new Customer();
        customer.setAge(12);
        customer.setEmail("565665@qq.com");
        customer.setLastName("tom");
        entityManager.persist(customer);
        //5.提交事物
        transaction.commit();
        //6.关闭EntityManager
        entityManager.close();
        //7.关闭EntityManagerFactory
        entityManagerFactory.close();
    }

三、JPA基本注解

@Entity 标注用于实体类声明语句之前,指出该Java 类为实体类,将映射到指定的数据库表。如声明一个实体类 Customer,它将映射到数据库中的 customer 表上

当实体类与其映射的数据库表名不同名时需要使用 @Table 标注说明,该标注与 @Entity 标注并列使用,置于实体类声明语句之前,可写于单独语句行,也可与声明语句同行。
@Table 标注的常用选项是 name,用于指明数据库的表名
@Table标注还有一个两个选项 catalog 和 schema 用于设置表所属的数据库目录或模式,通常为数据库名。uniqueConstraints 选项用于设置约束条件,通常不须设置。

import javax.persistence.*;
//关联数据表
@Table(name = "JAP_CUSTOMERS")
//持久化类
@Entity
public class Customer {}

@Id 标注用于声明一个实体类的属性映射为数据库的主键列。该属性通常置于属性声明语句之前,可与声明语句同行,也可写在单独行上。
@Id标注也可置于属性的getter方法之前。

    //映射主键及数据列(在get方法上)
    //数据列的列名( 如果相同可不写 )
    @Column(name = "ID")
    //生成主键的方式
    @GeneratedValue(strategy= GenerationType.AUTO)
    @Id
    public Integer getId() {
        return id;
    }

@GeneratedValue 用于标注主键的生成策略,通过 strategy 属性指定。默认情况下,JPA 自动选择一个最适合底层数据库的主键生成策略:SqlServer 对应 identity,MySQL 对应 auto increment。
在 javax.persistence.GenerationType 中定义了以下几种可供选择的策略:
IDENTITY:采用数据库 ID自增长的方式来自增主键字段,Oracle 不支持这种方式;
AUTO: JPA自动选择合适的策略,是默认选项;
SEQUENCE:通过序列产生主键,通过 @SequenceGenerator 注解指定序列名,MySql 不支持这种方式
TABLE:通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植。

    @Column(name = "ID")
    //生成主键的方式
    @TableGenerator(name = "ID_GENERATOR",
            table = "jpa_id_generators",
            pkColumnName = "PK_NAME",
            pkColumnValue = "CUSTOMER_ID",
            valueColumnName = "PK_VALUE",
            allocationSize = 100)
    @GeneratedValue(strategy= GenerationType.TABLE,generator = "ID_GENERATOR")
    @Id
    public Integer getId() {
        return id;
    }

@Transient表示该属性并非一个到数据库表的字段的映射,ORM框架将忽略该属性.如果一个属性并非数据库表的字段映射,就务必将其标示为@Transient,否则,ORM框架默认其注解为@Basic

@Temporal在核心的 Java API 中并没有定义 Date 类型的精度(temporal precision). 而在数据库中,表示 Date 类型的数据有 DATE, TIME, 和 TIMESTAMP 三种精度(即单纯的日期,时间,或者两者 兼备). 在进行属性映射时可使用@Temporal注解来调整精度.

@Basic 表示一个简单的属性到数据库表的字段的映射,对于没有任何标注的 getXxxx() 方法,默认即为@Basic
fetch: 表示该属性的读取策略,有 EAGER 和 LAZY 两种,分别表示主支抓取和延迟加载,默认为 EAGER.
optional:表示该属性是否允许为null, 默认为true

四、JPA API

Persistence 类是用于获取 EntityManagerFactory 实例。该类包含一个名为 createEntityManagerFactory 的 静态方法 。

createEntityManagerFactory 方法有如下两个重载版本。
带有一个参数的方法以 JPA 配置文件 persistence.xml 中的持久化单元名为参数
带有两个参数的方法:前一个参数含义相同,后一个参数 Map类型,用于设置 JPA 的相关属性,这时将忽略其它地方设置的属性。Map 对象的属性名必须是 JPA 实现库提供商的名字空间约定的属性名。

        //1.创建EntityManagerFactory
        String persistenceUnitName = "JPA_PRO";
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("hibernate.show_sql",true);//map存放的是property的属性name
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnitName,map);

EntityManagerFactory 接口主要用来创建 EntityManager 实例。该接口约定了如下4个方法:
createEntityManager():用于创建实体管理器对象实例。
createEntityManager(Map map):用于创建实体管理器对象实例的重载方法,Map 参数用于提供 EntityManager 的属性。
isOpen():检查 EntityManagerFactory 是否处于打开状态。实体管理器工厂创建后一直处于打开状态,除非调用close()方法将其关闭。
close():关闭 EntityManagerFactory 。 EntityManagerFactory 关闭后将释放所有资源,isOpen()方法测试将返回 false,其它方法将不能调用,否则将导致IllegalStateException异常。

EntityManager
find (Class entityClass,Object primaryKey):返回指定的 OID 对应的实体类对象,如果这个实体存在于当前的持久化环境,则返回一个被缓存的对象;否则会创建一个新的 Entity, 并加载数据库中相关信息;若 OID 不存在于数据库中,则返回一个 null。第一个参数为被查询的实体类类型,第二个参数为待查找实体的主键值。

getReference (Class entityClass,Object primaryKey):与find()方法类似,不同的是:如果缓存中不存在指定的 Entity, EntityManager 会创建一个 Entity 类的代理,但是不会立即加载数据库中的信息,只有第一次真正使用此 Entity 的属性才加载,所以如果此 OID 在数据库不存在,getReference() 不会返回 null 值, 而是抛出EntityNotFoundException

persist (Object entity):用于将新创建的 Entity 纳入到 EntityManager 的管理。该方法执行后,传入 persist() 方法的 Entity 对象转换成持久化状态。
如果传入 persist() 方法的 Entity 对象已经处于持久化状态,则 persist() 方法什么都不做。
如果对删除状态的 Entity 进行 persist() 操作,会转换为持久化状态。
如果对游离状态的实体执行 persist() 操作,可能会在 persist() 方法抛出 EntityExistException(也有可能是在flush或事务提交后抛出)

remove (Object entity):删除实例。如果实例是被管理的,即与数据库实体记录关联,则同时会删除关联的数据库记录。

package com.ming;

import com.ming.entity.Customer;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class JPATest {

    private EntityManagerFactory entityManagerFactory;
    private EntityManager entityManager;
    private EntityTransaction transaction;

    @Before
    public void init() {
        entityManagerFactory = Persistence.createEntityManagerFactory("JPA_PRO");
        entityManager = entityManagerFactory.createEntityManager();
        transaction = entityManager.getTransaction();
        transaction.begin();

    }

    @After
    public void destroy() {
        transaction.commit();
        entityManager.close();
        entityManagerFactory.close();
    }

    /**
     * 类似于hibernate中的session的get方法
     */
    @Test
    public void testFind() {
        Customer customer = entityManager.find(Customer.class, 6);
        System.out.println(customer);
    }

    /**
     * 相当于Hibernate中的session的load方法
     */
    @Test
    public void testGetReference() {
        Customer customer = entityManager.getReference(Customer.class, 6);
        System.out.println(customer);
    }

    /**
     * 雷士与Hibernate的save方法.使对象由临时状态变为持久化状态
     * 和hibernate的save方法不同之处:如对象由id不能执行insert操作,会抛出异常
     */
    @Test
    public void testPersistence() {
        Customer customer = new Customer();
        customer.setAge(12);
        customer.setEmail("2656556565@qq.com");
        customer.setLastName("xiaomi");
        entityManager.persist(customer);
    }

    /**
     * 类似于hibernate的session的delete方法。把对象对应的记录从数据库中移除
     * 注意:该方法只能移除持久化对象,hibernate的delete方法实际上还可以移除游离对象
     */
    @Test
    public void testRemove() {
//        Customer customer = new Customer();
//        customer.setId(6);
        Customer customer = entityManager.find(Customer.class, 6);
        entityManager.remove(customer);
    }
}

merge (T entity):merge() 用于处理 Entity 的同步。即数据库的插入和更新操作

在这里插入图片描述

    /**
     * 总的来说:类似于hibernate session的saveOrUpdate方法
     *
     * 1.若传入一个临时对象
     * 会创建一个新的对象,把临时对象的属性复制到到新的对象中,然后对新的对象执行持久化操作。
     * 所以新的对象中有id值,当以前的临时对象中没有id
     */
    @Test
    public void testMerge1(){
        Customer customer = new Customer();
        customer.setAge(12);
        customer.setEmail("2656556565@qq.com");
        customer.setLastName("xiaomi");
        Customer merge2 = entityManager.merge(customer);
        System.out.println(customer.getId());
        System.out.println(merge2.getId());
    }

    /**
     * 若传入的是一个游离对象,即传入的对象有OID
     * 1.若在entityManager 缓存中没有该对象
     * 2.若在数据库中没有对应的记录
     * 3.JPA会创建一个新的对象,然后把当前游离对象的属性复制到新创建的对象中
     * 4.对新创建的对象执行insert操作
     */
    @Test
    public void testMerge2(){
        Customer customer = new Customer();
        customer.setAge(12);
        customer.setEmail("2656556565@qq.com");
        customer.setLastName("xiaomi");
        customer.setId(100);
        Customer merge2 = entityManager.merge(customer);
        System.out.println(customer.getId());
        System.out.println(merge2.getId());
    }

    /**
     * 若传入的是一个游离对象,即传入的对象有OID
     * 1.若在entityManager 缓存中没有该对象
     * 2.若在数据库中有对应的记录
     * 3.JPA会查询对应的记录,然后返回该记录对应的对象,再然后会把游离对象的属复制到查询到的对象中
     * 4.对查询到的对象执行update操作
     */
    @Test
    public void testMerge3(){
        Customer customer = new Customer();
        customer.setAge(12);
        customer.setEmail("33@qq.com");
        customer.setLastName("xiaomi3");
        customer.setId(8);
        Customer merge2 = entityManager.merge(customer);
        System.out.println(customer==merge2);//false
    }



    /**
     * 若传入的是一个游离对象,即传入的对象有OID
     * 1.若在entityManager 缓存中有对应的对象
     * 2.JPA会把游离对象的属性复制到EntityManager缓存的对象中
     * 3.然后对EntityManager缓存的对象执行update
     */
    @Test
    public void testMerge4(){
        Customer customer = new Customer();
        customer.setAge(12);
        customer.setEmail("344@qq.com");
        customer.setLastName("xiaomi4");
        customer.setId(8);
        Customer customer1 = entityManager.find(Customer.class, 8);

        Customer merge2 = entityManager.merge(customer);
        System.out.println(customer==customer1);//false
    }

flush ():同步持久上下文环境,即将持久上下文环境的所有未保存实体的状态信息保存到数据库中。

setFlushMode (FlushModeType flushMode):设置持久上下文环境的Flush模式。参数可以取2个枚举
FlushModeType.AUTO 为自动更新数据库实体
FlushModeType.COMMIT 为直到提交事务时才更新数据库记录。

getFlushMode ():获取持久上下文环境的Flush模式。返回FlushModeType类的枚举值。

五、映射关联关系

1.单向多对一

package com.ming;

import com.ming.entity.Customer;
import com.ming.entity.Order;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class JPAORMTest {
    private EntityManagerFactory entityManagerFactory;
    private EntityManager entityManager;
    private EntityTransaction transaction;

    @Before
    public void init() {
        entityManagerFactory = Persistence.createEntityManagerFactory("JPA_PRO");
        entityManager = entityManagerFactory.createEntityManager();
        transaction = entityManager.getTransaction();
        transaction.begin();

    }

    @After
    public void destroy() {
        transaction.commit();
        entityManager.close();
        entityManagerFactory.close();
    }

    /**
     * 保存多对一时,先保存1的一端,再保存n的一端,这样不会多出额外的update语句
     */
    @Test
    public void ManyToOneTest() {
        Customer customer = new Customer();
        customer.setAge(12);
        customer.setEmail("2656556565@qq.com");
        customer.setLastName("xiaoc");

        Order order1 = new Order();
        order1.setOrderName("c-1");

        Order order2 = new Order();
        order2.setOrderName("c-2");
        //设置关联关系
        order1.setCustomer(customer);
        order2.setCustomer(customer);

        //执行保存操作
        entityManager.persist(customer);
        entityManager.persist(order1);
        entityManager.persist(order2);

    }

    /**
     * 默认情况下使用左连接的方式获取n的一端的对象和其关联的1的一端的对象
     * 改为懒加载@ManyToOne(fetch = FetchType.LAZY)修改默认的关联属性的加载策略
     */
    @Test
    public void ManyToOneFindTest() {
        Order order = entityManager.find(Order.class, 10);
        System.out.println(order.getOrderName());
        System.out.println(order.getCustomer().getLastName());
    }

    /**
     * 不能直接删除1的一端,因为有外键约束
     */
    @Test
    public void ManyToOneRemoveTest() {
//        Order order = entityManager.find(Order.class, 10);
//        entityManager.remove(order);
        Customer customer = entityManager.find(Customer.class, 9);
        entityManager.remove(customer);
    }


    @Test
    public void ManyToOneUpdateTest() {
        Order order = entityManager.find(Order.class, 10);
        order.getCustomer().setLastName("c2");
    }
}

2.单向一对多


    /**
     * 单向一对多:关联关系执行保存时一定会多出update语句
     * 因为多的一端在插入时不会同时插入外键列
     */
    @Test
    public void oneToMany(){
        Customer customer = new Customer();
        customer.setAge(12);
        customer.setEmail("adong5@qq.com");
        customer.setLastName("adong");

        Order order1 = new Order();
        order1.setOrderName("adong-1");

        Order order2 = new Order();
        order2.setOrderName("adong-2");
        //设置关联关系
        customer.getOrders().add(order1);
        customer.getOrders().add(order2);
        //执行保存操作
        entityManager.persist(customer);
        entityManager.persist(order1);
        entityManager.persist(order2);
    }

    /**
     * 默认对关联多的一方使用懒加载的策略
     */
    @Test
    public void oneToManyFind(){
        Customer customer = entityManager.find(Customer.class, 12);
        System.out.println(customer.getLastName());
        System.out.println(customer.getOrders().size());
    }

    /**
     * 默认情况下若删除1的一端先把关联的多的一端的外键置空,然后进行删除
     * @ OneToMany(fetch = FetchType.EAGER,cascade = {CascadeType.REMOVE})可以进行级联删除
     */
    @Test
    public void oneToManyRemove(){
        Customer customer = entityManager.find(Customer.class, 12);
        entityManager.remove(customer);
    }


    @Test
    public void oneToManyUpdate(){
        Customer customer = entityManager.find(Customer.class, 9);
        customer.getOrders().iterator().next().setOrderName("hukaili");

    }

3.双向多对一

双向一对多关系中,必须存在一个关系维护端,在 JPA 规范中,要求 many 的一方作为关系的维护端(owner side), one 的一方作为被维护端(inverse side)。
可以在 one 方指定 @OneToMany 注释并设置 mappedBy 属性,以指定它是这一关联中的被维护端,many 为维护端。
在 many 方指定 @ManyToOne 注释,并使用 @JoinColumn 指定外键名称

备注:双向多对一和双向一对多是一回事,注意两边的@JoinColumn列名是一致的

 /**
     * 单向一对多:关联关系执行保存时一定会多出update语句
     * 因为多的一端在插入时不会同时插入外键列
     *
     * 若是双向1-n的关联关系,执行保存时
     * 若先保存n多的一端,在保存1的一端,默认情况下回多出4条update语句
     * 若先保存1的一端,则会多出n条update语句
     * 建议在进行双向1-n关联关系时,使用多的一方维护关联关系,而1的一方不维护关联关系(放弃维护关联关系mappedBy,但是不能定义JoinColumn),这样会有效的减少sql
     *   @ OneToMany(fetch = FetchType.EAGER,cascade = {CascadeType.REMOVE},mappedBy = "customer")
     */
    @Test
    public void oneToMany(){
        Customer customer = new Customer();
        customer.setAge(12);
        customer.setEmail("kaige@qq.com");
        customer.setLastName("kaige");

        Order order1 = new Order();
        order1.setOrderName("kaige-1");

        Order order2 = new Order();
        order2.setOrderName("kaige-2");
        //设置关联关系
        customer.getOrders().add(order1);
        customer.getOrders().add(order2);

        order1.setCustomer(customer);
        order2.setCustomer(customer);
        //执行保存操作
        entityManager.persist(customer);
        entityManager.persist(order1);
        entityManager.persist(order2);
    }

4.双向一对一

基于外键的 1-1 关联关系:在双向的一对一关联中,需要在关系被维护端(inverse side)中的 @OneToOne 注释中指定 mappedBy,以指定是这一关联中的被维护端。同时需要在关系维护端(owner side)建立外键列指向关系被维护端的主键列。

    /**
     * 双向1-1的关联关系,建议先保存不维护关联关系的一方,既没有外键的一方,这样就不会多出Update语句
     */
    @Test
    public void oneToOneTest() {
        Manager manager = new Manager();
        manager.setMgrName("李经理");

        Department department = new Department();
        department.setDeptName("技术部");

        //设置关联关系
        manager.setDept(department);
        department.setMgr(manager);
        //执行保存操作
        entityManager.persist(manager);
        entityManager.persist(department);
    }

	//1. 默认情况下, 若获取不维护关联关系的一方, 则也会通过左外连接获取其关联的对象. 
	//可以通过 @OneToOne 的 fetch 属性来修改加载策略. 但依然会再发送 SQL 语句来初始化其关联的对象
	//这说明在不维护关联关系的一方, 不建议修改 fetch 属性. 
	@Test
	public void testOneToOneFind2(){
		Manager mgr = entityManager.find(Manager.class, 1);
		System.out.println(mgr.getMgrName());
		
		System.out.println(mgr.getDept().getClass().getName());
	}
	
	//1.默认情况下, 若获取维护关联关系的一方, 则会通过左外连接获取其关联的对象. 
	//但可以通过 @OneToOne(fetch = FetchType.LAZY) 属性来修改加载策略.
	@Test
	public void testOneToOneFind(){
		Department dept = entityManager.find(Department.class, 1);
		System.out.println(dept.getDeptName());
		System.out.println(dept.getMgr().getClass().getName());
	}

5.双向多对多

在双向多对多关系中,我们必须指定一个关系维护端(owner side),可以通过 @ManyToMany 注释中指定 mappedBy 属性来标识其为关系维护端。

@Table(name="JPA_ITEMS")
@Entity
public class Item {

	private Integer id;
	private String itemName;
	
	private Set<Category> categories = new HashSet<>();

	@GeneratedValue
	@Id
	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	@Column(name="ITEM_NAME")
	public String getItemName() {
		return itemName;
	}

	public void setItemName(String itemName) {
		this.itemName = itemName;
	}

	//使用 @ManyToMany 注解来映射多对多关联关系
	//使用 @JoinTable 来映射中间表
	//1. name 指向中间表的名字
	//2. joinColumns 映射当前类所在的表在中间表中的外键
	//2.1 name 指定外键列的列名
	//2.2 referencedColumnName 指定外键列关联当前表的哪一列
	//3. inverseJoinColumns 映射关联的类所在中间表的外键
	@JoinTable(name="ITEM_CATEGORY",
			joinColumns={@JoinColumn(name="ITEM_ID", referencedColumnName="ID")},
			inverseJoinColumns={@JoinColumn(name="CATEGORY_ID", referencedColumnName="ID")})
	@ManyToMany
	public Set<Category> getCategories() {
		return categories;
	}

	public void setCategories(Set<Category> categories) {
		this.categories = categories;
	}
}
@Table(name="JPA_CATEGORIES")
@Entity
public class Category {

	private Integer id;
	private String categoryName;
	
	private Set<Item> items = new HashSet<>();

	@GeneratedValue
	@Id
	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	@Column(name="CATEGORY_NAME")
	public String getCategoryName() {
		return categoryName;
	}

	public void setCategoryName(String categoryName) {
		this.categoryName = categoryName;
	}

	@ManyToMany(mappedBy="categories")
	public Set<Item> getItems() {
		return items;
	}

	public void setItems(Set<Item> items) {
		this.items = items;
	}
}
	//对于关联的集合对象, 默认使用懒加载的策略.
	//使用维护关联关系的一方获取, 还是使用不维护关联关系的一方获取, SQL 语句相同. 
	@Test
	public void testManyToManyFind(){
//		Item item = entityManager.find(Item.class, 5);
//		System.out.println(item.getItemName());
//		
//		System.out.println(item.getCategories().size());
		
		Category category = entityManager.find(Category.class, 3);
		System.out.println(category.getCategoryName());
		System.out.println(category.getItems().size());
	}
	
	//多对多的保存
	@Test
	public void testManyToManyPersist(){
		Item i1 = new Item();
		i1.setItemName("i-1");
	
		Item i2 = new Item();
		i2.setItemName("i-2");
		
		Category c1 = new Category();
		c1.setCategoryName("C-1");
		
		Category c2 = new Category();
		c2.setCategoryName("C-2");
		
		//设置关联关系
		i1.getCategories().add(c1);
		i1.getCategories().add(c2);
		
		i2.getCategories().add(c1);
		i2.getCategories().add(c2);
		
		c1.getItems().add(i1);
		c1.getItems().add(i2);
		
		c2.getItems().add(i1);
		c2.getItems().add(i2);
		
		//执行保存
		entityManager.persist(i1);
		entityManager.persist(i2);
		entityManager.persist(c1);
		entityManager.persist(c2);
	}
	

六、二级缓存

节点:若 JPA 实现支持二级缓存,该节点可以配置在当前的持久化单元中是否启用二级缓存,可配置如下值:
ALL:所有的实体类都被缓存
NONE:所有的实体类都不被缓存.
ENABLE_SELECTIVE:标识 @Cacheable(true) 注解的实体类将被缓存
DISABLE_SELECTIVE:缓存除标识 @Cacheable(false) 以外的所有实体类
UNSPECIFIED:默认值,JPA 产品默认值将被使用

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
	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">
	<persistence-unit name="jpa-1" transaction-type="RESOURCE_LOCAL">
		<!-- 
		配置使用什么 ORM 产品来作为 JPA 的实现 
		1. 实际上配置的是  javax.persistence.spi.PersistenceProvider 接口的实现类
		2. 若 JPA 项目中只有一个 JPA 的实现产品, 则也可以不配置该节点. 
		-->
		<provider>org.hibernate.ejb.HibernatePersistence</provider>
	
		<!-- 添加持久化类 -->
		<class>com.atguigu.jpa.helloworld.Customer</class>
		<class>com.atguigu.jpa.helloworld.Order</class>
	
		<class>com.atguigu.jpa.helloworld.Department</class>
		<class>com.atguigu.jpa.helloworld.Manager</class>
	
		<class>com.atguigu.jpa.helloworld.Item</class>
		<class>com.atguigu.jpa.helloworld.Category</class>
		
		<!-- 
		配置二级缓存的策略 
		ALL:所有的实体类都被缓存
		NONE:所有的实体类都不被缓存. 
		ENABLE_SELECTIVE:标识 @Cacheable(true) 注解的实体类将被缓存
		DISABLE_SELECTIVE:缓存除标识 @Cacheable(false) 以外的所有实体类
		UNSPECIFIED:默认值,JPA 产品默认值将被使用
		-->
		<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
	
		<properties>
			<!-- 连接数据库的基本信息 -->
			<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
			<property name="javax.persistence.jdbc.url" value="jdbc:mysql:///jpa"/>
			<property name="javax.persistence.jdbc.user" value="root"/>
			<property name="javax.persistence.jdbc.password" value="1230"/>
			
			<!-- 配置 JPA 实现产品的基本属性. 配置 hibernate 的基本属性 -->
			<property name="hibernate.format_sql" value="true"/>
			<property name="hibernate.show_sql" value="true"/>
			<property name="hibernate.hbm2ddl.auto" value="update"/>
			
			<!-- 二级缓存相关 -->
			<property name="hibernate.cache.use_second_level_cache" value="true"/>
			<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>
			<property name="hibernate.cache.use_query_cache" value="true"/>
		</properties>
	</persistence-unit>
</persistence>

七、JPQL

JPQL语言,即 Java Persistence Query Language 的简称。JPQL 是一种和 SQL 非常类似的中间性和对象化查询语言,它最终会被编译成针对不同底层数据库的 SQL 查询,从而屏蔽不同数据库的差异。

JPQL语言的语句可以是 select 语句、update 语句或delete语句,它们都通过 Query 接口封装执行

八、JPA整合Spring

三种整合方式:
LocalEntityManagerFactoryBean:适用于那些仅使用 JPA 进行数据访问的项目,该 FactoryBean 将根据JPA PersistenceProvider 自动检测配置文件进行工作,一般从“META-INF/persistence.xml”读取配置信息,这种方式最简单,但不能设置 Spring 中定义的DataSource,且不支持 Spring 管理的全局事务

从JNDI中获取:用于从 Java EE 服务器获取指定的EntityManagerFactory,这种方式在进行 Spring 事务管理时一般要使用 JTA 事务管理

LocalContainerEntityManagerFactoryBean:适用于所有环境的 FactoryBean,能全面控制 EntityManagerFactory 配置,如指定 Spring 定义的 DataSource 等等。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值