JPA

          今天做东西的时候使用到了JPA,在这里把JPA需要注意的知道的体系结构,给大家罗列一遍。如果能帮到大家一点,希望大家,点个赞关注一下,后期还会更新更多技术。

一、JPA概述 

1.使用JPA持久化对象的步骤

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

2. 开发JPA依赖的jar文件

  • hibernate-release-4.2.4.Final\lib\required\*.jar
  • hibernate-release-4.2.4.Final\lib\jpa\*.jar
  • 数据库驱动的 jar

JPA_第1张图片

3.persistence.xml

  • JPA 规范要求在类路径 META-INF 目录下放置persistence.xml文件的名称是固定的

JPA_第2张图片

 

4. 执行持久化操作

JPA_第3张图片

 

5. JPA 基本注解

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

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

         

  • @Transient:  

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

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

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

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

     JPA_第4张图片

  • @GeneratedValue:@GeneratedValue  用于标注主生成策略通过 strategy 属性指定默认情况下,JPA 自动选择一个最适合底层数据库的主键生成策略SqlServer 对应 identityMySQL 对应 auto increment

    • javax.persistence.GenerationType 定义了以下几种可供选择的策略

      • IDENTITY:采用数据库 ID自增长的方式来自增主键字段,Oracle 支持这种方式

      • AUTO JPA自动选择合适的策略,是默认选项

      • SEQUENCE:通过序列产生主键,通过 @SequenceGenerator 注解指定序列名,MySql 支持这种方式

      • TABLE:通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植

  • @Column:

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

    • @Column 标注常用属性 name,用于设置映射数据库表的列名。此外,该标注还包含其它多个属性unique nullablelength 等。

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

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

  • @Basic:

    • @Basic 表示一个简单的属性到数据库表的字段的映射,对于没有任何标注 getXxxx() 方法,默认即为@Basic

      • fetch: 表示该属性的读取策略, EAGER LAZY 两种,分别表示主支抓取和延迟加载,默认 EAGER.

        • FetchType.LAZY:懒加载,加载一个实体时,定义懒加载的属性不会马上从数据库中加载。

        • FetchType.EAGER:急加载,加载一个实体时,定义急加载的属性会立即从数据库中加载。

    • optional:表示该属性是否允许为null, 默认true

 6. table 生成主键详解

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

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

JPA_第5张图片

 

示例:

JPA_第6张图片

 

JPA_第7张图片  JPA_第8张图片

 

二、JPA API

  1. Persistence

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

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

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

    • 带有两个参数的方法:前一个参数含义相同,后一个参数 Map类型用于设置 JPA 的相关属性,这时将忽略其它地方设置的属性。Map 对象的属性名必须是 JPA 实现库提供商的名字空间约定的属性名。

2. EntityManagerFactory

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

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

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

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

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

3.EntityManager

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

  • 实体的状态:

    • 新建状态:   新创建的对象,尚未拥有持久性主键

    • 持久化状态:已经拥有持久性主键并和持久化建立了上下文环境

    • 游离状态拥有持久化主键,但是没有与持久化建立上下文环

    • 删除状态:  拥有持久化主键,已经和持久化建立上下文环境,但是数据库中删除

  • 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)删除实例。如果实例是被管理的,即与数据库实体记录关联,则同时会删除关联的数据库记录

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

JPA_第9张图片

 

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

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

    • FlushModeType.AUTO 自动更新数据库实体

    • FlushModeType.COMMIT 直到提交事务时才更新数据库记录。

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

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

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

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

  • isOpen ()判断当前的实体管理器是否是打开状态。

  • getTransaction ()返回资源层的事务对象。EntityTransaction实例可以用于开始和提交多个事务

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

  • createQuery (String qlString)创建一个查询对象

  • createNamedQuery (String name)根据命名的查询语句块创建查询对象。参数为命名的查询语句。

  • createNativeQuery (String sqlString)使用标准 SQL语句创建查询对象。参数为标准SQL语句字符串

  • createNativeQuery (String sqls, String resultSetMapping)使用标准SQL语句创建查询对象,并指定返回结果 Map的 名称

 

