JPA中(单向)多对一关联关系【1】

JPA多对一关系映射

1.实体类

1).Employee实体类

/**
 * 如何将java实体类,与mysql中的表映射呢?
 * 	通过注解进行配置即可。
 * 编写实体类,关于基本类型属性,一定不要使用基本类型,而是
 * 使用对应的包装类型,为什么呢?
 * 	基本类型属性,如果没有赋值,有一个默认值。
 * 	例如: int age;默认值age=0,0是代表没有填写年龄?0岁?
 * 	推荐使用改包装类:Integer,是一个引用类型数据,如果没有值,默认是null。
 * @author boge
 */
/**
 * Entity注解表示当前类是一个可以与mysql表映射的实体类,表明默认是:Employee的小写
 * 如果,你不喜欢这样,可以通过@Table注解来标识
 * 
 * @Baisic:标识在每一个get方法之上,表示与mysql表中的字段映射,默认就有。
 *  
 * mysql表中的字段,生成,默认按照实体类的get方法进行判断的。
 * 	下面我们做了一个案例,getUserName1方法,但是没有userName1属性。
 * 	结果,在mysql表中,就出现了userName1属性。
 * 	这就证明了,hibernate生成的mysql表中的列,默认是按照get方法进行判断的,而不是
 * 成员属性判断的。
 * @author boge
 */

@Table(name="emp")
@Entity
public class Employee {
	private Integer empId;
	private String userName;
	private Integer age;
	private String address;
	private String gender;
	
	//在多方,使用成员属性,来关联1方实体类
	//在表中,如何体现,关联关系?外键!在多方表中,使用一个外键列,关联一方的主键
	private Department dept;
	
	/**
	 * @ManyToOne默认是立即加载,EAGER,表示查询多方实体对象时,立即将他
	 * 关联的一方实体对象查询出来,并放到多方的成员属性
	 * 如果,修改fetch=FetchType.LAZY,则表示懒加载,意思就是:
	 * 	如果只查询多方数据,只发送查询多方的sql语句,并不会立即查询关联的一方数据。
	 * 	什么时候,通过多方实体对象,调用了关联的一方实体对象,则再发起查询语句去查询一方数据。
	 * 他们适用什么场景?
	 * 	1、如果你的业务场景,仅仅针对多方进行查询,并不需要他的一方数据,以后会单独某个多方
	 * 关联的一方数据,那这种场景,就设置为懒加载。
	 * 	2、如果,你的业务场景,查询多方数据时,必须要立刻查询出来关联的一方数据,那就使用默认的
	 * EAGER立即查询即可。
	 * @return
	 */
	@ManyToOne(fetch=FetchType.LAZY)//关联的对象,采用懒加载查询
	@JoinColumn(name="dept")//指定在多方表中的外键列名称
	public Department getDept() {
		return dept;
	}
	public void setDept(Department dept) {
		this.dept = dept;
	}

	/**
	 * @Id表示:映射mysql表中的主键,你希望哪个java类的属性,映射mysql中的中间,
	 * 你就在它对应的get方法上,添加ID注解即可
	 * 
	 * 如果,你使用的数字,你希望,主键自增,对应mysql表的:auto_increment
	 * 通过@GeneratedValue来指定主键自增策略,strategy指定具体采取哪个策略。
	 * IDENTITY:主键自增
	 * @return
	 */
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	public Integer getEmpId() {
		return empId;
	}
	public void setEmpId(Integer empId) {
		this.empId = empId;
	}
	/*
	 * 默认列名,就是java类的属性名,通过@Column进行修改
	 * ,unique=true,nullable=false
	 */
	@Column(name="user_name")
	@Basic()//  optional=false   userName字段,不能为null
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	/**
	 * 添加@Transient表示,忽略该属性对应的字段,将来就不会针对该属性进行映射
	 * 表中也不会有该字段
	 * 但是要注意:千万不要导错包,是:javax.persistences
	 * @return
	 */
//	@Transient
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public String getGender() {
		return gender;
	}
	public void setGender(String gender) {
		this.gender = gender;
	}
	@Override
	public String toString() {
		return "Employee [empId=" + empId + ", userName=" + userName + ", age=" + age + ", address=" + address
				+ ", gender=" + gender + "]";
	}
	public Employee(Integer empId, String userName, Integer age, String address, String gender) {
		super();
		this.empId = empId;
		this.userName = userName;
		this.age = age;
		this.address = address;
		this.gender = gender;
	}
	/**
	 * 如果你生成了有参构造方法,一定要为该实体类,提供一个无参构造方法
	 */
	public Employee() {
		super();
	}
}

