JPA

JPA

源码获取:26bl

1:JPA概述

1.1:jpa是什么

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

  • Java EE 5.0 平台标准的ORM规范,使得应用程序以统一的方式访问持久层
    在这里插入图片描述

1.2:JPA和Hibernate的关系

  • JPA是hibernate的一个抽象(就像JDBC和JDBC驱动的关系):
    • JPA本质上就是一种ORM规范,不是ORM框架,因为JPA未提供ORM实现,它只是制定了一些规范,提供了一些编程的API接口,但具体的实现则由ORM厂商提供实现
    • Hibernate是实现:hibernate除了作为ORM框架之外,它也是一种JPA实现
  • 从功能上来说,JPA是Hibernate功能的一个子集

1.3:JPA的供应商

  • JPA的目标之一就是制定一个可以由很多供应商实现的API,目前Hibernate 3.2+,TopLink 10.1+以及OpenJPA都提供了JPA的实现
  • Hibernate
    • JPA的始作俑者就是Hibernate的作者
    • Hibernate从3.2开始兼容JPA
  • OpenJPA
    • OpenJPA是Apache组织提供的开源项目
  • TopLink
    • TopLink以前需要收费,如今开源了

1.4:JPA包括三方面的技术

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

2:HelloWorld

2.1:使用JPA持久化对象的步骤

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

2.2:创建HelloWorld

  • 创建JPA文件(选择2.0版本)
    在这里插入图片描述
    在这里插入图片描述
  • 加入jar包

需要的jar包:e0am
在这里插入图片描述

2.3:对persistence.xml文件进行配置

  • 配置连接数据库的基本信息
    在这里插入图片描述

    • 即:

      <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="123456"/>
      

    创建对应数据库
    在这里插入图片描述

  • 配置JPA实现产品的基本属性,配置hibernate的基本属性

    <!-- 配置JPA实现产品的基本属性,配置hibernate的基本属性(格式化sql;控制台自动显示sql;生成数据表的策略 -->
    			<property name="hibernate.format_sql" value="true"/>
    			<property name="hibernate.show_sql" value="true"/>
    			<property name="hibernate.hbm2ddl.auto" value="update"/>
    
  • 配置使用什么ORM产品来作为JPA的实现

    实际上配置的是javax.persistence.spi.PersistenceProvider接口的实现类
    若JPA项目中只有一个jpa的实现产品,则也可以不配置该节点

    • <!-- 配置使用什么ORM产品来作为JPA的实现 
        		1:实际上配置的是javax.persistence.spi.PersistenceProvider接口的实现类
        		2:若JPA项目中只有一个jpa的实现产品,则也可以不配置该节点
        		-->
        		<provider>org.hibernate.ejb.HibernatePersistence</provider>
      

2.4:创建持久化类

  • 创建一个包和一个Customer类,使用注解描述持久化类和数据表对应关系

    package com.atguigu.jpa.helloworld;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.Table;
    
    @Table(name="JPA_CUSTOMERS")
    @Entity
    public class Customer {
        
    	private Integer id;
    	private String lastName;
    	
    	private String email;
    	private int age;
        
    	public Customer() {
    		super();
    	}
    
    
    	public Customer(String lastName, int age) {
    		super();
    		this.lastName = lastName;
    		this.age = age;
    	}
    
    
    	@GeneratedValue(strategy=GenerationType.AUTO)//普通的创建表
    	@Id
    	public Integer getId() {
    		return id;
    	}
    	public void setId(Integer id) {
    		this.id = id;
    	}
    	@Column(name="last_name")
    	public String getLastName() {
    		return lastName;
    	}
    	public void setLastName(String lastName) {
    		this.lastName = lastName;
    	}
    	@Column
    	public String getEmail() {
    		return email;
    	}
    	public void setEmail(String email) {
    		this.email = email;
    	}
    	
    	public int getAge() {
    		return age;
    	}
    	public void setAge(int age) {
    		this.age = age;
    	}
    }
    
    
  • 添加持久化类

    		<!-- 添加持久化类 -->
    		 
    		<class>com.atguigu.jpa.helloworld.Customer</class>
    
  • 创建main,运行,即可生成数据表

    package com.atguigu.jpa.helloworld;
    
    import javax.persistence.EntityManager;
    import javax.persistence.EntityManagerFactory;
    import javax.persistence.EntityTransaction;
    import javax.persistence.Persistence;
    
    public class Main {
    
    	public static void main(String[] args) {
    		
    		//1:创建EntitymanagerFactory
    		String persistenceUnitName="jpa-1";
    	
    		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("xy@163.com");
    		customer.setLastName("lbs");
    		
    		entityManager.persist(customer);
    		
    		//5:提交事务
    		transaction.commit();
    		
    		//6:关闭EntityManager
    		entityManager.close();
    		
    		//7:关闭EntityManagerFactory
    		entityManagerFactory.close();
    		
    		System.out.println(customer.getInfo());
    	}
    
    }
    
    

3:JPA的基本注解

3.1:基本注解

  • @Entity

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

    • 当实体类与其映射的数据库表名不同时需要使用@Table标注说明,该标注与@Entity标注并列使用,置于实体类声明语句之前,可写于单独语句行,也可与声明语句同行

    • @Table标注常用的选项是name,用于指明数据库的表明

    • @Table标注还有两个选项,catalog和schema用于设置表所属的数据库目录或模式,通常为数据库名,uniqueConstraints选项用于设置约束条件,通常不须设置

      @Table(name="JPA_CUSTOMERS")
      @Entity
      public class Customer {
      
  • @Id

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

    • @Id标注也可置于属性的getter方法之前

      	@Column(name="CUSTOMER_ID")
      	@GeneratedValue(strategy=GenerationType.AUTO)//普通的创建表
      	@Id
      	public Integer getId() {
      		return id;
      	}
      	public void setId(Integer id) {
      		this.id = id;
      	}
      
  • @GeneratedValue

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

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

    • 当实体的属性与其映射的数据表的列不同名时需要@Column标注说明。该属性通常置于实体类的属性声明语句之前,还可与@Id标注一起使用

    • @Column标注的常用属性是name,用于映射数据库表的列名。此外,该标注还包含其他多个属性,如:unique、nullable、length等

    • @Column标注的columnDefinition属性:表示该字段在数据库中的实际类型。通常ORM框架可以根据属性类型自动判断数据库中字段的类型,但是对于Date类型仍无法确定数据库中字段类型究竟是DATE,TIME还是TIMESTAMP.此外,String的默认映射类型为VARCHAR

    • @Column标注也可置于属性的getter方法之前

      @Column(name="email",length=30,nullable=false)
      	public String getEmail() {
      		return email;
      	}
      	public void setEmail(String email) {
      		this.email = email;
      	}
      

3.2:其他注解

3.2.1:@Transient

  • 表示该属性并非一个到数据库表的字段的映射,ORM框架将忽略该属性

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

    //工具方法,不需要映射为数据表的一列
    	@Transient//不需要映射的列
    	public String getInfo() {
    		return "lastName:"+lastName+"   "+"email:"+email;
    	}
    

3.2.1:@Temporal

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

    • 增加两个字段,自动生成set,get方法

      	private Date createdTime;
      	private Date birth;
      
      	public Date getCreatedTime() {
      		return createdTime;
      	}
      	public void setCreatedTime(Date createdTime) {
      		this.createdTime = createdTime;
      	}
      
      	public Date getBirth() {
      		return birth;
      	}
      	public void setBirth(Date birth) {
      		this.birth = birth;
      	}
      
    • 持久化

      		Customer customer=new Customer();
      		customer.setAge(12);
      		customer.setEmail("xy@163.com");
      		customer.setLastName("lbs");
      		customer.setCreatedTime(new Date());
      		customer.setBirth(new Date());;
      		
      		entityManager.persist(customer);
      
    • 加入@Temporal注解

    @Temporal(TemporalType.TIMESTAMP)//精度指定时间戳
    	public Date getCreatedTime() {
    		return createdTime;
    	}
    	public void setCreatedTime(Date createdTime) {
    		this.createdTime = createdTime;
    	}
    	
    	@Temporal(TemporalType.DATE)//精度--date
    	public Date getBirth() {
    		return birth;
    	}
    	public void setBirth(Date birth) {
    		this.birth = birth;
    	}
    

在这里插入图片描述

4:用table来生成主键

  • 将当前主键的值单独保存到一个数据库的表中,主键的值每次都是从指定的表中查询来获得

  • 这种方法生成主键的策略可以适用于任何数据库,不必担心不同数据库不兼容造成的问题

    • 创建jpa_id_generators表
      在这里插入图片描述

    • 由pkColumnName和pkColumnValue确定某一行,由valueColumnName确定某一列
      在这里插入图片描述

    • 加入对应注解

      @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")//table创建表
      	@Id
      	public Integer getId() {
      		return id;
      	}
      	public void setId(Integer id) {
      		this.id = id;
      	}
      

5:JPA相关接口/类(API)

5.1:Persistence

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

  • createEntityManagerFactory方法有如下两个重载版本。

    • 带有一个参数的方法以JPA配置文件persistence.xml中的持久化单元名为参数

      String persistenceUnitName="jpa-1";
      EntityManagerFactory entityManagerFactory=
      				Persistence.createEntityManagerFactory(persistenceUnitName);//创建EntityManagerFactory常用方式,一个参数
      
    • 带有两个参数的方法:前一个参数含义相同,后一个参数Map类型,用于设置JPA的相关属性,这时将忽略其它地方设置的属性。Map对象的属性名必须是JPA实现库提供商的名字空间约定的属性名。

      String persistenceUnitName="jpa-1";
      		
      		Map<String,Object> properites=new HashMap<String,Object>();
      		properites.put("hibernate.show_sql", false);//改为false,不打印sql语句
      		
      		EntityManagerFactory entityManagerFactory=
      		Persistence.createEntityManagerFactory(persistenceUnitName,properites);//创建EntityManagerFactory第二种,两个参数
      

5.2:EntityManagerFactory

  • EntityManagerFactory接口主要用来创建EntityManager实例。该接口约定了如下4个方法

    • createEntityManager():用于创建实体管理器对象实例

    • createEntityManager(Map map) :用于创建实体管理器对象实例的重载方法, Map参数用于提供EntityManager的属性

    • isOpen() :检查EntityManagerFactory是否处于打开状态。实体管理器工厂创建后一直处于打开状态,除非调用close0方法将其关闭

    • close():关闭EntityManagerFactory。EntityManagerFactory关闭后将释放所有资源, isOpen0方法测试将返回false ,其它方法将不能调用,否则将导致IllegalStateException异常

      //创建EntityManager
      EntityManager entityManager=entityManagerFactory.createEntityManager();
      

5.3:EntityManager

  • 主键增长改为普通模式

    	@GeneratedValue(strategy=GenerationType.AUTO)//普通的创建表
    	@Id
    	public Integer getId() {
    		return id;
    	}
    	public void setId(Integer id) {
    		this.id = id;
    	}
    
  • 增加测试类,新建junit测试类

    package com.atguigu.jpa.test;
    
    import static org.junit.Assert.*;
    
    import java.util.Date;
    import java.util.List;
    
    import javax.persistence.EntityManager;
    import javax.persistence.EntityManagerFactory;
    import javax.persistence.EntityTransaction;
    import javax.persistence.Persistence;
    import javax.persistence.Query;
    
    import org.hibernate.ejb.QueryHints;
    import org.junit.After;
    import org.junit.Before;
    import org.junit.Test;
    
    import com.atguigu.jpa.helloworld.Category;
    import com.atguigu.jpa.helloworld.Customer;
    import com.atguigu.jpa.helloworld.Department;
    import com.atguigu.jpa.helloworld.Item;
    import com.atguigu.jpa.helloworld.Manager;
    import com.atguigu.jpa.helloworld.Order;
    
    public class JPATest {
    	
    	private EntityManagerFactory entityManagerFactory;
    	private EntityManager entityManager;
    	private EntityTransaction transaction;
    	
    	@Before
    	public void init() {
    		entityManagerFactory=Persistence.createEntityManagerFactory("jpa-1");
    		entityManager=entityManagerFactory.createEntityManager();
    		transaction =entityManager.getTransaction();
    		transaction.begin();
    	}
    	
    	@After
    	public void destory() {
    		transaction.commit();
    		entityManager.close();
    		entityManagerFactory.close();
    	}
    }
    

5.3.1:Find

  • 测试代码
	@Test
	public void testFind() {//创建的时候向数据库发送sql初始化数据
		Customer customer=entityManager.find(Customer.class,1);//主键值1
		System.out.println("--------------------------------");
		System.out.println(customer);
	}
  • 结果

    Hibernate: 
        select
            customer0_.id as id1_3_0_,
            customer0_.age as age2_3_0_,
            customer0_.birth as birth3_3_0_,
            customer0_.createdTime as createdT4_3_0_,
            customer0_.email as email5_3_0_,
            customer0_.last_name as last6_3_0_ 
        from
            JPA_CUSTOMERS customer0_ 
        where
            customer0_.id=?
    --------------------------------
    Customer [id=1, lastName=AA, email=aa@163.com, age=13, createdTime=2019-11-15 00:42:18.0, birth=2019-11-13]
    

5.3.2:getReference

  • 使用数据的时候才向数据库发送sql初始化数据,容易造成类加载异常(关闭EntityManager后输出会发生类加载异常)

  • 测试代码

    		@Test
    	public void testGetReference() {//使用数据的时候才向数据库发送sql初始化数据,容易造成类加载异常(关闭EntityManager后输出会发生类加载异常)
    		Customer customer=entityManager.getReference(Customer.class,1);//主键值1
    		System.out.println(customer.getClass().getName());
    		System.out.println("--------------------------------");
    		System.out.println(customer);
    	}
    
  • 结果

    com.atguigu.jpa.helloworld.Customer_$$_javassist_0
    --------------------------------
    Hibernate: 
        select
            customer0_.id as id1_3_0_,
            customer0_.age as age2_3_0_,
            customer0_.birth as birth3_3_0_,
            customer0_.createdTime as createdT4_3_0_,
            customer0_.email as email5_3_0_,
            customer0_.last_name as last6_3_0_ 
        from
            JPA_CUSTOMERS customer0_ 
        where
            customer0_.id=?
    Customer [id=1, lastName=AA, email=aa@163.com, age=13, createdTime=2019-11-15 00:42:18.0, birth=2019-11-13]
    

5.3.3:persist

  • 类似于hibernate的save方法,使对象由临时状态变为持久化状态,和hibernate的save方法不同:如果有id,则不能执行,抛出异常

  • 测试代码

    	//类似于hibernate的save方法,使对象由临时状态变为持久化状态
    	//和hibernate的save方法不同:如果有id,则不能执行,抛出异常
    	@Test
    	public void testPersistence() {
    		Customer customer=new Customer();
    		customer.setAge(15);
    		customer.setBirth(new Date());
    		customer.setCreatedTime(new Date());
    		customer.setEmail("bb@163.com");
    		customer.setLastName("BB");
    		
    		entityManager.persist(customer);
    		System.out.println(customer.getId());
    	}
    
  • 结果

    Hibernate: 
        insert 
        into
            JPA_CUSTOMERS
            (age, birth, createdTime, email, last_name) 
        values
            (?, ?, ?, ?, ?)
    17
    

5.3.4:remove

  • 类似于hibernate的delete方法,把对象从数据库移除.但是,只能移除持久化状态,而hibernate的delete还可以移除游离状态

  • 测试代码

    	//类似于hibernate的delete方法,把对象从数据库移除
    	//但是,只能移除持久化状态,而hibernate的delete还可以移除游离状态
    	@Test
    	public void testRemove() {
    //		Customer customer=new Customer();
    //		customer.setId(2);
    		
    		Customer customer=entityManager.find(Customer.class,3);
    		entityManager.remove(customer);
    	}
    
  • 结果

    Hibernate: 
        select
            customer0_.id as id1_3_0_,
            customer0_.age as age2_3_0_,
            customer0_.birth as birth3_3_0_,
            customer0_.createdTime as createdT4_3_0_,
            customer0_.email as email5_3_0_,
            customer0_.last_name as last6_3_0_ 
        from
            JPA_CUSTOMERS customer0_ 
        where
            customer0_.id=?
    Hibernate: 
        select
            orders0_.CUSTOMER_ID as CUSTOMER3_3_1_,
            orders0_.id as id1_1_1_,
            orders0_.id as id1_1_0_,
            orders0_.CUSTOMER_ID as CUSTOMER3_1_0_,
            orders0_.ORDER_NAME as ORDER2_1_0_ 
        from
            JAP_OEDERS orders0_ 
        where
            orders0_.CUSTOMER_ID=?
    Hibernate: 
        delete 
        from
            JPA_CUSTOMERS 
        where
            id=?
    
    • 错误操作抛出的异常

      java.lang.IllegalArgumentException: Removing a detached instance com.atguigu.jpa.helloworld.Customer#4
      	at org.hibernate.ejb.event.EJB3DeleteEventListener.performDetachedEntityDeletionCheck(EJB3DeleteEventListener.java:67)
      	at org.hibernate.event.internal.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:107)
      	at org.hibernate.event.internal.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:74)
      	at org.hibernate.internal.SessionImpl.fireDelete(SessionImpl.java:957)
      	at org.hibernate.internal.SessionImpl.delete(SessionImpl.java:936)
      	。。。。。
      
      