3. EntityTransaction

  • begin ()

    • 用于启动一个事务,此后的多个数据库操作将作为整体被提交或撤消。若这时事务已启动则会抛 IllegalStateException 异常

  • commit ()

    • 用于提交当前事务。即将事务启动以后的所有数据库更新操作持久化至数据库中。

  • rollback ()

    • 撤消(回滚)当前事务。即撤消事务启动后的所有数据库更新操作,从而不对数据库产生影响。

  • setRollbackOnly ()

    • 使当前事务只能被撤消。

  • getRollbackOnly ()

    • 查看当前事务是否设置了只能撤消标志。

  • isActive ()

    • 查看当前事务是否是活动的。如果返回true则不能调用begin方法,否则将抛 IllegalStateException 异常;如果返回 false 不能调用 commitrollbacksetRollbackOnly getRollbackOnly 方法,否则将抛 IllegalStateException 异常。

 

三、关联映射

1. 双向一对多及多对一映射

  • 双向一对多关系中必须在一个关系维护端, JPA 规范中,要求  many 一方作为关系的维护(owner side), one 一方作为被维护端(inverse side)

  • 可以在 one 方指定 @OneToMany 注释设置 mappedBy 属性,以指定它是这一关联中的被维护端,many 维护端

  • many 方指定 @ManyToOne 注释,并使用 @JoinColumn 指定外键名

JPA_第10张图片

2. 双向一对一映射

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

                    JPA_第11张图片

3. 双向 1-1 不延迟加载的问题

  • 如果延迟加载要起作用, 必须设置一个代理对象.

  • Manager 其实可以不关联一 Department

  • 如果有 Department 关联就设置为代理对象而延迟加载, 如果存在关联的 Department 就设置 null, 因为外键字段是定义 Department 中的,Hibernate 读取 Department 的情况是无法判断是否关联有 Deparmtment, 因此无法判断设置 null 还是代理对象, 而统一设置为代理对象,也无法满足不关联的情况, 所以无法使用延迟加载,只 有显式读取 Department.

JPA_第12张图片

 

4. 双向多对多关联关系

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

JPA_第13张图片

JPA_第14张图片JPA_第15张图片

以上是JPA在做Demo 之前需要知道的知识体系。以下使用Demo实例的方式,介绍一下具体的使用方式。项目的目录结构如下:

JPA_第16张图片  JPA_第17张图片

 

一对多、多对一关联映射Demo:

1.在Entity包下创建Customer.java和Order.java实体类,代码如下:

  • Customer.jara:
package com.nyist.Jpa.Entity;

import java.util.Date;
import java.util.HashSet;
import java.util.Set;

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.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

import org.hibernate.engine.internal.Cascade;

@Table(name="JPA_CUSTOMER")															//需要生成的数据表名称
@Entity																				//表明这是一个持久化类 对象
public class Customer {
	@Id	
	@Column(name="jpa_id",nullable=false,unique=true,length=50)						//映射得 列名												//Id  主键
//	strategy 主键的生成策略改变成功为 用table表的方式生成主键
//	generator  指定生成器
//	@GeneratedValue(strategy=GenerationType.TABLE,generator="ID_GENERATOR")			//生成值得策略
//	name ID_GENERATOR 表生成策略的名称  table 生成主键策略的表	pkColumnName	生成主键的列		pkColumnValue	生成值
//	allocationSize	增长幅度 为 1 	initalValue	初始化值为1
//	@TableGenerator(name="ID_GENERATOR",
//		table="JPA_GENERATOR_ID",
//		pkColumnName="person_id",
//		pkColumnValue="ID_VAL",
//		allocationSize=1,
//		initialValue=1)
	@GeneratedValue(strategy=GenerationType.AUTO)
	private Integer id;
	@Column(name="jpa_last_name",length=50,nullable=false)							//映射数据库字段
	private String lastName;
	@Column(length=50,nullable=false,name="jpa_email")
	private String email;
	@Column(name="jpa_age",length=10,nullable=false)
	private Integer age;
	@Temporal(TemporalType.TIMESTAMP)
	@Column(name="jap_createTime")
	private Date createTime;
	@Temporal(TemporalType.DATE)
	@Column(name="jpa_birth")
	private Date birth;
	//映射 单向一对多的关联映射	
	//使用 @OneToMany 来映射 1-n 的关联关系
	//@OneToMany 可以使用属性 fetch 属性来指定 加载方式 懒加载和急加载
	//注意:在1的一端的@OneToMany 中使用 mapperedBy 属性,则@OneToMany端就不能使用@JoinColumn属性了  但是 也没有外键约束关联了
	//@JoinColumn(name="customer_id")
	//可以通过 修改 cascade 的属性 来修改删除策略。cascade = {CascadeType.REMOVE}  实现级联删除策略
    //指定 mappedBy   表示该类是 1的一端,不维护关联关系,需要使用mapperedBy 指定维护关联关系的一方
	@OneToMany(fetch=FetchType.EAGER,cascade={CascadeType.REMOVE},mappedBy="customer")
	private Set orders =  new HashSet();