2).Department实体类

@Entity
public class Department {

	private Integer deptId;
	private String deptName;
	private String deptManager;
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	public Integer getDeptId() {
	return deptId;
	}
	public void setDeptId(Integer deptId) {
	this.deptId = deptId;
	}
	public String getDeptName() {
		return deptName;
	}
	public void setDeptName(String deptName) {
		this.deptName = deptName;
	}
	public String getDeptManager() {
		return deptManager;
	}
	public void setDeptManager(String deptManager) {
		this.deptManager = deptManager;
	}
	@Override
	public String toString() {
		return "Department [deptId=" + deptId + ", deptName=" + deptName + ", deptManager=" + deptManager + "]";
	}
}

2.测试类(RMTest)

注:测试方法由下往上依次看更容易理解

/**
 * 关系映射测试类,在本类中,测试实体类之间的关联关系
 * @author boge
 */
public class RMTest {
	/**
	 * 1、单向多对一,员工和部门,两个实体
	 * 		Employee实体类为多方,Department实体类为一方
	 * 	单向多对一:在多方来关联一方的关联关系,只在多方编写关联代码
	 * 如何在多方中,关联一方呢?
	 * 	在多方实体类Employee当中,使用Department成员属性,来表示关联的一方实体类
	 * 在多方实体类中Employee,使用@ManyToOne注解,标注在一方的get方法上面即可。
	 * 	会自动生成多对一关联关系,生成的表当中,会自动添加一个外键列,来关联一方的主键列。
	 */
	EntityManagerFactory emf;
	EntityManager em;
	EntityTransaction transaction;

//	演示通过@ManyToOne注解的fetch属性,修改为懒加载模式。
//	演示查询多方时,默认采用左外连接关联查询一方数据。
	@Test
	public void test4(){
		/*
		 * 因为有ManyToOne关联关系,默认查询多方时,就会通过左外连接查询
		 * 立刻将多方关联的一方数据,查询出来,并且,封装到多方的成员属性当中。
		 * 这也是ORM的一个好处,通过关联对象,将关联的其他对象,直接查询出来。
		 * 同时,这也是一个弊端,如:当前你只想查询员工信息,并不想查询他的部门信息
		 * 但是,它会立即将部门信息查询出来。会影响性能。
		 */
		Employee xiaobai = em.find(Employee.class, 1);
		System.out.println(xiaobai);
		System.out.println(xiaobai.getDept());
	}

//	演示能不能删除一方持久态对象。
	@Test
	public void test3(){
			//持久态
		Department lizong = em.find(Department.class, 1);
		em.remove(lizong);
		//为什么提交了事务,删除持久态对象,报错?
		//原因是:可能是一方持久态对象,有关联的多方数据,所以不能删除,外键级联操作。
		transaction.commit();
	}
	