5.3.5:merge

  • merge(T entity)用于处理Entity的同步。即数据库的插入和更新操作
    在这里插入图片描述

  • 在JPA规范中,EntityManager是完成持久化操作的核心对象。实体作为普通Java对象,只有在调用EntityManager将其持久化后才会变成持久化对象。EntityManager 对象在一组实体类与底层数据源之间进行O/R映射的管理。它可以用来管理和更新Entity Bean,根椐主键查找Entity Bean,还可以通过JPQL语句查询实体。

  • 实体的状态

    • 新建状态:新创建的对象,尚未拥有持久性主键。
    • 持久化状态:已经拥有持久性主键并和持久化建立了上下文环境
    • 游离状态:拥有持久化主键,但是没有与持久化建立上下文环境
    • 删除状态:拥有持久化主键,已经和持久化建立上下文环境,但是从数据库中删除。
  • 测试代码:临时对象

    /*类似于hibernateSerssion 的savePOrUpdate方法 
    	 * 1:若传入的是一个临时对象,
    	 * 会创建一个新的对象,把临时对象的属性复制到新的对象中,然后对新的对象执行持久化操作,所以
    	 * 新的对象有id,以前的没有id
    	 * 
    	 */
    	@Test
    	public void testMerge1() {
    		Customer customer=new Customer();
    		customer.setAge(18);
    		customer.setBirth(new Date());
    		customer.setCreatedTime(new Date());
    		customer.setEmail("cc@163.com");
    		customer.setLastName("cc");
    		
    		Customer customer2=entityManager.merge(customer);
    		
    		System.out.println("customer#id:"+customer.getId());
    		System.out.println("customer2#id:"+customer2.getId());
    		
    	}
    
    • 结果
    Hibernate: 
        insert 
        into
            JPA_CUSTOMERS
            (age, birth, createdTime, email, last_name) 
        values
            (?, ?, ?, ?, ?)
    customer#id:null
    customer2#id:18
    
  • 测试代码:游离对象,既有oid。在EntityManager缓存中没有该对象;在数据库中没有对应的记录

    /*
    	 * 若传入的是一个游离对象,既有oid
    	 *1:若在EntityManager缓存中没有该对象
    	 *2:若在数据库中没有对应的记录
    	 *3:jpa会创建一个新的对象,然后把当前游离对象的属性复制到新创建的对象,
    	 * 4:对新创建的对象执行insert操作
    	 * 
    	 */
    	
    	@Test
    	public void testMerge2() {
    		Customer customer=new Customer();
    		customer.setAge(18);
    		customer.setBirth(new Date());
    		customer.setCreatedTime(new Date());
    		customer.setEmail("dd@163.com");
    		customer.setLastName("dd");
    		
    		customer.setId(100);
    		
    		Customer customer2=entityManager.merge(customer);
    		
    		System.out.println("customer#id:"+customer.getId());
    		System.out.println("customer2#id:"+customer2.getId());
    		
    	}
    
    • 结果
    Hibernate: 
        select
            customer0_.id as id1_3_0_,
            customer0_.age as age2_3_0_,
            customer0_.birth as birth3_3_0_,
            customer0_.createdTime as createdT4_3_0_,
            customer0_.email as email5_3_0_,
            customer0_.last_name as last6_3_0_ 
        from
            JPA_CUSTOMERS customer0_ 
        where
            customer0_.id=?
    Hibernate: 
        insert 
        into
            JPA_CUSTOMERS
            (age, birth, createdTime, email, last_name) 
        values
            (?, ?, ?, ?, ?)
    customer#id:100
    customer2#id:19
    
  • 测试代码:游离对象,既有oid。在EntityManager缓存中没有该对象;在数据库中有对应的记录

    /*
    	 * 若传入的是一个游离对象,既有oid
    	 *1:若在EntityManager缓存中没有该对象
    	 *2:若在数据库中有对应的记录
    	 *3:jpa会查询对应的的记录,然后返回该记录的一个对象,再然后把游离对象的属性复制到查询对象去,
    	 * 4:对查询到的的对象执行insert操作
    	 * 
    	 */
    	@Test
    	public void testMerge3() {
    		Customer customer=new Customer();
    		customer.setAge(18);
    		customer.setBirth(new Date());
    		customer.setCreatedTime(new Date());
    		customer.setEmail("ee@163.com");
    		customer.setLastName("ee");
    		
    		customer.setId(4);
    		
    		Customer customer2=entityManager.merge(customer);
    		
    		System.out.println(customer==customer2);
    		
    		
    	}
    
    • 结果
    Hibernate: 
        select
            customer0_.id as id1_3_0_,
            customer0_.age as age2_3_0_,
            customer0_.birth as birth3_3_0_,
            customer0_.createdTime as createdT4_3_0_,
            customer0_.email as email5_3_0_,
            customer0_.last_name as last6_3_0_ 
        from
            JPA_CUSTOMERS customer0_ 
        where
            customer0_.id=?
    false
    Hibernate: 
        update
            JPA_CUSTOMERS 
        set
            age=?,
            birth=?,
            createdTime=?,
            email=?,
            last_name=? 
        where
            id=?
    
  • 测试代码:游离对象,既有oid。在EntityManager缓存中有该对象;在数据库中有对应的记录

    /*
    	 * 若传入的是一个游离对象,既有oid
    	 *1:若在EntityManager缓存中有该对象
    	 *2:若在数据库中有对应的记录
    	 *3:jpa会把游离对象的属性复制到查询到EntityManager缓存的对象中,
    	 * 4:EntityManager缓存中的对象执行UPDATE
    	 * 
    	 */
    	@Test
    	public void testMerge4() {
    		Customer customer=new Customer();
    		customer.setAge(18);
    		customer.setBirth(new Date());
    		customer.setCreatedTime(new Date());
    		customer.setEmail("ee@163.com");
    		customer.setLastName("DD");
    		
    		customer.setId(4);
    		
    		Customer customer2=entityManager.find(Customer.class,4);
    		entityManager.merge(customer);
    		System.out.println(customer==customer2);
    		
    		
    	}
    
    • 结果
    Hibernate: 
        select
            customer0_.id as id1_3_0_,
            customer0_.age as age2_3_0_,
            customer0_.birth as birth3_3_0_,
            customer0_.createdTime as createdT4_3_0_,
            customer0_.email as email5_3_0_,
            customer0_.last_name as last6_3_0_ 
        from
            JPA_CUSTOMERS customer0_ 
        where
            customer0_.id=?
    false
    Hibernate: 
        update
            JPA_CUSTOMERS 
        set
            age=?,
            birth=?,
            createdTime=?,
            email=?,
            last_name=? 
        where
            id=?
    

