JPA(二)
JPA-API
Persistence
- Persistence 类是用于获取 EntityManagerFactory 实例。该类包含一个名为 createEntityManagerFactory 的 静态方法 。
- createEntityManagerFactory 方法有如下两个重载版本。
- 带有一个参数的方法以 JPA 配置文件 persistence.xml 中的持久化单元名为参数
- 带有两个参数的方法:前一个参数含义相同,后一个参数 Map类型,用于设置 JPA 的相关属性,这时将忽略其它地方设置的属性。Map 对象的属性名必须是 JPA 实现库提供商的名字空间约定的属性名。
//1. 创建 EntitymanagerFactory
String persistenceUnitName = "jpa-1";
Map<String, Object> properites = new HashMap<String, Object>();
//此时就是会打印sql语句
properites.put("hibernate.show_sql", true);
EntityManagerFactory entityManagerFactory =
//Persistence.createEntityManagerFactory(persistenceUnitName);
Persistence.createEntityManagerFactory(persistenceUnitName, properites);
EntityManagerFactory
- EntityManagerFactory 接口主要用来创建 EntityManager 实例。该接口约定了如下4个方法:
- createEntityManager():用于创建实体管理器对象实例。
- createEntityManager(Map map):用于创建实体管理器对象实例的重载方法,Map 参数用于提供 EntityManager 的属性。
- isOpen():检查 EntityManagerFactory 是否处于打开状态。实体管理器工厂创建后一直处于打开状态,除非调用close()方法将其关闭。
- close():关闭 EntityManagerFactory 。 EntityManagerFactory 关闭后将释放所有资源,isOpen()方法测试将返回 false,其它方法将不能调用,否则将导致IllegalStateException异常。
//2. 创建 EntityManager. 类似于 Hibernate 的 SessionFactory
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityManager
- 在 JPA 规范中, EntityManager 是完成持久化操作的核心对象。实体作为普通 Java 对象,只有在调用 EntityManager 将其持久化后才会变成持久化对象。EntityManager 对象在一组实体类与底层数据源之间进行 O/R 映射的管理。它可以用来管理和更新 Entity Bean, 根椐主键查找 Entity Bean, 还可以通过JPQL语句查询实体。
- 实体的状态:
- 新建状态: 新创建的对象,尚未拥有持久性主键。
- 持久化状态:已经拥有持久性主键并和持久化建立了上下文环境
- 游离状态:拥有持久化主键,但是没有与持久化建立上下文环境
- 删除状态: 拥有持久化主键,已经和持久化建立上下文环境,但是从数据库中删除。
首先,我们先将实体类对应的数据表的主键的生成策略改成auto;
@Table(name="JPA_CUSTOMERS")
@Entity
public class Customer {
private Integer id;
private String lastName;
private String email;
private int age;
private Date createTime;
private Date birth;
//时间戳类型的
@Temporal(TemporalType.TIMESTAMP)
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@Temporal(TemporalType.DATE)
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
//如果列名和字段名一样的话,@Column则可以省略
/* @TableGenerator(name="ID_GENERATOR",
table="jpa_id_generator",
pkColumnName="PK_NAME",
pkColumnValue="CUSTOMER_ID",
valueColumnName="PK_VALUE",
allocationSize=100)
@GeneratedValue(strategy=GenerationType.TABLE,generator="ID_GENERATOR")*/
@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;
}
//这个也可以不用写@Column
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//写上一个工具方法
@Transient
public String getInfo(){
return "lastName:"+lastName+" ,email:"+email;
}
@Override
public String toString() {
return "Customer [id=" + id + ", lastName=" + lastName + ", email="
+ email + ", age=" + age + ", createTime=" + createTime
+ ", birth=" + birth + "]";
}
}
find (Class<T> entityClass,Object primaryKey)
:返回指定的 OID 对应的实体类对象,如果这个实体存在于当前的持久化环境,则返回一个被缓存的对象;否则会创建一个新的 Entity, 并加载数据库中相关信息;若 OID 不存在于数据库中,则返回一个 null。第一个参数为被查询的实体类类型,第二个参数为待查找实体的主键值。
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 destroy(){
transaction.commit();
entityManager.close();
entityManagerFactory.close();
}
//类似于Hibernate中的Session的get方法
@Test
public void testFind() {
}
}
我们运行testFind方法重新生成一下数据表:
我们往里面添加一条数据:
//类似于Hibernate中的Session的get方法
@Test
public void testFind() {
/**
* 返回根据指定id从数据表从查找对应的实体对象
* 第一个参数为被查询的实体类类型,第二个参数为待查找实体的主键值。
*/
Customer customer = entityManager.find(Customer.class, 1);
System.out.println("------------------------");
System.out.println(customer);
}
运行效果:
getReference (Class<T> entityClass,Object primaryKey)
:与find()方法类似,不同的是:如果缓存中不存在指定的 Entity, EntityManager 会创建一个 Entity 类的代理,但是不会立即加载数据库中的信息,只有第一次真正使用此 Entity 的属性才加载,所以如果此 OID 在数据库不存在,getReference() 不会返回 null 值, 而是抛出EntityNotFoundException
/**
* 相当于Hibernate中Session的load方法
*/
@Test
public void testGetReference(){
Customer customer = entityManager.getReference(Customer.class, 1);
System.out.println("------------------------");
System.out.println(customer);
}
此时返回的就是一个代理对象,既然是一个代理对象,就是会出现懒加载异常的问题-就是在真正使用实体对象之前,我们把EntityManager对象给关了,
我们可以来测试一下:
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 destroy(){
/*transaction.commit();
entityManager.close();*/
entityManagerFactory.close();
}
//类似于Hibernate中的Session的get方法
@Test
public void testFind() {
/**
* 返回根据指定id从数据表从查找对应的实体对象
* 第一个参数为被查询的实体类类型,第二个参数为待查找实体的主键值。
*/
Customer customer = entityManager.find(Customer.class, 1);
System.out.println("------------------------");
System.out.println(customer);
}
/**
* 相当于Hibernate中Session的load方法
*/
@Test
public void testGetReference(){
Customer customer = entityManager.getReference(Customer.class, 1);
System.out.println(customer.getClass().getName());
//测试懒加载异常-在真正使用实体对象之前把entityManager对象给关了
transaction.commit();
entityManager.close();
System.out.println("------------------------");
System.out.println(customer);
}
}
当我们再运行的时候,报错了-出现懒加载异常:
persist (Object entity)
:用于将新创建的 Entity 纳入到 EntityManager 的管理。该方法执行后,传入 persist() 方法的 Entity 对象转换成持久化状态。- 如果传入 persist() 方法的 Entity 对象已经处于持久化状态,则 persist() 方法什么都不做。
- 如果对删除状态的 Entity 进行 persist() 操作,会转换为持久化状态。
- 如果对游离状态的实体执行 persist() 操作,可能会在 persist() 方法抛出 EntityExistException(也有可能是在flush或事务提交后抛出)。
/**
* 类似于hibernate中的save方法,使对象由临时状态变为持久化的状态
* 和hibernate中的save方法的不同之处:若对象有id,则不能执行insert的操作,而是会抛出异常
*/
@Test
public void testPersist(){
Customer customer = new Customer();
customer.setAge(15);
customer.setBirth(new Date());
customer.setCreateTime(new Date());
customer.setEmail("ldc126@ldc.com");
customer.setLastName("AA");
customer.setId(100);
entityManager.persist(customer);
System.out.println(customer.getId());
}
若对象有id的话,则不能执行insert的操作,而是会抛出一个异常:
remove (Object entity)
:删除实例。如果实例是被管理的,即与数据库实体记录关联,则同时会删除关联的数据库记录。
/**
* 类似于hibernate中的delete方法,把对象对应的记录从数据库中移除
* 但注意:该方法只能移除持久化对象,而hibernate的delete方法实际上还可以移除游离对象
*/
@Test
public void testRemove(){
/* Customer customer = new Customer();
customer.setId(2);*/
//此时,是根据id从数据库中获取的,此时的状态是为持久化的状态
Customer customer = entityManager.find(Customer.class, 2);
entityManager.remove(customer);
}
这个时候,就是可以从数据库中移除的:
id为2的记录已经被删除了:
merge (T entity)
:merge() 用于处理 Entity 的同步。即数据库的插入和更新操作
1.当传入的是一个临时对象
//1.若传入的就是一个临时对象
/**
* 会创建一个新的对象,把临时对象的属性复制到新的对象中,然后把新的对象执行持久化操作,
* 所以新的对象中有id,但以前的临时对象中没有id
*/
@Test
public void testMerge1(){
Customer customer = new Customer();
customer.setAge(18);
customer.setBirth(new Date());
customer.setCreateTime(new Date());
customer.setEmail("CC@163.com");
customer.setLastName("CC");
Customer customer2 = entityManager.merge(customer);
System.out.println("customer "+customer.getId());
System.out.println("customer2 "+customer2.getId());
}
2.若传进来的是一个游离对象,也即传进来的对象里面有id:
/**
* 若传进来的是一个游离对象,即传入的对象有OID
* 1.若在entityManager缓存中没有该对象
* 2.若在数据库中一没有对应的记录
* 3.JPA会创建一个新的对象,然后把当前游离对象的属性复制到新创建的对象中
* 4.对新创建的对象执行insert操作
*/
@Test
public void testMerge2(){
Customer customer = new Customer();
customer.setAge(18);
customer.setBirth(new Date());
customer.setCreateTime(new Date());
customer.setEmail("DD@163.com");
customer.setLastName("DD");
customer.setId(100);
Customer customer2 = entityManager.merge(customer);
System.out.println("customer "+customer.getId());
System.out.println("customer2 "+customer2.getId());
}
3.若传进来的对象缓存中没有对应的记录,而数据库中有对应的记录,则会先去查询,再复制属性,再进行更新的操作
/**
* 若传进来的是一个游离对象,即传入的对象有OID
* 1.若在entityManager缓存中没有该对象
* 2.若在数据库中有对应的记录
* 3.JPA会查询对应的记录,然后返回该记录对应的对象,再然后会把游离对象的属性复制到查询到的对象中
* 4.对查询到的对象执行update操作
*/
@Test
public void testMerge3(){
Customer customer = new Customer();
customer.setAge(18);
customer.setBirth(new Date());
customer.setCreateTime(new Date());
customer.setEmail("EE@163.com");
customer.setLastName("EE");
customer.setId(5);
Customer customer2 = entityManager.merge(customer);
System.out.println("customer "+customer.getId());
System.out.println("customer2 "+customer2.getId());
}
4.若传进来的是一个游离的对象,而缓存中也有这个对象,则会复制属性给缓存中的对象,然后进行update的操作:
/**
* 若传进来的是一个游离对象,即传入的对象有OID
* 1.若在entityManager缓存中有对应的对象
* 2.JPA会把游离对象的属性复制到entityManager缓存中的对象中
* 3.对entityManager缓存中的对象执行update的操作
*/
@Test
public void testMerge4(){
Customer customer = new Customer();
customer.setAge(18);
customer.setBirth(new Date());
customer.setCreateTime(new Date());
customer.setEmail("EE@163.com");
customer.setLastName("EE");
customer.setId(5);
Customer customer2 = entityManager.find(Customer.class, 5);
entityManager.merge(customer);
System.out.println("customer "+customer.getId());
System.out.println("customer2 "+customer2.getId());
}
- 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的 名称。
EntityTransaction
- EntityTransaction 接口用来管理资源层实体管理器的事务操作。通过调用实体管理器的getTransaction方法 获得其实例。
- begin ()
- 用于启动一个事务,此后的多个数据库操作将作为整体被提交或撤消。若这时事务已启动则会抛出 IllegalStateException 异常。
- commit ()
- 用于提交当前事务。即将事务启动以后的所有数据库更新操作持久化至数据库中。
- rollback ()
- 撤消(回滚)当前事务。即撤消事务启动后的所有数据库更新操作,从而不对数据库产生影响。
- setRollbackOnly ()
- 使当前事务只能被撤消。
- getRollbackOnly ()
- 查看当前事务是否设置了只能撤消标志。
- isActive ()
- 查看当前事务是否是活动的。如果返回true则不能调用begin方法,否则将抛出 IllegalStateException 异常;如果返回 false 则不能调用 commit、rollback、setRollbackOnly 及 getRollbackOnly 方法,否则将抛出 IllegalStateException 异常。
映射关联关系
双向一对多及多对一映射
首先我们先添加一个订单表实体类,并指定映射关系:
@Entity
@Table(name="JPA_ORDERS")
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来映射外键
*/
//映射单向多对一个关联关系
//外键列的列名是
@JoinColumn(name="CUSTOMER_ID")
@ManyToOne
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
我们可以运行这个方法来生成数据表,我们先看看数据表的结构对不对:
@Test
public void testManyToOne(){
}
此时,数据表已经创建完成了:
/**
* 保存多对一时,建议先保存1的一端,后保存n的一端,这样不会多出额外的update语句
*/
@Test
public void testManyToOnePersist(){
Customer customer = new Customer();
customer.setAge(18);
customer.setBirth(new Date());
customer.setCreateTime(new Date());
customer.setEmail("ff@163.com");
customer.setLastName("ff");
Order order1 = new Order();
order1.setOrderName("O-FF-1");
Order order2 = new Order();
order2.setOrderName("O-FF-2");
//设置关联关系
order1.setCustomer(customer);
order2.setCustomer(customer);
//执行保存操作
entityManager.persist(customer);
entityManager.persist(order1);
entityManager.persist(order2);
}
这个时候,就是已经创建好了:
如果想要使用懒加载的功能的话,则只需要这样来进行设置:@ManyToOne(fetch=FetchType.LAZY)
/**
* 使用@ManyToOne来映射多对一的关联关系
* 使用@JoinColumn来映射外键
*/
//映射单向多对一个关联关系
//外键列的列名是
@JoinColumn(name="CUSTOMER_ID")
@ManyToOne(fetch=FetchType.LAZY)
public Customer getCustomer() {
return customer;
}
查询:
/**
* 默认使用左外连接的方式来获取多的一端和其关联1的一端的对象
*/
@Test
public void testManyToOneFind(){
Order order = entityManager.find(Order.class, 1);
System.out.println(order.getOrderName());
System.out.println(order.getCustomer().getLastName());
}
删除:不能直接删除一的一端,因为有外键的约束
一对多的关系:
@Table(name="JPA_CUSTOMERS")
@Entity
public class Customer {
private Integer id;
private String lastName;
private String email;
private int age;
private Date createTime;
private Date birth;
private Set<Order> orders = new HashSet<>();
/**
* 映射方式跟之前的多对一的方式是对应的
*/
@OneToMany
@JoinColumn(name="CUSTOMER_ID")
public Set<Order> getOrders() {
return orders;
}
public void setOrders(Set<Order> orders) {
this.orders = orders;
}
//时间戳类型的
@Temporal(TemporalType.TIMESTAMP)
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@Temporal(TemporalType.DATE)
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
//如果列名和字段名一样的话,@Column则可以省略
/* @TableGenerator(name="ID_GENERATOR",
table="jpa_id_generator",
pkColumnName="PK_NAME",
pkColumnValue="CUSTOMER_ID",
valueColumnName="PK_VALUE",
allocationSize=100)
@GeneratedValue(strategy=GenerationType.TABLE,generator="ID_GENERATOR")*/
@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;
}
//这个也可以不用写@Column
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//写上一个工具方法
@Transient
public String getInfo(){
return "lastName:"+lastName+" ,email:"+email;
}
@Override
public String toString() {
return "Customer [id=" + id + ", lastName=" + lastName + ", email="
+ email + ", age=" + age + ", createTime=" + createTime
+ ", birth=" + birth + "]";
}
}
测试保存的操作:
/**
* 单向一对多的关联关系执行保存时,一定会多出UPDATE语句
* 因为多的一端在插入时,不会同时插入外键列
*/
@Test
public void testOneToManyPersist(){
Customer customer = new Customer();
customer.setAge(18);
customer.setBirth(new Date());
customer.setCreateTime(new Date());
customer.setEmail("zz@163.com");
customer.setLastName("zz");
Order order1 = new Order();
order1.setOrderName("O-ZZ-1");
Order order2 = new Order();
order2.setOrderName("O-ZZ-2");
//建立关联关系
customer.getOrders().add(order1);
customer.getOrders().add(order2);
//执行保存操作
entityManager.persist(customer);
entityManager.persist(order1);
entityManager.persist(order2);
}
删除的操作:
/**
* 默认情况下,若删除1的一端的,则会先把关联的多的一端的外键置空,然后进行删除的操作
* 可以通过修改@OneToMany的cascade属性来修改默认的删除策略。
*/
@Test
public void testOneToManyRemove(){
Customer customer = entityManager.find(Customer.class, 9);
entityManager.remove(customer);
}
双向一对多或双向多对一的关联关系:
@Table(name="JPA_CUSTOMERS")
@Entity
public class Customer {
private Integer id;
private String lastName;
private String email;
private int age;
private Date createTime;
private Date birth;
private Set<Order> orders = new HashSet<>();
/**
* 映射方式跟之前的多对一的方式是对应的
*/
@OneToMany
@JoinColumn(name="CUSTOMER_ID")
public Set<Order> getOrders() {
return orders;
}
public void setOrders(Set<Order> orders) {
this.orders = orders;
}
//时间戳类型的
@Temporal(TemporalType.TIMESTAMP)
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@Temporal(TemporalType.DATE)
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
//如果列名和字段名一样的话,@Column则可以省略
/* @TableGenerator(name="ID_GENERATOR",
table="jpa_id_generator",
pkColumnName="PK_NAME",
pkColumnValue="CUSTOMER_ID",
valueColumnName="PK_VALUE",
allocationSize=100)
@GeneratedValue(strategy=GenerationType.TABLE,generator="ID_GENERATOR")*/
@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;
}
//这个也可以不用写@Column
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//写上一个工具方法
@Transient
public String getInfo(){
return "lastName:"+lastName+" ,email:"+email;
}
@Override
public String toString() {
return "Customer [id=" + id + ", lastName=" + lastName + ", email="
+ email + ", age=" + age + ", createTime=" + createTime
+ ", birth=" + birth + "]";
}
}
以及:
@Entity
@Table(name="JPA_ORDERS")
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来映射外键
*/
//映射单向多对一个关联关系
//外键列的列名是
@JoinColumn(name="CUSTOMER_ID")
@ManyToOne(fetch=FetchType.LAZY)
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
此时,就是一个双向的关系;需要注意的是:两边的外键名需要一致;
双向一对一映射
@Table(name="JPA_DEPARTMENTS")
@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=true
* @return
*/
@JoinColumn(name="MGR_ID", unique=true)
@OneToOne(fetch=FetchType.LAZY)
public Manager getMgr() {
return mgr;
}
public void setMgr(Manager mgr) {
this.mgr = mgr;
}
}
@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来进行映射,建议设置mapperBy=true
@OneToOne(mappedBy="mgr")
public Department getDept() {
return dept;
}
public void setDept(Department dept) {
this.dept = dept;
}
}
//双向 1-1 的关联关系, 建议先保存不维护关联关系的一方, 即没有外键的一方, 这样不会多出 UPDATE 语句.
@Test
public void testOneToOnePersistence(){
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);
}
//1.默认情况下, 若获取维护关联关系的一方, 则会通过左外连接获取其关联的对象.
//但可以通过 @OntToOne 的 fetch 属性来修改加载策略.
@Test
public void testOneToOneFind(){
Department dept = entityManager.find(Department.class, 1);
System.out.println(dept.getDeptName());
System.out.println(dept.getMgr().getClass().getName());
}
//1. 默认情况下, 若获取不维护关联关系的一方, 则也会通过左外连接获取其关联的对象.
//可以通过 @OneToOne 的 fetch 属性来修改加载策略. 但依然会再发送 SQL 语句来初始化其关联的对象
//这说明在不维护关联关系的一方, 不建议修改 fetch 属性.
@Test
public void testOneToOneFind2(){
Manager mgr = entityManager.find(Manager.class, 1);
System.out.println(mgr.getMgrName());
System.out.println(mgr.getDept().getClass().getName());
}
双向多对多关联关系
@Table(name="JPA_ITEMS")
@Entity
public class Item {
private Integer id;
private String itemName;
private Set<Category> categories = new HashSet<>();
@GeneratedValue
@Id
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(name="ITEM_NAME")
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
//使用 @ManyToMany 注解来映射多对多关联关系
//使用 @JoinTable 来映射中间表
//1. name 指向中间表的名字
//2. joinColumns 映射当前类所在的表在中间表中的外键
//2.1 name 指定外键列的列名
//2.2 referencedColumnName 指定外键列关联当前表的哪一列
//3. inverseJoinColumns 映射关联的类所在中间表的外键
@JoinTable(name="ITEM_CATEGORY",
joinColumns={@JoinColumn(name="ITEM_ID", referencedColumnName="ID")},
inverseJoinColumns={@JoinColumn(name="CATEGORY_ID", referencedColumnName="ID")})
@ManyToMany
public Set<Category> getCategories() {
return categories;
}
public void setCategories(Set<Category> categories) {
this.categories = categories;
}
}
@Table(name="JPA_CATEGORIES")
@Entity
public class Category {
private Integer id;
private String categoryName;
private Set<Item> items = new HashSet<>();
@GeneratedValue
@Id
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(name="CATEGORY_NAME")
public String getCategoryName() {
return categoryName;
}
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
@ManyToMany(mappedBy="categories")
public Set<Item> getItems() {
return items;
}
public void setItems(Set<Item> items) {
this.items = items;
}
}
我们来进行测试:
//对于关联的集合对象, 默认使用懒加载的策略.
//使用维护关联关系的一方获取, 还是使用不维护关联关系的一方获取, SQL 语句相同.
@Test
public void testManyToManyFind(){
// Item item = entityManager.find(Item.class, 5);
// System.out.println(item.getItemName());
//
// System.out.println(item.getCategories().size());
Category category = entityManager.find(Category.class, 3);
System.out.println(category.getCategoryName());
System.out.println(category.getItems().size());
}
//多对所的保存
@Test
public void testManyToManyPersist(){
Item i1 = new Item();
i1.setItemName("i-1");
Item i2 = new Item();
i2.setItemName("i-2");
Category c1 = new Category();
c1.setCategoryName("C-1");
Category c2 = new Category();
c2.setCategoryName("C-2");
//设置关联关系
i1.getCategories().add(c1);
i1.getCategories().add(c2);
i2.getCategories().add(c1);
i2.getCategories().add(c2);
c1.getItems().add(i1);
c1.getItems().add(i2);
c2.getItems().add(i1);
c2.getItems().add(i2);
//执行保存
entityManager.persist(i1);
entityManager.persist(i2);
entityManager.persist(c1);
entityManager.persist(c2);
}