	@Test
	public void test2(){
		/*		演示一方、多方先、后保存的区别。
		 *  先保存多方,比后保存多方,多了一条Update语句,为什么呢?
		 *  	如果,先保存多方EMployee,在此时,并不知道一方的主键,因为一方还没插入呢!!
		 *  	于是,先保存Employee的除了外键列的其他列数据。
		 *  	然后再保存一方数据,得到了1方Department的主键
		 *  	最后再那种Department的主键去修改多方Employee的外键。
		 *  
		 *  如果先保存1方,就不会多一条UPDATA语句,为什么呢?
		 *  	因为,先保存了1方,1方的主键已经有了,再保存多方的时候,自然可以直接添加外键列。  
		 */
		Employee emp1 = new Employee(null, "小白", 18, "云南昆明", "男");
		Department department1 = new Department();
		department1.setDeptManager("李总");
		department1.setDeptName("研发部");
		//实现关联关系的映射,仅仅通过set方法即可。
		emp1.setDept(department1);
		
		em.persist(emp1);//多方员工
		em.persist(department1);//保存一方部门
		//em.persist(emp1);//多方员工
		
		transaction.commit();	
	}
	
	@Test
	public void test1(){
		Employee emp1 = new Employee(null, "小白", 18, "云南昆明", "男");
		Employee emp2 = new Employee(null, "小黑", 18, "云南昆明", "男");
		
		Department department1 = new Department();
		department1.setDeptManager("李总");
		department1.setDeptName("研发部");
		Department department2 = new Department();
		department2.setDeptManager("张总");
		department2.setDeptName("设计部");
		//通过对象关联的方式,将表中数据进行关联。
		emp1.setDept(department1);
		emp2.setDept(department2);
		
		em.persist(department1);
		em.persist(department2);
		
		em.persist(emp1);
		em.persist(emp2);
		transaction.commit();
		
		/*
		 * 经过测试,我们发现,jpa可以自动帮助我们维护关联关系,
		 * 如:它会自动在多方表中的记录中,通过外键列维护多对一的关系
		 * 而在java代码中,我们仅仅需要,通过java代码,实现对象之间的关联即可。
		 */
	}

	@Before
	public void before() {
		// 1、获取EntityMangerFactory
		emf = Persistence.createEntityManagerFactory("jpa-01");
		// 2、获取EntityManger
		// 重点对象,所有增删改查操作,都是通过它的方法进行的
		em = emf.createEntityManager();
		transaction = em.getTransaction();
		transaction.begin();
	}
}

3.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<!-- RESOURCE_LOCAL:开启本地事务的支持 -->
	<persistence-unit name="jpa-01" transaction-type="RESOURCE_LOCAL" >
	<!--  添加底层实现的支持 ,如,配置HIbernate对于JPA的支持-->
	<provider>org.hibernate.ejb.HibernatePersistence</provider>
	<!-- 映射的实体类 -->
	<class>
		com.kmu.entity.Employee
	</class>
	<class>
		com.kmu.entity.Department
	</class>

	<properties>
		<!-- 配置数据库连接信息 -->
		<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
		<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/jpa" />
		<property name="javax.persistence.jdbc.user" value="root" />
		<property name="javax.persistence.jdbc.password" value="123123" />
		<!-- 配置Hibernate框架需要的部分数据 -->
		<!-- 
			hibernate.format_sql:格式化sql,将来,程序运行时,会将运行的一些
			信息,打印在控制台,如执行的sql语句打印在控制台。
			hibernate.show_sql:在日志中,显示执行的mysql语句
			hibernate.hbm2ddl.auto:自动见表、更新表结构的操作。
				update:通过该配置选项,可以自动根据实体类,生成mysql中的数据表。
				none:则不会自动生成表,而是需要你手动亲自在mysql中创建一个与java类映射的表。
		 -->
		<property name="hibernate.format_sql" value="true" />
		<property name="hibernate.show_sql" value="true" />
		<property name="hibernate.hbm2ddl.auto" value="update" />
	</properties>
	</persistence-unit>
</persistence>

4.所用到得jpa-jar

百度网盘:
链接:https://pan.baidu.com/s/1IS_EMbgNUQXigoNbj3Sp8Q
提取码:r5jw

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值