首页 > 代码库 > jpa的双向一对多和双向一对一关联关系

jpa的双向一对多和双向一对一关联关系

在分享之前先给大家看两个异常,应该说这一类的异常还是很常见的,主要原因是jar包冲突

异常1:

java.lang.NoSuchFieldError: INSTANCE
	at org.hibernate.type.BasicTypeRegistry.<init>(BasicTypeRegistry.java:94)
	at org.hibernate.type.TypeResolver.<init>(TypeResolver.java:59)
	at org.hibernate.cfg.Configuration.<init>(Configuration.java:250)
	at org.hibernate.cfg.Configuration.<init>(Configuration.java:302)
	at org.hibernate.cfg.AnnotationConfiguration.<init>(AnnotationConfiguration.java:108)
	at org.hibernate.ejb.Ejb3Configuration.<clinit>(Ejb3Configuration.java:107)
	at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:124)
	at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:52)
	at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:34)
	at org.lxh.util.JpaUtil.<clinit>(JpaUtil.java:10)
	at org.lxh.test.Test.testInsert(Test.java:20)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)

这个异常主要出现在hibernate3.6或更高版本,解决办法是删除hibernate-annotations.jar和hibernate-commons-annotations.jar

因为hibernate3.6包里已经包含以上两包了


异常2:

Caused by: java.lang.NoSuchMethodError: javax.persistence.OneToMany.orphanRemoval()Z  
    at org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:1912)  
    at org.hibernate.cfg.AnnotationBinder.processIdPropertiesIfNotAlready(AnnotationBinder.java:796)  
    at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:707)  
    at org.hibernate.cfg.Configuration$MetadataSourceQueue.processAnnotatedClassesQueue(Configuration.java:4035)  
    at org.hibernate.cfg.Configuration$MetadataSourceQueue.processMetadata(Configuration.java:3989)  
    at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1398)  
    at org.hibernate.cfg.Configuration.buildMappings(Configuration.java:1375)  
    at org.springframework.orm.hibernate3.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:717)  
    at org.springframework.orm.hibernate3.AbstractSessionFactoryBean.afterPropertiesSet(AbstractSessionFactoryBean.java:188)  
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1514)  
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1452)  
    ... 65 more

该异常也是jar包冲突导致,主要还是出现在hibernate3.6或更高版本,解决办法是删除ejb3-persistence.jar

一  一对多关联关系

异常的问题说了,下面进入正题,昨天研究了jpa的基本使用,这次分享下jpa的一对多和一对一关联关系,这一部分也和hibernate大体类似,只是在配置上稍有差异。一对多是很常见的关联关系,在jpa中一对多关联关系由多的一方维护,下面以员工和部门为例来表现一对多的这种关系

1. 部门的员工的实体类

package org.lxh.info;

import java.util.*;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;

@Entity
public class Depart {
	private Integer id;
	private String departName;
	private String departNum;
	private Set<Employee> emp;
    @Id @GeneratedValue
	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}
    @Column(length=50,nullable=false,unique=true)
	public String getDepartName() {
		return departName;
	}

	public void setDepartName(String departName) {
		this.departName = departName;
	}

	public String getDepartNum() {
		return departNum;
	}

	public void setDepartNum(String departNum) {
		this.departNum = departNum;
	}
    @OneToMany(cascade={CascadeType.REMOVE,CascadeType.PERSIST},mappedBy="depart")
	public Set<Employee> getEmp() {
		return emp;
	}

	public void setEmp(Set<Employee> emp) {
		this.emp = emp;
	}
}

一对多关联使用的注解是@OneToMany,cascade就不解释了,mappedBy属性主要用在关系被维护端,在这里就是一的这一边,mappedBy="depart"表明在关系维护端对应的属性为depart

package org.lxh.info;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;

@Entity
public class Employee {
	private Integer id;
	private String num;
	private String name;
	private Depart depart;
       @Id @GeneratedValue
	public Integer getId() {
		return id;
	}

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

	public String getNum() {
		return num;
	}

	public void setNum(String num) {
		this.num = num;
	}
        @Column(nullable=false,length=50)
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
        @ManyToOne
        @JoinColumn(name="depart_id")
	public Depart getDepart() {
		return depart;
	}

	public void setDepart(Depart depart) {
		this.depart = depart;
	}
}

Employee作为关系维护端负责外键字段或外键记录的更新,体现在数据库中则为从表,里面含有一个外键和depart相对应。@JoinColumn用来指定外键的列名,从程序可以看出这里的外键列名为depart_id


