JPA中(单向)多对一关联关系
程序员文章站
2022-04-15 22:11:58
...
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
上一篇: Hibernate 单向的多对一关联