5.5.6:flush

  • 正常情况提交事物持久化

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

  • setFlushMode.(FlushModeType flushMode) :设置持久上下文环境的Flush模式。参数可以取2个枚举

    • FlushModeType.AUTQ为自动更新数据库实体,
    • FlushModeType.COMMIT为直到提交事务时才更新数据库记录
  • getFlushMode() :获取持久上下文环境的Flush模式。返回FlushModeType类的枚举值

  • 代码

    //同步持久上下文环境
    	@Test
    	public void testFlush() {
    		Customer customer=entityManager.find(Customer.class,1);
    		System.out.println(customer);
    		
    		customer.setLastName("AA");
    		
    		entityManager.flush();
    		
    	}
    
    • 结果

      Hibernate: 
          select
              customer0_.id as id1_3_0_,
              customer0_.age as age2_3_0_,
              customer0_.birth as birth3_3_0_,
              customer0_.createdTime as createdT4_3_0_,
              customer0_.email as email5_3_0_,
              customer0_.last_name as last6_3_0_ 
          from
              JPA_CUSTOMERS customer0_ 
          where
              customer0_.id=?
      Customer [id=1, lastName=AA, email=aa@163.com, age=13, createdTime=2019-11-15 00:42:18.0, birth=2019-11-13]
      

5.5.7:refresh

  • refresh (Object entity) :用数据库实体记录的值更新实体对象的状态,即更新实例的属性值。

  • 代码

    @Test
    	public void testReflush() {
    		Customer customer=entityManager.find(Customer.class,1);
    		customer=entityManager.find(Customer.class,1);
    		
    		entityManager.refresh(customer);
    	}
    
    • 结果(如果没有refresh,只发送一条select语句)

      Hibernate: 
          select
              customer0_.id as id1_3_0_,
              customer0_.age as age2_3_0_,
              customer0_.birth as birth3_3_0_,
              customer0_.createdTime as createdT4_3_0_,
              customer0_.email as email5_3_0_,
              customer0_.last_name as last6_3_0_ 
          from
              JPA_CUSTOMERS customer0_ 
          where
              customer0_.id=?
      Hibernate: 
          select
              customer0_.id as id1_3_0_,
              customer0_.age as age2_3_0_,
              customer0_.birth as birth3_3_0_,
              customer0_.createdTime as createdT4_3_0_,
              customer0_.email as email5_3_0_,
              customer0_.last_name as last6_3_0_ 
          from
              JPA_CUSTOMERS customer0_ 
          where
              customer0_.id=?
      

5.5.8:其他

  • clear() :清除持久上下文环境.断开所有关联的实体。如果这时还有未提交
    的更新则会被撤消。

  • contains (Object entity) :判断一个实例是否属于当前持久上下文环境管理
    的实体。

  • isOpen() :判断当前的实体管理器是否是打开状态。
    getTransaction():返回资源层的事务对象。EntityTransaction实例可以用
    于开始和提交多个事务。

  • close():关闭实体管理器。之后若调用实体管理器实例的方法或其派生的查
    询对象的方法都将抛出llegalstateException异常,除了getTransaction
    和isOpen方法(返回false)。不过.当与实体管理器关联的事务处于活动状
    态时,调用close方法后持久上下文将仍处于被管理状态,直到事务完成。

  • JPQL用到的EntityManager方法

    • createQuery (String sqlString) :创建一个查询对象
    • createNamedQuery (String name) :根据命名的查询语句块创建查询对象。参数为命名的查询语句
    • createNativeQuery (String sqlString ) :使用标准SQL语句创建查询对象。参数为标准SQL语句字符串
    • createNativeQuery (String sqls, String resultSetMapping) :使用标准SQL语句创建查询对象,并指定返回结果集Map的名称

5.4:EntityTransaction

EntityTraction:接口用来管理资源层实体管理器的事务操作。通过调用实体管理器的getTransaction获得其实例

方法:

  • begin ():用于启动一个事务,此后的多个数据库操作将作为整体被提交或撤消。若这时事务已启动则会抛出llegalStateException异常。
  • commit ():用于提交当前事务。即将事务启动以后的所有数据库更新操作持久化至数据库中。
  • rollback ():撤消(回滚)当前事务。即撤消事务启动后的所有数据库更新操作,从而不对数据库产生影响。
  • setRollbgckOnly():使当前事务只能被撤消。
  • getRollbackOnly():查看当前事务是否设置了只能撤消标志。
  • isActive ():查看当前事务是否是活动的。如果返回true则不能调用begin方法,否则将抛出IlegalStateException异常;如果返回false则不能调用commit、rollback.setRollbackOnly及getRollbackOnly方法,否则将抛出IllegalStateException异常。

6:映射关联关系

6.1:映射单向多对一的关联关系

  • 创建Order实体类

    package com.atguigu.jpa.helloworld;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.JoinColumn;
    import javax.persistence.ManyToOne;
    import javax.persistence.Table;
    
    @Table(name="JAP_OEDERS")
    @Entity
    public class Order {
    	
    	private Integer id;
    	private String orderName;
    	
    	private Customer customer;
    	
    	@GeneratedValue
    	@Id
    	public Integer getId() {
    		return id;
    	}
    	public void setId(Integer id) {
    		this.id = id;
    	}
    	@Column(name="ORDER_NAME")
    	public String getOrderName() {
    		return orderName;
    	}
    	public void setOrderName(String orderName) {
    		this.orderName = orderName;
    	}
    	//映射单项多对一的关联关系
    	//使用@ManyToOne来映射多对一的关联关系
    	//使用@JoinColumn(name="")来映射外键,列名
    	@JoinColumn(name="CUSTOMER_ID")
    	@ManyToOne(fetch=FetchType.LAZY)//使用懒加载方式
    	public Customer getCustomer() {
    		return customer;
    	}
    	public void setCustomer(Customer customer) {
    		this.customer = customer;
    	}
    	
    
    }
    
    • 配置文件添加实体类

      • <class>com.atguigu.jpa.helloworld.Order</class>
        
    • 生成Order
      在这里插入图片描述

    • 测试保存(persist)

      • 代码

        //保存多对一时,建议先保存1的一端,后保存n的一端,这样不会额外多出UPDATE语句
        	@Test
        	public void testManyToOnePersist() {
        		Customer customer=new Customer();
        		customer.setAge(18);
        		customer.setBirth(new Date());
        		customer.setCreatedTime(new Date());
        		customer.setEmail("gg@163.com");
        		customer.setLastName("GG");
        		
        		Order order1=new Order();
        		order1.setOrderName("0-GG-1");
        		
        		Order order2=new Order();
        		order2.setOrderName("0-GG-2");
        		
        		//设置关联关系
        		order1.setCustomer(customer);
        		order2.setCustomer(customer);
        		
        		//执行保存操作
        		
        		entityManager.persist(order1);
        		entityManager.persist(order2);
        		entityManager.persist(customer);
        	}
        
        • 先保存多的,再保存一的结果

          Hibernate: 
              insert 
              into
                  JAP_OEDERS
                  (CUSTOMER_ID, ORDER_NAME) 
              values
                  (?, ?)
          Hibernate: 
              insert 
              into
                  JAP_OEDERS
                  (CUSTOMER_ID, ORDER_NAME) 
              values
                  (?, ?)
          Hibernate: 
              insert 
              into
                  JPA_CUSTOMERS
                  (age, birth, createdTime, email, last_name) 
              values
                  (?, ?, ?, ?, ?)
          Hibernate: 
              update
                  JAP_OEDERS 
              set
                  CUSTOMER_ID=?,
                  ORDER_NAME=? 
              where
                  id=?
          Hibernate: 
              update
                  JAP_OEDERS 
              set
                  CUSTOMER_ID=?,
                  ORDER_NAME=? 
              where
                  id=?
          
          • 先保存一的,再保存多的结果

            Hibernate: 
                insert 
                into
                    JPA_CUSTOMERS
                    (age, birth, createdTime, email, last_name) 
                values
                    (?, ?, ?, ?, ?)
            Hibernate: 
                insert 
                into
                    JAP_OEDERS
                    (CUSTOMER_ID, ORDER_NAME) 
                values
                    (?, ?)
            Hibernate: 
                insert 
                into
                    JAP_OEDERS
                    (CUSTOMER_ID, ORDER_NAME) 
                values
                    (?, ?)
            
    • 测试获取(find)

      • 代码

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

          Hibernate: 
              select
                  order0_.id as id1_1_0_,
                  order0_.CUSTOMER_ID as CUSTOMER3_1_0_,
                  order0_.ORDER_NAME as ORDER2_1_0_ 
              from
                  JAP_OEDERS order0_ 
              where
                  order0_.id=?
          0-yy-1
          Hibernate: 
              select
                  customer0_.id as id1_3_0_,
                  customer0_.age as age2_3_0_,
                  customer0_.birth as birth3_3_0_,
                  customer0_.createdTime as createdT4_3_0_,
                  customer0_.email as email5_3_0_,
                  customer0_.last_name as last6_3_0_ 
              from
                  JPA_CUSTOMERS customer0_ 
              where
                  customer0_.id=?
          yy
          
    • 测试删除(remove)

      • 代码

        	//不能直接删除1的一端,因为有外键约束,先删除多的一端正常
        	@Test
        	public void testManyToOneRemove() {
        		Customer customer=entityManager.find(Customer.class,11);
        		entityManager.remove(customer);
        	}
        
        • 报错

          javax.persistence.PersistenceException: [PersistenceUnit: jpa-1] Unable to build EntityManagerFactory
          	at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:924)
          	at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:899)
          	at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:59)
          	at 。。。。
          
          
    • 测试修改(update)

      • 代码

        	@Test
        	public void testManyToOneUpdate() {
        		Order order=entityManager.find(Order.class, 4);
        		order.getCustomer().setLastName("XX");
        		
        	}
        
        • 结果

          Hibernate: 
              select
                  order0_.id as id1_1_0_,
                  order0_.CUSTOMER_ID as CUSTOMER3_1_0_,
                  order0_.ORDER_NAME as ORDER2_1_0_ 
              from
                  JAP_OEDERS order0_ 
              where
                  order0_.id=?
          Hibernate: 
              select
                  customer0_.id as id1_3_0_,
                  customer0_.age as age2_3_0_,
                  customer0_.birth as birth3_3_0_,
                  customer0_.createdTime as createdT4_3_0_,
                  customer0_.email as email5_3_0_,
                  customer0_.last_name as last6_3_0_ 
              from
                  JPA_CUSTOMERS customer0_ 
              where
                  customer0_.id=?
          Hibernate: 
              update
                  JPA_CUSTOMERS 
              set
                  age=?,
                  birth=?,
                  createdTime=?,
                  email=?,
                  last_name=? 
              where
                  id=?
          