	public Set getOrders() {
		return orders;
	}
	public void setOrders(Set orders) {
		this.orders = orders;
	}
	public Date getCreateTime() {
		return createTime;
	}
	public void setCreateTime(Date createTime) {
		this.createTime = createTime;
	}
	public Date getBirth() {
		return birth;
	}
	public void setBirth(Date birth) {
		this.birth = birth;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	public Customer() {
		super();
		// TODO Auto-generated constructor stub
	}
	
	//这种工具方法  不需要映射为数据裤的 字段
	//@Transient  用来指定 不被映射成数据裤的字段
	@Transient
	public String getInfo(){
		return "lastName:"+lastName+", email:"+email;
	}
	
	public Customer(Integer id, String lastName, String email, Integer age) {
		super();
		this.id = id;
		this.lastName = lastName;
		this.email = email;
		this.age = age;
	}
	@Override
	public String toString() {
		return "Customer [id=" + id + ", lastName=" + lastName + ", email=" + email + ", age=" + age + "]";
	}
	
}
  • Order.java:
package com.nyist.Jpa.Entity;

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.ManyToOne;
import javax.persistence.Table;

@Table(name="jpa_order")
@Entity
public class Order {
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private Integer id;
	private String orderName;
	//如何映射 单项 多对一 的 n-1的关系
	//@JoinColumn	外键列的列名称
	//使用@ManyToOne	来映射多对一
	//使用@JoinColumn 映射外键
	//@ManyToOne 使用fetch 方法来改变默认关联属性的加载策略
	/**fetch: 表示该属性的读取策略,有 EAGER 和 LAZY 两种,分别表示主支抓取和延迟加载,默认为 EAGER.
	 *  FetchType.LAZY:懒加载,加载一个实体时,定义懒加载的属性不会马上从数据库中加载。
  		FetchType.EAGER:急加载,加载一个实体时,定义急加载的属性会立即从数据库中加载。
	 */
	@JoinColumn(name="customer_id")
	@ManyToOne(fetch=FetchType.LAZY)
	private Customer customer;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getOrderName() {
		return orderName;
	}
	public void setOrderName(String orderName) {
		this.orderName = orderName;
	}
	public Customer getCustomer() {
		return customer;
	}
	public void setCustomer(Customer customer) {
		this.customer = customer;
	}
	public Order(Integer id, String orderName, Customer customer) {
		super();
		this.id = id;
		this.orderName = orderName;
		this.customer = customer;
	}
	public Order() {
		super();
		// TODO Auto-generated constructor stub
	}
	@Override
	public String toString() {
		return "Order [id=" + id + ", orderName=" + orderName + "]";
	}
	
}
  • 测试类TestIncidenceMapping.java
package com.nyist.Jpa.Test;


import java.util.Date;

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

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.nyist.Jpa.Entity.Customer;
import com.nyist.Jpa.Entity.Order;

//测试 关联映射
public class TestIncidenceMapping {
	
	private EntityManagerFactory entityManagerFactory;
	private EntityManager entityManager;
	private EntityTransaction entityTransaction;
	
	@Before
	public void init(){
		String persistenceUnitName = "Jpa_01";
		entityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnitName);
		entityManager = entityManagerFactory.createEntityManager();
		entityTransaction = entityManager.getTransaction();
		entityTransaction.begin();
	}
	
	@After
	public void destory(){
		//提交事务
		entityTransaction.commit();
		//关闭entityManager
		entityManager.close();
		//关闭entityManagerFactory
		entityManagerFactory.close();
	}
	
	/**
	 * 保存多对一时候,建议先保存1的一端,后保存n的端,这样不会额外多处update 语句
	 */
	//若是双向 一对多的关联关系  若先保存 n 的一端,再保存1的一端口。默认情况下,会多出4条Update语句
	//若先保存1的一段,则会多出2条update 语句
	//在进行双向 1-n关联映射的时,建议使用 n 的一方的来维护关联关系,而1的一方不维护关联关系。这样会有效的减少SQL语句
	