2  使用单元测试保存数据

package org.lxh.test;

import static org.junit.Assert.*;

import java.util.*;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Query;

import org.lxh.info.Depart;
import org.lxh.info.Employee;
import org.lxh.util.JpaUtil;

public class Test {

	@org.junit.Test
	public void testInsert() {
		EntityManager em=null;
		EntityTransaction tx=null;
		try{
		    em=JpaUtil.getEntityManager();
	            tx=em.getTransaction();
		    tx.begin();
		    Depart d=new Depart();
		    d.setDepartName("质量管理部");
		    d.setDepartNum("DP0001");
		    
		    Employee e=new Employee();
		    e.setDepart(d);
		    e.setName("李明");
		    e.setNum("C0001");
		    
		    Set<Employee> emps=new HashSet<Employee>();
		    emps.add(e);
		    
		    d.setEmp(emps);;
		    
		    em.persist(d);
		    
			tx.commit();
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(em!=null){
				em.close();
			}
		}
		
	}
	
}

下面是打印出的sql语句:

Hibernate: 
    insert 
    into
        Depart
        (departName, departNum) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        Employee
        (depart_id, name, num) 
    values
        (?, ?, ?)

这样数据就保存成功了,其他操作暂时忽略,大家可以自己写一写


二  一对一关联关系

一对一关联使用的场合相对较少,好多人都会把两个表并为一个,字段看起来很多但是操作起来着实方便

1  先来看看实体,我的例子是人员和人员信息详细表的对于关系

package org.lxh.info;

import java.util.Date;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
@Table(name = "m_users")
public class User {
	
	private int id;
	private String name;
	private Date birthday;
	private Sex sex;
	private UserDetails userDetails;
	@Id
	@GeneratedValue
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}
	@Column(length=20,nullable=false) 
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
        @Temporal(TemporalType.DATE)
	public Date getBirthday() {
		return birthday;
	}

	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}
    @Enumerated(EnumType.STRING)
    @Column(length=5,nullable=false)
	public Sex getSex() {
		return sex;
	}

	public void setSex(Sex sex) {
		this.sex = sex;
	}
        @OneToOne(cascade=CascadeType.ALL)        @JoinColumn(name="detailsId")        public UserDetails getUserDetails() {
		return userDetails;
	}

	public void setUserDetails(UserDetails userDetails) {
		this.userDetails = userDetails;
	}
}

对于一对一关联关系,代码编写者可以根据自已的意愿或业务需要选择任意一方作为关系维护方,我这里选择的人员信息详细作为关系维护方

package org.lxh.info;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.OneToOne;


@Entity
public class UserDetails {
	private Integer id;
	private String introduce;
	private String cardNum;
	private User user;
        @Id @GeneratedValue
   	public Integer getId() {
 		return id;
 	}

 	public void setId(Integer id) {
		 this.id = id;
	}
        @Lob
	public String getIntroduce() {
 		return introduce;
 	}

 	public void setIntroduce(String introduce) {
		this.introduce = introduce;
	 }

 	public String getCardNum() {
		 return cardNum;
	}

	 public void setCardNum(String cardNum) {
		this.cardNum = cardNum;
 	}
        @OneToOne(mappedBy="userDetails",cascade=CascadeType.MERGE)
 	public User getUser() {
		return user;
 	}

	public void setUser(User user) {
 		this.user = user;
	}

}


到这里实体就编写好了


2  一对一的保存操作

@org.junit.Test
	public void testInsert() {
		EntityManager em=null;
		EntityTransaction tx=null;
		try{
			em=JpaUtil.getEntityManager();
			tx=em.getTransaction();
			tx.begin();
			
			User u=new User();
			u.setBirthday(new Date());
			u.setName("潘玮柏");
			u.setSex(Sex.MAN);
			
			UserDetails detail=new UserDetails();
			detail.setCardNum("562923");
			detail.setIntroduce("暂无");
			detail.setUser(u);
			
			u.setUserDetails(detail);
			em.persist(u);
		    
			tx.commit();
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			if(em!=null){
				em.close();
			}
		}
		
	}

打印出来的sql语句如下:

Hibernate: 
    insert 
    into
        UserDetails
        (cardNum, introduce) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        m_users
        (birthday, name, sex, detailsId) 
    values
        (?, ?, ?, ?)

是不是很简单呢得意


jpa的双向一对多和双向一对一关联关系