6.2:映射单向一对多的关联关系

  • 修改Order,注释掉其中的customer属性和set,get方法

    package com.atguigu.jpa.helloworld;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.JoinColumn;
    import javax.persistence.ManyToOne;
    import javax.persistence.Table;
    
    @Table(name="JAP_OEDERS")
    @Entity
    public class Order {
    	
    	private Integer id;
    	private String orderName;
    	
    //	private Customer customer;
    	
    	@GeneratedValue
    	@Id
    	public Integer getId() {
    		return id;
    	}
    	public void setId(Integer id) {
    		this.id = id;
    	}
    	@Column(name="ORDER_NAME")
    	public String getOrderName() {
    		return orderName;
    	}
    	public void setOrderName(String orderName) {
    		this.orderName = orderName;
    	}
    //	//映射单项多对一的关联关系
    //	//使用@ManyToOne来映射多对一的关联关系
    //	//使用@JoinColumn(name="")来映射外键,列名
    //	@JoinColumn(name="CUSTOMER_ID")
    //	@ManyToOne(fetch=FetchType.LAZY)//使用懒加载方式
    //	public Customer getCustomer() {
    //		return customer;
    //	}
    //	public void setCustomer(Customer customer) {
    //		this.customer = customer;
    //	}
    	
    
    }
    
  • 添加Customer基本属性和方法

    package com.atguigu.jpa.helloworld;
    
    import java.util.Date;
    import java.util.HashSet;
    import java.util.Set;
    
    import javax.persistence.Cacheable;
    import javax.persistence.CascadeType;
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.JoinColumn;
    import javax.persistence.NamedQuery;
    import javax.persistence.OneToMany;
    import javax.persistence.Table;
    import javax.persistence.TableGenerator;
    import javax.persistence.Temporal;
    import javax.persistence.TemporalType;
    import javax.persistence.Transient;
    
    @Table(name="JPA_CUSTOMERS")
    @Entity
    public class Customer {
    	
    	
    	
    
    	private Integer id;
    	private String lastName;
    	
    	private String email;
    	private int age;
    	
    	private Date createdTime;
    	private Date birth;
    	
    	private Set<Order> orders=new HashSet<>();
    	
    	
    	public Customer() {
    		super();
    	}
    
    
    	public Customer(String lastName, int age) {
    		super();
    		this.lastName = lastName;
    		this.age = age;
    	}
    
    
    	@GeneratedValue(strategy=GenerationType.AUTO)//普通的创建表
    //	@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")//table创建表
    	@Id
    	public Integer getId() {
    		return id;
    	}
    	public void setId(Integer id) {
    		this.id = id;
    	}
    	@Column(name="last_name",length=50,nullable=false)
    	public String getLastName() {
    		return lastName;
    	}
    	public void setLastName(String lastName) {
    		this.lastName = lastName;
    	}
    	@Column(name="email",length=30,nullable=false)
    	public String getEmail() {
    		return email;
    	}
    	public void setEmail(String email) {
    		this.email = email;
    	}
    	
    	public int getAge() {
    		return age;
    	}
    	public void setAge(int age) {
    		this.age = age;
    	}
    	
    	@Temporal(TemporalType.TIMESTAMP)//精度指定时间戳
    	public Date getCreatedTime() {
    		return createdTime;
    	}
    	public void setCreatedTime(Date createdTime) {
    		this.createdTime = createdTime;
    	}
    	
    	@Temporal(TemporalType.DATE)//精度--date
    	public Date getBirth() {
    		return birth;
    	}
    	public void setBirth(Date birth) {
    		this.birth = birth;
    	}
    	//注意若在1的一端,@OneToMany使用mapperBy属性,则不能使用@JoinColumn
    	
    	//映射单向一对多的关联关系
    	//使用@OneToMany来映射1-n的关联关系
    	//使用@JoinColumn映射外键列的名称
    	//可以使用@OneToMany修改默认属性,lazy
    	//可以通过修改@OneToMany的cascade属性来修改默认的删除策略
    	@JoinColumn(name="CUSTOMER_ID")
    	@OneToMany
    	public Set<Order> getOrders() {
    		return orders;
    	}
    	public void setOrders(Set<Order> orders) {
    		this.orders = orders;
    	}
    	//工具方法,不需要映射为数据表的一列
    	@Transient//不需要映射的列
    	public String getInfo() {
    		return "lastName:"+lastName+"   "+"email:"+email;
    	}
    	
    	@Override
    	public String toString() {
    		return "Customer [id=" + id + ", lastName=" + lastName + ", email=" + email + ", age=" + age + ", createdTime="
    				+ createdTime + ", birth=" + birth + "]";
    	}
    }
    
    
    • 测试保存

      //	单向1-n关联关系执行保存的时候,一定会多出update语句,
      //	因为n的一端再插入时不会同时插入外键列
      	@Test
      	public void testOneToManyPersist() {
      		Customer customer=new Customer();
      		customer.setAge(18);
      		customer.setBirth(new Date());
      		customer.setCreatedTime(new Date());
      		customer.setEmail("gg@163.com");
      		customer.setLastName("GG");
      		
      		Order order1=new Order();
      		order1.setOrderName("0-gg-1");
      		
      		Order order2=new Order();
      		order2.setOrderName("0-gg-1");
      		
      		//建立关联关系
      		customer.getOrders().add(order1);
      		customer.getOrders().add(order2);
      
      		//执行保存操作
      		
      		entityManager.persist(order1);
      		entityManager.persist(order2);
      		
      		entityManager.persist(customer);
      	}
      
      • 结果

        Hibernate: 
            insert 
            into
                JAP_OEDERS
                (ORDER_NAME) 
            values
                (?)
        Hibernate: 
            insert 
            into
                JAP_OEDERS
                (ORDER_NAME) 
            values
                (?)
        Hibernate: 
            insert 
            into
                JPA_CUSTOMERS
                (age, birth, createdTime, email, last_name) 
            values
                (?, ?, ?, ?, ?)
        Hibernate: 
            update
                JAP_OEDERS 
            set
                CUSTOMER_ID=? 
            where
                id=?
        Hibernate: 
            update
                JAP_OEDERS 
            set
                CUSTOMER_ID=? 
            where
                id=?
        
    • 测试find

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

        Hibernate: 
            select
                customer0_.id as id1_3_0_,
                customer0_.age as age2_3_0_,
                customer0_.birth as birth3_3_0_,
                customer0_.createdTime as createdT4_3_0_,
                customer0_.email as email5_3_0_,
                customer0_.last_name as last6_3_0_ 
            from
                JPA_CUSTOMERS customer0_ 
            where
                customer0_.id=?
        yy
        Hibernate: 
            select
                orders0_.CUSTOMER_ID as CUSTOMER3_3_1_,
                orders0_.id as id1_1_1_,
                orders0_.id as id1_1_0_,
                orders0_.ORDER_NAME as ORDER2_1_0_ 
            from
                JAP_OEDERS orders0_ 
            where
                orders0_.CUSTOMER_ID=?
        0
        
        • //加载方式修改为EAGER
          @OneToMany(fetch=FetchType.EAGER)
          
          • 结果

            Hibernate: 
                select
                    customer0_.id as id1_3_1_,
                    customer0_.age as age2_3_1_,
                    customer0_.birth as birth3_3_1_,
                    customer0_.createdTime as createdT4_3_1_,
                    customer0_.email as email5_3_1_,
                    customer0_.last_name as last6_3_1_,
                    orders1_.CUSTOMER_ID as CUSTOMER3_3_3_,
                    orders1_.id as id1_1_3_,
                    orders1_.id as id1_1_0_,
                    orders1_.ORDER_NAME as ORDER2_1_0_ 
                from
                    JPA_CUSTOMERS customer0_ 
                left outer join
                    JAP_OEDERS orders1_ 
                        on customer0_.id=orders1_.CUSTOMER_ID 
                where
                    customer0_.id=?
            yy
            0
            
    • 测试remove

      	//默认情况下,若删除1的一端,则会先把关联的n的一端外键置空,然后删除
      	//可以通过修改@OneToMany的cascade属性来修改默认的删除策略
      	@Test
      	public void testOneToManyRemove() {
      		Customer customer=entityManager.find(Customer.class,22);
      		entityManager.remove(customer);
      	}
      
      • 结果

        Hibernate: 
            select
                customer0_.id as id1_3_1_,
                customer0_.age as age2_3_1_,
                customer0_.birth as birth3_3_1_,
                customer0_.createdTime as createdT4_3_1_,
                customer0_.email as email5_3_1_,
                customer0_.last_name as last6_3_1_,
                orders1_.CUSTOMER_ID as CUSTOMER3_3_3_,
                orders1_.id as id1_1_3_,
                orders1_.id as id1_1_0_,
                orders1_.ORDER_NAME as ORDER2_1_0_ 
            from
                JPA_CUSTOMERS customer0_ 
            left outer join
                JAP_OEDERS orders1_ 
                    on customer0_.id=orders1_.CUSTOMER_ID 
            where
                customer0_.id=?
        Hibernate: 
            update
                JAP_OEDERS 
            set
                CUSTOMER_ID=null 
            where
                CUSTOMER_ID=?
        Hibernate: 
            delete 
            from
                JPA_CUSTOMERS 
            where
                id=?
        
        • @OneToMany(cascade= {CascadeType.REMOVE}),删除时会级联删除

          //注意若在1的一端,@OneToMany使用mapperBy属性,则不能使用@JoinColumn
          	
          	//映射单向一对多的关联关系
          	//使用@OneToMany来映射1-n的关联关系
          	//使用@JoinColumn映射外键列的名称
          	//可以使用@OneToMany修改默认属性,lazy
          	//可以通过修改@OneToMany的cascade属性来修改默认的删除策略
          	//@OneToMany(fetch=FetchType.LAZY,cascade= {CascadeType.REMOVE},mappedBy="customer")//mappedBy="customer",customer来维护
          
    • 测试update

      • 	@Test
        	public void testUpdate() {
        		Customer customer=entityManager.find(Customer.class,15);
        		
        		customer.getOrders().iterator().next().setOrderName("0-xxx-10");
        	}
        
        • 结果

          Hibernate: 
              select
                  customer0_.id as id1_3_1_,
                  customer0_.age as age2_3_1_,
                  customer0_.birth as birth3_3_1_,
                  customer0_.createdTime as createdT4_3_1_,
                  customer0_.email as email5_3_1_,
                  customer0_.last_name as last6_3_1_,
                  orders1_.CUSTOMER_ID as CUSTOMER3_3_3_,
                  orders1_.id as id1_1_3_,
                  orders1_.id as id1_1_0_,
                  orders1_.ORDER_NAME as ORDER2_1_0_ 
              from
                  JPA_CUSTOMERS customer0_ 
              left outer join
                  JAP_OEDERS orders1_ 
                      on customer0_.id=orders1_.CUSTOMER_ID 
              where
                  customer0_.id=?
          Hibernate: 
              update
                  JAP_OEDERS 
              set
                  ORDER_NAME=? 
              where
                  id=?
          