	@Test
	public void ManyToOne(){
		Customer cs = new Customer();
		cs.setAge(22);
		cs.setBirth(new Date());
		cs.setCreateTime(new Date());
		cs.setEmail("[email protected]");
		cs.setLastName("PQQP");
		
		Order order = new Order();
		order.setOrderName("OP-MM-1");
		
		Order order2 = new Order();
		order2.setOrderName("PO-MM2");
		//设置关联关系 
//		order.setCustomer(cs);
//		order2.setCustomer(cs);
		//持久化到数据表中
		entityManager.persist(cs);
		entityManager.persist(order);
		entityManager.persist(order2);
	}
	
	//默认情况下,使用左外连接的方式获取n的一端,和其关联的1 的一端的对象
	//@ManyToOne 使用fetch 方法来改变默认关联属性的加载策略
	/**fetch: 表示该属性的读取策略,有 EAGER 和 LAZY 两种,分别表示主支抓取和延迟加载,默认为 EAGER.
	 *  FetchType.LAZY:懒加载,加载一个实体时,定义懒加载的属性不会马上从数据库中加载。
  		FetchType.EAGER:急加载,加载一个实体时,定义急加载的属性会立即从数据库中加载。
	 */
	@Test
	public void testManyToOneFind(){
//		Order order = entityManager.find(Order.class,1);
//		System.out.println(order.getOrderName());
		//不能直接删除 1 的一端,因为有外键约束
		Customer customer = entityManager.find(Customer.class,7);
		entityManager.remove(customer);
//		System.out.println(customer.getCustomer().getLastName());
	}
	
	@Test
	public void testManyToOneRemove(){
		Order order = entityManager.find(Order.class,1);
		entityManager.remove(order);
	}
	
//	@Test
//	public void testManyToOneUpdate(){
//		Order order = entityManager.find(Order.class,2);
//		order.getCustomer().setLastName("AAA");
//	}
	/*****************************************************一对多***********************************************/
	@Test
	public void testOneToMany(){
	}
	
	//单向 1-n  关联 关系执行保存的时候,一定会多出Update 语句。
	//因为 n 的 一端口,在插入的时候不会同时插入外键列
	@Test
	public void testOneToManyPersist(){
		Customer cs = new Customer();
		cs.setAge(18);
		cs.setBirth(new Date());
		cs.setCreateTime(new Date());
		cs.setEmail("[email protected]");
		cs.setLastName("XX");
		//处理关联映射
		Order order = new Order();
		order.setOrderName("DD-WW-ZZ");
		Order order2 = new Order();
		order2.setOrderName("XX-WW-DD"); 
		//建立关联关系
		cs.getOrders().add(order);
		cs.getOrders().add(order2);
		//持久化  到数据裤中
		entityManager.persist(cs);
		entityManager.persist(order);
		entityManager.persist(order2);
	}
	
	@Test
	public void testOnToManyFind(){
		Customer Customer = entityManager.find(Customer.class,8);
		System.out.println(Customer.getLastName());
		System.out.println(Customer.getOrders().size());
	}
	
	//删除
	//默认情况下,若删除 1 的一端,则会把关联的多的一端的外键置空,然后进行删除。
	@Test
	public void OneToManyRemove(){
		//删除  Customer
		Customer customer = entityManager.find(Customer.class,6);
		entityManager.remove(customer);
	} 
	
	//修改
	@Test
	public void testOneToManyUpdate(){
		Customer customer = entityManager.find(Customer.class,7);
		customer.getOrders().iterator().next().setOrderName("O-XXX-10");
	}
}
  • 数据裤中生成的表如下:

JPA_第18张图片

JPA_第19张图片JPA_第20张图片

 

2.一对一映射

  • 一对一映射,用例是经理和部门的关系,一个经理只能管理一个部门,一个部门也只能属于一个经理管理,这样就形成了

一对一映射,但是在部门表中需要维护一个外键关联关系,因为部门是由经理管理的,所以知道部门的时候应该能找到相应的经理。

  • 创建Manger.java 和 DepartMent.java实体类
  • Manger.java 内容如下:
package com.nyist.Jpa.Entity;

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.OneToOne;
import javax.persistence.Table;

@Table(name="jpa_manager")
@Entity
public class Manager {

	@GeneratedValue(strategy=GenerationType.AUTO)
	@Id
	private Integer id;
	@Column(name="mgr_name")
	private String mgrName;
	//对于不维护 关联关系的一方,使用@OneToOne 来进行映射关联属性,建议设置mapperBy=true 
	@OneToOne(mappedBy="mgr",fetch=FetchType.LAZY)
	private Department dept;
	