6.3:映射双向多对一的关联关系

  • 打开Order中的Customer属性和方法,注意@JoinColumn(name=“CUSTOMER_ID”)要一致,即:

    package com.atguigu.jpa.helloworld;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.JoinColumn;
    import javax.persistence.ManyToOne;
    import javax.persistence.Table;
    
    @Table(name="JAP_OEDERS")
    @Entity
    public class Order {
    	
    	private Integer id;
    	private String orderName;
    	
    	private Customer customer;
    	
    	@GeneratedValue
    	@Id
    	public Integer getId() {
    		return id;
    	}
    	public void setId(Integer id) {
    		this.id = id;
    	}
    	@Column(name="ORDER_NAME")
    	public String getOrderName() {
    		return orderName;
    	}
    	public void setOrderName(String orderName) {
    		this.orderName = orderName;
    	}
    	//映射单项多对一的关联关系
    	//使用@ManyToOne来映射多对一的关联关系
    	//使用@JoinColumn(name="")来映射外键,列名
    	@JoinColumn(name="CUSTOMER_ID")
    	@ManyToOne(fetch=FetchType.EAGER)//使用懒加载方式
    	public Customer getCustomer() {
    		return customer;
    	}
    	public void setCustomer(Customer customer) {
    		this.customer = customer;
    	}
    	
    
    }
    
    
    package com.atguigu.jpa.helloworld;
    
    import java.util.Date;
    import java.util.HashSet;
    import java.util.Set;
    
    import javax.persistence.Cacheable;
    import javax.persistence.CascadeType;
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.JoinColumn;
    import javax.persistence.NamedQuery;
    import javax.persistence.OneToMany;
    import javax.persistence.Table;
    import javax.persistence.TableGenerator;
    import javax.persistence.Temporal;
    import javax.persistence.TemporalType;
    import javax.persistence.Transient;
    
    //@NamedQuery(name="testNamedQuery",query="FROM Customer c WHERE c.id=?")
    //@Cacheable(true)
    @Table(name="JPA_CUSTOMERS")
    @Entity
    public class Customer {
    	
    	
    	
    
    	private Integer id;
    	private String lastName;
    	
    	private String email;
    	private int age;
    	
    	private Date createdTime;
    	private Date birth;
    	
    	private Set<Order> orders=new HashSet<>();
    	
    	
    	public Customer() {
    		super();
    	}
    
    
    	public Customer(String lastName, int age) {
    		super();
    		this.lastName = lastName;
    		this.age = age;
    	}
    
    
    	@GeneratedValue(strategy=GenerationType.AUTO)//普通的创建表
    //	@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")//table创建表
    	@Id
    	public Integer getId() {
    		return id;
    	}
    	public void setId(Integer id) {
    		this.id = id;
    	}
    	@Column(name="last_name",length=50,nullable=false)
    	public String getLastName() {
    		return lastName;
    	}
    	public void setLastName(String lastName) {
    		this.lastName = lastName;
    	}
    	@Column(name="email",length=30,nullable=false)
    	public String getEmail() {
    		return email;
    	}
    	public void setEmail(String email) {
    		this.email = email;
    	}
    	
    	public int getAge() {
    		return age;
    	}
    	public void setAge(int age) {
    		this.age = age;
    	}
    	
    	@Temporal(TemporalType.TIMESTAMP)//精度指定时间戳
    	public Date getCreatedTime() {
    		return createdTime;
    	}
    	public void setCreatedTime(Date createdTime) {
    		this.createdTime = createdTime;
    	}
    	
    	@Temporal(TemporalType.DATE)//精度--date
    	public Date getBirth() {
    		return birth;
    	}
    	public void setBirth(Date birth) {
    		this.birth = birth;
    	}
    	//注意若在1的一端,@OneToMany使用mapperBy属性,则不能使用@JoinColumn
    	
    	//映射单向一对多的关联关系
    	//使用@OneToMany来映射1-n的关联关系
    	//使用@JoinColumn映射外键列的名称
    	//可以使用@OneToMany修改默认属性,lazy
    	//可以通过修改@OneToMany的cascade属性来修改默认的删除策略
    	//@OneToMany(fetch=FetchType.LAZY,cascade= {CascadeType.REMOVE},mappedBy="customer")//mappedBy="customer",customer来维护
    	@JoinColumn(name="CUSTOMER_ID")
    	@OneToMany(fetch=FetchType.EAGER)
    	public Set<Order> getOrders() {
    		return orders;
    	}
    	public void setOrders(Set<Order> orders) {
    		this.orders = orders;
    	}
    	//工具方法,不需要映射为数据表的一列
    	@Transient//不需要映射的列
    	public String getInfo() {
    		return "lastName:"+lastName+"   "+"email:"+email;
    	}
    	
    	@Override
    	public String toString() {
    		return "Customer [id=" + id + ", lastName=" + lastName + ", email=" + email + ", age=" + age + ", createdTime="
    				+ createdTime + ", birth=" + birth + "]";
    	}
    }
    
    
  • 测试保存

    //	若是双向1-n,执行保存时,先保存n的一端,在保存1,多出2n条语句
    //	若是双向1-n,执行保存时,先保存1,多出n条
    //	在进行双向1-n关联时,建议使用n的一方维护关联关系。这样可以减少sql语句。
    //	注意若在1的一端,@OneToMany使用mapperBy属性,则不能使用@JoinColumn
    	@Test
    	public void testOneToManyPersist() {
    		Customer customer=new Customer();
    		customer.setAge(18);
    		customer.setBirth(new Date());
    		customer.setCreatedTime(new Date());
    		customer.setEmail("gg@163.com");
    		customer.setLastName("GG");
    		
    		Order order1=new Order();
    		order1.setOrderName("0-gg-1");
    		
    		Order order2=new Order();
    		order2.setOrderName("0-gg-1");
    		
    		//建立关联关系
    		customer.getOrders().add(order1);
    		customer.getOrders().add(order2);
    		
    		order1.setCustomer(customer);
    		order2.setCustomer(customer);
    		//执行保存操作
    		
    		entityManager.persist(order1);
    		entityManager.persist(order2);
    		
    		entityManager.persist(customer);
    	}
    
    • 结果

      Hibernate: 
          insert 
          into
              JAP_OEDERS
              (CUSTOMER_ID, ORDER_NAME) 
          values
              (?, ?)
      Hibernate: 
          insert 
          into
              JAP_OEDERS
              (CUSTOMER_ID, ORDER_NAME) 
          values
              (?, ?)
      Hibernate: 
          insert 
          into
              JPA_CUSTOMERS
              (age, birth, createdTime, email, last_name) 
          values
              (?, ?, ?, ?, ?)
      Hibernate: 
          update
              JAP_OEDERS 
          set
              CUSTOMER_ID=?,
              ORDER_NAME=? 
          where
              id=?
      Hibernate: 
          update
              JAP_OEDERS 
          set
              CUSTOMER_ID=?,
              ORDER_NAME=? 
          where
              id=?
      Hibernate: 
          update
              JAP_OEDERS 
          set
              CUSTOMER_ID=? 
          where
              id=?
      Hibernate: 
          update
              JAP_OEDERS 
          set
              CUSTOMER_ID=? 
          where
              id=?
      
      • 使用mappedBy属性可指定由多的一方维护关联关系

        • 代码

          注意若在1的一端,@OneToMany使用mapperBy属性,则不能使用@JoinColumn
          @OneToMany(fetch=FetchType.EAGER,cascade= {CascadeType.REMOVE},mappedBy="customer")//mappedBy="customer",customer来维护
          
          • 结果

            Hibernate: 
                insert 
                into
                    JAP_OEDERS
                    (CUSTOMER_ID, ORDER_NAME) 
                values
                    (?, ?)
            Hibernate: 
                insert 
                into
                    JAP_OEDERS
                    (CUSTOMER_ID, ORDER_NAME) 
                values
                    (?, ?)
            Hibernate: 
                insert 
                into
                    JPA_CUSTOMERS
                    (age, birth, createdTime, email, last_name) 
                values
                    (?, ?, ?, ?, ?)
            Hibernate: 
                update
                    JAP_OEDERS 
                set
                    CUSTOMER_ID=?,
                    ORDER_NAME=? 
                where
                    id=?
            Hibernate: 
                update
                    JAP_OEDERS 
                set
                    CUSTOMER_ID=?,
                    ORDER_NAME=? 
                where
                    id=?
            

6.4:映射双向一对一的关联关系

  • 新建两个类:Manager,Department

    package com.atguigu.jpa.helloworld;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.JoinColumn;
    import javax.persistence.OneToOne;
    import javax.persistence.Table;
    
    @Table(name="JPA_DEPARMENTS")
    @Entity
    public class Department {
    	
    	private Integer id;
    	private String deptName;
    	private Manager mgr;
    	
    	@GeneratedValue
    	@Id
    	public Integer getId() {
    		return id;
    	}
    	public void setId(Integer id) {
    		this.id = id;
    	}
    	@Column(name="DEPT_NAME")
    	public String getDeptName() {
    		return deptName;
    	}
    	public void setDeptName(String deptName) {
    		this.deptName = deptName;
    	}
    	
    	//使用@OneToOne映射1-1,若需要在当前数据表中添加主键,需要使用@JoinColumn来映射,
    	//注意1-1关联关系,所以需要添加unique
    	@JoinColumn(name="MGR_ID",unique=true)
    	@OneToOne()
    	public Manager getMgr() {
    		return mgr;
    	}
    	public void setMgr(Manager mgr) {
    		this.mgr = mgr;
    	}
    
    }
    
    
    package com.atguigu.jpa.helloworld;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.OneToOne;
    import javax.persistence.Table;
    
    @Table(name="JPA_MANAGERS")
    @Entity
    public class Manager {
    	
    	private Integer id;
    	private String mgrName;
    	private Department dept;
    	@GeneratedValue
    	@Id
    	public Integer getId() {
    		return id;
    	}
    	public void setId(Integer id) {
    		this.id = id;
    	}
    	@Column(name="MGR_NAME")
    	public String getMgrName() {
    		return mgrName;
    	}
    	public void setMgrName(String mgrName) {
    		this.mgrName = mgrName;
    	}
    	
    	//对于不维护联系关系,没有外键的一方,使用@OneToOne,建议mappedBy=true
    	@OneToOne(mappedBy="mgr")
    	public Department getDept() {
    		return dept;
    	}
    	public void setDept(Department dept) {
    		this.dept = dept;
    	}
    	
    }
    
    
    <class>com.atguigu.jpa.helloworld.Department</class>
    <class>com.atguigu.jpa.helloworld.Manager</class>
    
  • 测试保存

    	//双向1-1的关联关系,建议先保存不维护关联关系的一方,既没有外键的。不多出update
    	@Test
    	public void testOneToOne() {
    		Manager mgr=new Manager();
    		mgr.setMgrName("M-BB");
    		
    		Department dept=new Department();
    		dept.setDeptName("D-BB");
    		
    		//设置关联关系
    		mgr.setDept(dept);
    		dept.setMgr(mgr);
    		
    		//执行保存操作
    		entityManager.persist(mgr);
    		entityManager.persist(dept);
    	}
    
    • 结果

      Hibernate: 
          insert 
          into
              JPA_MANAGERS
              (MGR_NAME) 
          values
              (?)
      Hibernate: 
          insert 
          into
              JPA_DEPARMENTS
              (DEPT_NAME, MGR_ID) 
          values
              (?, ?)
      
  • 测试find

    	//若获取维护关联关系的一方,则会通过左外连接获取关联对象
    	//但可以通过@OneToOne的fetch属性来修改加载策略
    	@Test
    	public void testOneToOneFind() {
    		Department dept=entityManager.find(Department.class,1);
    		System.out.println(dept.getDeptName());
    		System.out.println(dept.getMgr().getClass().getName());
    	}
    
    @OneToOne(fetch=FetchType.LAZY)
    
    • 结果

      Hibernate: 
          select
              department0_.id as id1_4_1_,
              department0_.DEPT_NAME as DEPT2_4_1_,
              department0_.MGR_ID as MGR3_4_1_,
              manager1_.id as id1_6_0_,
              manager1_.MGR_NAME as MGR2_6_0_ 
          from
              JPA_DEPARMENTS department0_ 
          left outer join
              JPA_MANAGERS manager1_ 
                  on department0_.MGR_ID=manager1_.id 
          where
              department0_.id=?
      Hibernate: 
          select
              department0_.id as id1_4_1_,
              department0_.DEPT_NAME as DEPT2_4_1_,
              department0_.MGR_ID as MGR3_4_1_,
              manager1_.id as id1_6_0_,
              manager1_.MGR_NAME as MGR2_6_0_ 
          from
              JPA_DEPARMENTS department0_ 
          left outer join
              JPA_MANAGERS manager1_ 
                  on department0_.MGR_ID=manager1_.id 
          where
              department0_.MGR_ID=?
      D-AA
      com.atguigu.jpa.helloworld.Manager
      
  • 测试find2

    	//若获取不维护关联关系的一方,则会通过左外连接获取关联对象
    	//但可以通过@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());
    	}
    
    • 结果

      Hibernate: 
          select
              manager0_.id as id1_6_1_,
              manager0_.MGR_NAME as MGR2_6_1_,
              department1_.id as id1_4_0_,
              department1_.DEPT_NAME as DEPT2_4_0_,
              department1_.MGR_ID as MGR3_4_0_ 
          from
              JPA_MANAGERS manager0_ 
          left outer join
              JPA_DEPARMENTS department1_ 
                  on manager0_.id=department1_.MGR_ID 
          where
              manager0_.id=?
      M-AA
      com.atguigu.jpa.helloworld.Department
      

6.5:映射双向多对多的关联关系

  • 创建两个实体类

    package com.atguigu.jpa.helloworld;
    
    import java.util.HashSet;
    import java.util.Set;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.ManyToMany;
    import javax.persistence.Table;
    
    @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;
    	}
    }
    
    
    package com.atguigu.jpa.helloworld;
    
    import java.util.HashSet;
    import java.util.Set;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.JoinTable;
    import javax.persistence.ManyToMany;
    import javax.persistence.Table;
    import javax.persistence.JoinColumn;
    @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;
    	}
    
    	public String getitemName() {
    		return itemName;
    	}
    	
    	@Column(name="ITEM_NAME")
    	public void setitemName(String itemName) {
    		this.itemName = itemName;
    	}
    	
    	//使用@ManyToMany注解来映射多对多的关联关系
    	//使用@JoinTable来映射中间表
    	//1.name指向中间表
    	//2.joinColumns映射当前类所在中间表的外键
    	//2.1name指定外键列的列名
    	//2.2referencedColumnName指定外键列关联当前表的那一列
    	//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;
    	}
    	
    }
    
    
    <class>com.atguigu.jpa.helloworld.Item</class>
    <class>com.atguigu.jpa.helloworld.Category</class>
    
    • 测试保存
    //多对多的保存
    	@Test
    	public void testManyToMany() {
    		
    		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);
    	}
    
    • 结果

      Hibernate: 
          insert 
          into
              JPA_ITEMS
              (itemName) 
          values
              (?)
      Hibernate: 
          insert 
          into
              JPA_ITEMS
              (itemName) 
          values
              (?)
      Hibernate: 
          insert 
          into
              JPA_CATEGORIES
              (CATEGORY_NAME) 
          values
              (?)
      Hibernate: 
          insert 
          into
              JPA_CATEGORIES
              (CATEGORY_NAME) 
          values
              (?)
      Hibernate: 
          insert 
          into
              ITEM_CATEGORY
              (ITEM_ID, CATEGORY_ID) 
          values
              (?, ?)
      Hibernate: 
          insert 
          into
              ITEM_CATEGORY
              (ITEM_ID, CATEGORY_ID) 
          values
              (?, ?)
      Hibernate: 
          insert 
          into
              ITEM_CATEGORY
              (ITEM_ID, CATEGORY_ID) 
          values
              (?, ?)
      Hibernate: 
          insert 
          into
              ITEM_CATEGORY
              (ITEM_ID, CATEGORY_ID) 
          values
              (?, ?)
      
    • 测试find

      	//对于关联的集合对象,默认使用懒加载的策略
      	//使用维护关联关系的一方获取,还是使用不维护关联关系的一方获取,sql语句相同
      	@Test
      	public void testManyToManyFind() {
      //		Item item=entityManager.find(Item.class,1);
      //		System.out.println(item.getitemName());
      //		
      //		System.out.println(item.getCategories().size());
      		
      		Category category=entityManager.find(Category.class,1);
      		
      		System.out.println(category.getCategoryName());
      		System.out.println(category.getItems().size());
      	}
      
      • 结果

        Hibernate: 
            select
                category0_.id as id1_2_0_,
                category0_.CATEGORY_NAME as CATEGORY2_2_0_ 
            from
                JPA_CATEGORIES category0_ 
            where
                category0_.id=?
        c-1
        Hibernate: 
            select
                items0_.CATEGORY_ID as CATEGORY2_2_1_,
                items0_.ITEM_ID as ITEM1_0_1_,
                item1_.id as id1_5_0_,
                item1_.itemName as itemName2_5_0_ 
            from
                ITEM_CATEGORY items0_ 
            inner join
                JPA_ITEMS item1_ 
                    on items0_.ITEM_ID=item1_.id 
            where
                items0_.CATEGORY_ID=?
        1
        