	public Department getDept() {
		return dept;
	}
	public void setDept(Department dept) {
		this.dept = dept;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getMgrName() {
		return mgrName;
	}
	public void setMgrName(String mgrName) {
		this.mgrName = mgrName;
	}
	protected Manager(Integer id, String mgrName) {
		this.id = id;
		this.mgrName = mgrName;
	}
	public Manager() {
		super();
	}
	@Override
	public String toString() {
		return "Manager [id=" + id + ", mgrName=" + mgrName + ",dept="+dept+"]";
	}
}
  • DepartMent.java 实体
package com.nyist.Jpa.Entity;

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_department")
@Entity
public class Department {
	@Id
	@GeneratedValue
	private Integer id;
	@Column(name="dept_name")
	private String deptName;
	
	//使用@OneToOne 来映射 一对一 关系
	//若需要在当前数据表中添加主键则使用 @JOinColumn() 来映射,注意 一对一关联关系  需要添加 unique=true
	//注意:	@Column 和 @OneToOne 、@ManyToOne 等注解 不能一块使用
	//@Column(name="mgr")
	@JoinColumn(name="mgr_id",unique=true,nullable=false)
	@OneToOne(fetch=FetchType.LAZY)
	private Manager mgr;
	
	public Manager getMgr() {
		return mgr;
	}
	public void setMgr(Manager mgr) {
		this.mgr = mgr;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getDeptName() {
		return deptName;
	}
	public void setDeptName(String deptName) {
		this.deptName = deptName;
	}
	public Department(Integer id, String deptName,Manager mgr) {
		this.id = id;
		this.deptName = deptName;
		this.mgr=mgr;
	}
	public Department() {
	}
	@Override
	public String toString() {
		return "Department [id=" + id + ", deptName=" + deptName +",mgr="+mgr+"]";
	}
}
  •  测试类TestOneToOne.java
package com.nyist.Jpa.Test;

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

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.nyist.Jpa.Entity.Department;
import com.nyist.Jpa.Entity.Manager;

public class TestOneToOne {
	
	private EntityManagerFactory entityManagerFactory;
	private EntityManager entityManager;
	private EntityTransaction entityTransaction;
	
	@Before
	public void init(){
		String persistenceUnitName = "Jpa_01";
		entityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnitName);
		entityManager = entityManagerFactory.createEntityManager();
		entityTransaction = entityManager.getTransaction();
		entityTransaction.begin();
	}
	
	@After
	public void destory(){
		//提交事务
		entityTransaction.commit();
		//关闭entityManager
		entityManager.close();
		//关闭entityManagerFactory
		entityManagerFactory.close();
	}
	
	@Test
	public void testOneToOne(){}
	
	//双向 1-1 的关系映射,建议先保存不维护关联关系的一方,即没有外键的一方,这样不会多出UPDATE 语句
	@Test
	public void testOneToOnePersistence(){
		Manager manager = new Manager();
		manager.setMgrName("M-CC");
		Department department = new Department();
		department.setDeptName("D-CC");
		//设置 关联关系
		manager.setDept(department);
		department.setMgr(manager);
		//持久化 到数据裤
		entityManager.persist(manager);
		entityManager.persist(department);
	}
	
	//默认情况下
	//1.若获取维护关联关系的一方,则会通过左外连接获取关联对象
	//可以通过 @OneToOne fetch 的策略 改为 懒加载  可以改变
	@Test
	public void testOneToOneFind(){
		Manager manager = entityManager.find(Manager.class,1);
		System.out.println(manager.getMgrName());
		System.out.println(manager.getDept().getClass().getName());
	}
	
	//1.若获取维护关联关系的一方,则会通过左外连接获取关联对象
	//可以通过 @OneToOne fetch 的策略 改为 懒加载  可以改变,但以然会在发送SQL 语句来初始化其关联对象
	//这说明 在不维护关系的一方,不建议修改fetch 属性
	@Test
	public void testOneToOneFind1(){
		Department department = entityManager.find(Department.class,2);
		System.out.println(department.getDeptName());
		System.out.println(department.getMgr().getClass().getName());
	}
}
  • 数据库表关系如下:

JPA_第21张图片  JPA_第22张图片

JPA_第23张图片

3.多对多关联映射

  • 多对多的关联关系映射相当于项目(item)和部门的关系(Category),一个项目的完成和构建可以对应多个部门的共同维护和协作,一个部门又可以维护多个项目。所以这俩者之间就形成了多对多的关联映射关系。
  • Item.java 实体类:
package com.nyist.Jpa.Entity;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
@Table(name="jpa_item")
@Entity
public class Item {
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private Integer id;
	@Column(name="item_name")
	private String itemName;
	/**
	 * 使用 @ManyToMany  注解来映射多对多关联映射关系
	 * 使用@JoinTable	来映射中间表
	 * 1.name	指向中间表的名称
	 * 2.joinColumns	映射当前类所在的表在中间表的外键
	 * 	2.1	name	制定外键列名
	 * 	2.2	referencedColumnName	指定外键列关联当前表的那一列
	 * 3.inverseJoinColumns	映射关联的类所在中间表的外键
	 * 当前类所在的表在中间表的外键
	 */
	@JoinTable(name="jpa_item_category",
			joinColumns={@JoinColumn(name="item_id",referencedColumnName="id")},
			inverseJoinColumns={@JoinColumn(name="category_id",referencedColumnName="id")})
	@ManyToMany
	private Set categories = new HashSet();
	
	public Integer getId() {
		return id;
	}

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

	public String getItemName() {
		return itemName;
	}

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

	public Set getCategories() {
		return categories;
	}

	public void setCategories(Set categories) {
		this.categories = categories;
	}

	@Override
	public String toString() {
		return "Item [id=" + id + ", itemName=" + itemName + ", categories=" + categories + "]";
	}
}
  • Category.java 实体类:
package com.nyist.Jpa.Entity;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

@Table(name="jpa_category")
@Entity
public class Category {
	@GeneratedValue(strategy=GenerationType.AUTO)
	@Id
	private Integer id;
	@Column(name="category_name")
	private String categoryName;
	@ManyToMany(mappedBy="categories") 
	private Set items = new HashSet();
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getCategoryName() {
		return categoryName;
	}
	public void setCategoryName(String categoryName) {
		this.categoryName = categoryName;
	}
	public Set getSet() {
		return items;
	}
	public void setSet(Set set) {
		this.items = set;
	}
	public Category(Integer id, String categoryName, Set set) {
		super();
		this.id = id;
		this.categoryName = categoryName;
		this.items = set;
	}

	public Category() {
		super();
	}
	@Override
	public String toString() {
		return "Category [id=" + id + ", categoryName=" + categoryName + ", set=" + items + "]";
	}
	
}
  •  测试类TestManyToMany.java如下:
package com.nyist.Jpa.Test;

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

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.nyist.Jpa.Entity.Category;
import com.nyist.Jpa.Entity.Item;

public class TestManyToMany {

	private EntityManagerFactory entityManagerFactory;
	private EntityManager entityManager;
	private EntityTransaction entityTransaction;
	
	@Before
	public void init(){
		String persistenceUnitName = "Jpa_01";
		entityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnitName);
		entityManager = entityManagerFactory.createEntityManager();
		entityTransaction = entityManager.getTransaction();
		entityTransaction.begin();
	}
	
	@After
	public void destory(){
		//提交事务
		entityTransaction.commit();
		//关闭entityManager
		entityManager.close();
		//关闭entityManagerFactory
		entityManagerFactory.close();
	}
	
	@Test
	public void testManyToMany(){
		
	}
	
	@Test
	public void testManyToManyPersistence(){
		Item item = new Item();
		item.setItemName("I-AA");
		Item item2 = new Item();
		item2.setItemName("I—BB");
		
		Category category = new Category();
		category.setCategoryName("C-AA");
		Category category2 = new Category();
		category2.setCategoryName("C-BB");
		
		//设置关联关系映射
		item.getCategories().add(category);
		item.getCategories().add(category2);
		
		item2.getCategories().add(category);
		item2.getCategories().add(category2);
		
		category.getSet().add(item);
		category.getSet().add(item2);
		
		category2.getSet().add(item);
		category2.getSet().add(item2);
		
		//持久化 到数据裤中
		entityManager.persist(item);
		entityManager.persist(item2);
		entityManager.persist(category);
		entityManager.persist(category2);
	}
	
	//对于关联的集合对象 默认使用懒加载策略
	//使用维护关联关系的一方获取,还是使用不维护关系一方获取,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.getSet().size());
	}
}
  •  数据库表的关系如下:

JPA_第24张图片  JPA_第25张图片  JPA_第26张图片

JPA_第27张图片

 

 

 

 

你可能感兴趣的:(JPA,JPA)