7:二级缓存

  • 一级缓存测试

    • Customer的fetch=FetchType.LAZY
    @Test
    	public void testSecondLevelCache() {
    		Customer customer1=entityManager.find(Customer.class, 1);
            
    		Customer customer2=entityManager.find(Customer.class, 1);
    	}
    
    • 结果

      Hibernate: 
          select
              customer0_.id as id1_3_0_,
              customer0_.age as age2_3_0_,
              customer0_.birth as birth3_3_0_,
              customer0_.createdTime as createdT4_3_0_,
              customer0_.email as email5_3_0_,
              customer0_.last_name as last6_3_0_ 
          from
              JPA_CUSTOMERS customer0_ 
          where
              customer0_.id=?
      
  • 二级缓存测试-1(关掉EntityManager然后开启,一级缓存清除,所以两条sql语句)

    • 	@Test
      	public void testSecondLevelCache() {
      		Customer customer1=entityManager.find(Customer.class, 1);
      		
      		transaction.commit();
      		entityManager.close();
      		
      		entityManager=entityManagerFactory.createEntityManager();
      		transaction =entityManager.getTransaction();
      		transaction.begin();
      		
      		Customer customer2=entityManager.find(Customer.class, 1);
      	}
      
      • 结果

        Hibernate: 
            select
                customer0_.id as id1_3_0_,
                customer0_.age as age2_3_0_,
                customer0_.birth as birth3_3_0_,
                customer0_.createdTime as createdT4_3_0_,
                customer0_.email as email5_3_0_,
                customer0_.last_name as last6_3_0_ 
            from
                JPA_CUSTOMERS customer0_ 
            where
                customer0_.id=?
        Hibernate: 
            select
                customer0_.id as id1_3_0_,
                customer0_.age as age2_3_0_,
                customer0_.birth as birth3_3_0_,
                customer0_.createdTime as createdT4_3_0_,
                customer0_.email as email5_3_0_,
                customer0_.last_name as last6_3_0_ 
            from
                JPA_CUSTOMERS customer0_ 
            where
                customer0_.id=?
        
  • 二级缓存测试-2(二级缓存可跨EntityManager)

    • 加入对应配置到persistence.xml

      		<!-- 配置二级缓存的策略
      		ALL:所有实体类将被缓存
      		NONE:所有的实体类都不被缓存
      		ENABLE_SELECTIVE:标识@Cacheable(true)注解的实体类将被缓存
      		DISABLE_SELECTIVE:缓存除标识@Cacheable(false)以外的所有实体类
      		UNSPECIFIED:默认值,JPA产品默认值将被使用
      		-->
      		<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
      
      <!-- 二级缓存相关 -->
      			<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"/>
      
    • 加入jar包,jar包在2.2中已经包含(ehcache-core-2.4.3.jar;hibernate-ehcache-4.2.4.Final.jar;slf4j-api-1.6.1.jar)

    • 在src下加入ehcache的配置文件

      <?xml version="1.0" encoding="UTF-8"?>
      <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
               updateCheck="false">
          <!--
             diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
             user.home – 用户主目录
             user.dir  – 用户当前工作目录
             java.io.tmpdir – 默认临时文件路径
           -->
          <diskStore path="java.io.tmpdir/Tmp_EhCache"/>
          <!--
             defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
           -->
          <!--
            name:缓存名称。
            maxElementsInMemory:缓存最大数目
            maxElementsOnDisk:硬盘最大缓存个数。
            eternal:对象是否永久有效,一但设置了,timeout将不起作用。
            overflowToDisk:是否保存到磁盘,当系统当机时
            timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
            timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
            diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
            diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
            diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
            memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
            clearOnFlush:内存数量最大时是否清除。
            memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
            FIFO,first in first out,这个是大家最熟的,先进先出。
            LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
            LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
         -->
          <defaultCache
                  eternal="false"
                  maxElementsInMemory="10000"
                  overflowToDisk="false"
                  diskPersistent="false"
                  timeToIdleSeconds="1800"
                  timeToLiveSeconds="259200"
                  memoryStoreEvictionPolicy="LRU"/>
       
          <cache
                  name="cloud_user"
                  eternal="false"
                  maxElementsInMemory="5000"
                  overflowToDisk="false"
                  diskPersistent="false"
                  timeToIdleSeconds="1800"
                  timeToLiveSeconds="1800"
                  memoryStoreEvictionPolicy="LRU"/>
       
      </ehcache>
      
    • 在Customer上加入注解

      @Cacheable(true)
      @Table(name="JPA_CUSTOMERS")
      @Entity
      public class Customer {
      
    • 测试代码

      	@Test
      	public void testSecondLevelCache() {
      		Customer customer1=entityManager.find(Customer.class, 1);
      		
      		transaction.commit();
      		entityManager.close();
      		
      		entityManager=entityManagerFactory.createEntityManager();
      		transaction =entityManager.getTransaction();
      		transaction.begin();
      		
      		Customer customer2=entityManager.find(Customer.class, 1);
      	}
      
      • 结果(二级缓存成功)

        Hibernate: 
            select
                customer0_.id as id1_3_0_,
                customer0_.age as age2_3_0_,
                customer0_.birth as birth3_3_0_,
                customer0_.createdTime as createdT4_3_0_,
                customer0_.email as email5_3_0_,
                customer0_.last_name as last6_3_0_ 
            from
                JPA_CUSTOMERS customer0_ 
            where
                customer0_.id=?
        

8:JPQL

8.1:JPQL语言

  • JPQL语言,即Java Persistence Query Language的简称。JPQL是一种和SQL非常类似的中间性和对象化查询语言,它最终会被编译成针对不同底层数据库的SQL查询,从而屏蔽不同数据库的差异。
  • JPQL语言的语句可以是select语句、update 语句或delete语句,它们都通过Query接口封装执行

8.2:Query接口的主要方法

Query接口封装了执行数据库查询的相关方法。调用EntityManager的createQuery、createNamedQuery及createNativeQuery方法可以获得查询对象,进而可调用Query接口的相关方法来执行查询操作

  • int executeUpdate():用于执行update或delete语句
  • List getResultList():用于执行select语句并返回结果集实体列表。
  • Object getSingleResult():用于执行只返回单个结果实体的select语句
  • Query setFirstResult(int startPosition):用于设置从哪个实体记录开始返回查询结果。
  • Query setMaxResults(int maxResult):用于设置返回结果实体的最大数。与setFirstResult结合使用可实现分页查询
  • Query setFlushMode(FlushModeType flushMode):设置查询对象的Flush模式。 参数可以取2个枚举值: FlushModeType.AUTO为自动更新数据库记录, FlushMode. Type.COMMIT为直到提交事务时才更新数据库记录
  • setHint(String hintName, Object value):设置与查询对象相关的特定供应商参数或提示信息。参数名及其取值
    需要参考特定JPA实现库提供商的文档。如果第二个参 数无效将抛出IllegalArgumentException异常
  • setParameter(int position, Object value)
    为查询语句的指定位置参数赋值。Position 指定参数序号, value 为赋给参数的值。
  • setParameter(int position, Date d, TemporalType type):为查询语句的指定位置参数赋Date值。Position 指定参数序号,value为赋给参数的值, temporalType取TemporalType的枚举常量,包括DATE、TIME及TIMESTAMP三个.用于将Java的Date型值临时转换为数据库支持的日期时间类型( java.sql.Date;java.sql.Time及java.sql.Timestamp )

8.3:Helloworld

  • 获取对象

    	@Test
    	public void testHelloJPQL() {
    		String jpql="FROM Customer c WHERE c.age > ?";
    		Query query =entityManager.createQuery(jpql);
    		
    		//占位符的索引是从1开始
    		query.setParameter(1, 1);
    		List<Customer> customers=query.getResultList();
    		System.out.println(customers.size());
    	}
    
    • 结果

      Hibernate: 
          select
              customer0_.id as id1_3_,
              customer0_.age as age2_3_,
              customer0_.birth as birth3_3_,
              customer0_.createdTime as createdT4_3_,
              customer0_.email as email5_3_,
              customer0_.last_name as last6_3_ 
          from
              JPA_CUSTOMERS customer0_ 
          where
              customer0_.age>?
      16
      
  • 获取部分属性

    	//默认情况下,若只查部分属性,则将返回Object[]类型的结果,或者Object[]类型的List
    	//也可以在实体类中创建对应的构造器,然后再JPQL语句中利用对应的构造器返回实体类的对象。
    	@Test
    	public void testPartlyProperties() {
            //返回Object[]类型的结果,或者Object[]类型的List
            //String jpql="SELECT c.lastName,c.age FROM Customer c WHERE c.id>?";
            
            //利用对应的构造器返回实体类的对象
    		String jpql="SELECT new Customer(c.lastName,c.age) FROM Customer c WHERE c.id>?";
    		List result =entityManager.createQuery(jpql).setParameter(1, 1).getResultList();
    		System.out.println(result);
    	}
    
    • 构造器代码

      • 	public Customer() {
        		super();
        	}
        
        
        	public Customer(String lastName, int age) {
        		super();
        		this.lastName = lastName;
        		this.age = age;
        	}
        
    • 结果

      Hibernate: 
          select
              customer0_.last_name as col_0_0_,
              customer0_.age as col_1_0_ 
          from
              JPA_CUSTOMERS customer0_ 
          where
              customer0_.id>?
      [Customer [id=null, lastName=DD, email=null, age=18, createdTime=null, birth=null], Customer [id=null, lastName=BB, email=null, age=15, createdTime=null, birth=null], Customer [id=null, lastName=cc, email=null, age=18, createdTime=null, birth=null], Customer [id=null, lastName=cc, email=null, age=18, createdTime=null, birth=null], Customer [id=null, lastName=dd, email=null, age=18, createdTime=null, birth=null]
      

8.4:Query

8.4.1:createNamedQuery

  • Customer对应注解

    @NamedQuery(name="testNamedQuery",query="FROM Customer c WHERE c.id=?")
    @Cacheable(true)
    @Table(name="JPA_CUSTOMERS")
    @Entity
    public class Customer {
    
  • 测试代码

    	//createNamedQuery适用于实体类前使用@NamedQuery标记的查询语言
    	@Test
    	public void testNamedQuery() {
    		Query query =entityManager.createNamedQuery("testNamedQuery").setParameter(1, 1);
    		Customer customer=(Customer) query.getSingleResult();
    		System.out.println(customer);
    	}
    
    • 结果

      Hibernate: 
          select
              customer0_.id as id1_3_,
              customer0_.age as age2_3_,
              customer0_.birth as birth3_3_,
              customer0_.createdTime as createdT4_3_,
              customer0_.email as email5_3_,
              customer0_.last_name as last6_3_ 
          from
              JPA_CUSTOMERS customer0_ 
          where
              customer0_.id=?
      Customer [id=1, lastName=AA, email=aa@163.com, age=13, createdTime=2019-11-15 00:42:18.0, birth=2019-11-13]
      

8.4.2:createNativeQuery

  • 测试代码

    	//createNativeQuery适用于本地SQL
    	@Test
    	public void testNativeQuery() {
    		String sql="SELECT age FROM jpa_customers WHERE id=?";
    		Query query =entityManager.createNativeQuery(sql).setParameter(1, 1);
    		
    		Object result=query.getSingleResult();
    		System.out.println(result);
    	}
    
  • 结果

    Hibernate: 
        SELECT
            age 
        FROM
            jpa_customers 
        WHERE
            id=?
    13
    

9:Hibernate的查询缓存

  • 确认配置了查询缓存

    <property name="hibernate.cache.use_query_cache" value="true"/>
    
  • 测试代码(.setHint(QueryHints.HINT_CACHEABLE, true);

    //使用hibernate的查询缓存
    	@Test
    	public void testQueryCache() {
    		String jpql="FROM Customer c WHERE c.age > ?";
    		Query query =entityManager.createQuery(jpql).setHint(QueryHints.HINT_CACHEABLE, true);
    		
    		//占位符的索引是从1开始
    		query.setParameter(1, 1);
    		List<Customer> customers=query.getResultList();
    		System.out.println(customers.size());
    		
    		query=entityManager.createQuery(jpql).setHint(QueryHints.HINT_CACHEABLE, true);
    		
    		query.setParameter(1, 1);
    		customers=query.getResultList();
    		System.out.println(customers.size());
    	}
    
    • 结果

      Hibernate: 
          select
              customer0_.id as id1_3_,
              customer0_.age as age2_3_,
              customer0_.birth as birth3_3_,
              customer0_.createdTime as createdT4_3_,
              customer0_.email as email5_3_,
              customer0_.last_name as last6_3_ 
          from
              JPA_CUSTOMERS customer0_ 
          where
              customer0_.age>?
      16
      16
      

10:Oder By和Group By

Oder By

  • 代码

    	@Test
    	public void testQueryCache() {
    		String jpql="FROM Customer c WHERE c.age > ? ORDER BY c.age DESC";
    		Query query =entityManager.createQuery(jpql).setHint(QueryHints.HINT_CACHEABLE, true);
    		
    		//占位符的索引是从1开始
    		query.setParameter(1, 1);
    		List<Customer> customers=query.getResultList();
    		System.out.println(customers.size());
    	}
    
    • 结果

      Hibernate: 
          select
              customer0_.id as id1_3_,
              customer0_.age as age2_3_,
              customer0_.birth as birth3_3_,
              customer0_.createdTime as createdT4_3_,
              customer0_.email as email5_3_,
              customer0_.last_name as last6_3_ 
          from
              JPA_CUSTOMERS customer0_ 
          where
              customer0_.age>? 
          order by
              customer0_.age DESC
      16
      
  • Group By

    • 测试代码

      	//查询order数量大于2的那些customer
      	@Test
      	public void testGroupBy() {
      		String jpql="SELECT o.customer FROM Order o Group BY o.customer HAVING count(o.id)>=2";
      		List<Customer> customers=entityManager.createQuery(jpql).getResultList();
      		System.out.println(customers);
      	}
      
      • 结果

        Hibernate: 
            select
                customer1_.id as id1_3_,
                customer1_.age as age2_3_,
                customer1_.birth as birth3_3_,
                customer1_.createdTime as createdT4_3_,
                customer1_.email as email5_3_,
                customer1_.last_name as last6_3_ 
            from
                JAP_OEDERS order0_ 
            inner join
                JPA_CUSTOMERS customer1_ 
                    on order0_.CUSTOMER_ID=customer1_.id 
            group by
                order0_.CUSTOMER_ID 
            having
                count(order0_.id)>=2
        [Customer [id=15, lastName=yy, email=yy@163.com, age=18, createdTime=2019-11-25 21:14:53.0, birth=2019-11-25], Customer [id=20, lastName=ZZ, email=zz@163.com, age=18, createdTime=2020-06-13 22:05:01.0, birth=2020-06-13], Customer [id=21, lastName=ZZ, email=zz@163.com, age=18, createdTime=2020-06-13 22:06:33.0, birth=2020-06-13], Customer [id=23, lastName=GG, email=gg@163.com, age=18, createdTime=2020-06-14 13:49:52.0, birth=2020-06-14], Customer [id=24, lastName=GG, email=gg@163.com, age=18, createdTime=2020-06-14 13:56:24.0, birth=2020-06-14]]
        

11:关联查询

  • 在JPQL中,很多时候都是通过在实体类中配置实体关联的类属性来实现隐含的关联(join)查询。例如:select o from Orders o where o.address. streetNumber= 2000

  • 上述JPQL语句编译成以下SQL时就会自动包含关联,默认为左关联。

  • 在某些情况下可能仍然需要对关联做精确的控制。为此,JPQL也支持和SQL中类似的关联语法。如:

    • left out join / left join
    • inner join G
    • left join / inner join fetch
    • 其中,left,join和left out join等义,都是允许符合条件的右边表达式中的实体为空。
  • 测试代码

    	//建议加上fetch,这样得到一个对象
    	@Test
    	public void testLeftOuterJoinFetch() {
    		String jpql="FROM Customer c LEFT OUTER JOIN FETCH c.orders WHERE c.id=?";
    		Customer customers=(Customer)entityManager.createQuery(jpql).setParameter(1, 15).getSingleResult();
    		System.out.println(customers.getLastName());
    		System.out.println(customers.getOrders().size());
    	}
    
    • 结果

      Hibernate: 
          select
              customer0_.id as id1_3_0_,
              orders1_.id as id1_1_1_,
              customer0_.age as age2_3_0_,
              customer0_.birth as birth3_3_0_,
              customer0_.createdTime as createdT4_3_0_,
              customer0_.email as email5_3_0_,
              customer0_.last_name as last6_3_0_,
              orders1_.CUSTOMER_ID as CUSTOMER3_1_1_,
              orders1_.ORDER_NAME as ORDER2_1_1_,
              orders1_.CUSTOMER_ID as CUSTOMER3_3_0__,
              orders1_.id as id1_1_0__ 
          from
              JPA_CUSTOMERS customer0_ 
          left outer join
              JAP_OEDERS orders1_ 
                  on customer0_.id=orders1_.CUSTOMER_ID 
          where
              customer0_.id=?
      yy
      3
      

12:子查询

  • JPQL也支持子查询,在 where 或 having 子句中可以包含另一个查询。当子查询返回多于1个结
    果集时,它常出现在any、all exist s表达式中用于集合匹配查询。它们的用法与SQL语句基本
    相同。

  • 测试代码

    	@Test
    	public void testSubQuery() {
    		//查询所有Customer的lastName为yy的order
    		String jpql="SELECT o FROM Order o "
    				+ "WHERE o.customer = (SELECT c FROM Customer c WHERE c.lastName = ?)";
    		Query query=entityManager.createQuery(jpql).setParameter(1, "yy");
    		List<Order> orders=query.getResultList();
    		System.out.println(orders.size());
    	}
    
    • 结果

      Hibernate: 
          select
              order0_.id as id1_1_,
              order0_.CUSTOMER_ID as CUSTOMER3_1_,
              order0_.ORDER_NAME as ORDER2_1_ 
          from
              JAP_OEDERS order0_ 
          where
              order0_.CUSTOMER_ID=(
                  select
                      customer1_.id 
                  from
                      JPA_CUSTOMERS customer1_ 
                  where
                      customer1_.last_name=?
              )
      Hibernate: 
          select
              customer0_.id as id1_3_0_,
              customer0_.age as age2_3_0_,
              customer0_.birth as birth3_3_0_,
              customer0_.createdTime as createdT4_3_0_,
              customer0_.email as email5_3_0_,
              customer0_.last_name as last6_3_0_ 
          from
              JPA_CUSTOMERS customer0_ 
          where
              customer0_.id=?
      3
      

13:JPQL函数

  • JPQL提供了以下一些内建函数,包括字符串处理函数、算术函数和
    日期函数

  • 字符串处理函数主要有:

    • concat(String s1, String s2) :字符串合并/连接函数

    • substring(String s, int start, int length) :取字串函数

    • trim([leading|trailing|both,] [char c,] String s) :从字符串中去掉首/尾指定的字符或空格

    • lower(String s) :将字符串转换成小写形式

    • upper(String s) :将字符串转换成大写形式

    • length(String s) :求字符串的长度

    • locate(String s1, String s2[, int start]) :从第一个字符串中查找第二个字符串(子审)出现的位置。若未找到则返回0

    • 算术函数主要有abs、 mod、sqrt、 size 等。Size用于求集合的元素个数

    • 日期函数主要为三个,即current_date、current_time、 current _timestamp ,它们不需要参数,返回服务器上的当前日期、时间和时戳

  • 测试代码

    	//使用jpql内建函数
    	@Test
    	public void testJpqlFunction() {
    		String jpql="SELECT upper(c.email) FROM Customer c";
    		
    		List<String> emails=entityManager.createQuery(jpql).getResultList();
    		System.out.println(emails);
    	}
    
    • 结果

      Hibernate: 
          select
              upper(customer0_.email) as col_0_0_ 
          from
              JPA_CUSTOMERS customer0_
      [AA@163.COM, EE@163.COM, BB@163.COM, CC@163.COM, CC@163.COM, DD@163.COM, BB@163.COM, YY@163.COM, YY@163.COM, BB@163.COM, CC@163.COM, DD@163.COM, ZZ@163.COM, ZZ@163.COM, GG@163.COM, GG@163.COM]
      

14:update和delete

  • 测试代码

    	//可以使用jpql完成update和delete操作
    	@Test
    	public void testExcuteUpdate() {
    		String jpql="UPDATE Customer c SET c.lastName=? WHERE c.id=?";
    		
    		Query query=entityManager.createQuery(jpql).setParameter(1, "AA").setParameter(2, 11);
    		query.executeUpdate();
    	}
    
    • 结果

      Hibernate: 
          update
              JPA_CUSTOMERS 
          set
              last_name=? 
          where
              id=?
      

15:spring整合JPA

三种整合方式:

  • LocalEntityManagerFactoryBean :适用于那些仅使用JPA进行数据访问的项目,该FactoryBean将根据JPAPersistenceProvider自动检测配置文件进行工作,一般从"META-INF/persistence.xml"读取配置信息,这种方式最简单,但不能设置Spring中定义的DataSource,且不支持Spring管理的全局事务
  • 从JNDI中获取:用于从Java EE服务器获取指定的EntityManagerFactory ,这种方式在进行Spring事务管理时一般要使用JTA事务管理
  • LocalContainerEntityManagerFactoryBean :适用于所有环境的FactoryBean ,能全面EntityManagerFactory配置,如指定Spring定义的DataSource等等。(本次使用此种方法整合)
    • 加入jar包
      在这里插入图片描述
  • 配置数据库文件
    在这里插入图片描述
  • 配置C3P0数据源和datasource
    在这里插入图片描述
  • 配置其他属性
    在这里插入图片描述
  • 配置自动扫描的包
    在这里插入图片描述
  • 配置EntityManagerFactory
    在这里插入图片描述
  • 配置JPA使用的事物管理器
    在这里插入图片描述
  • 通过@PersistenceContext注解标记成员变量
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值