JPA数据关联


JPA不仅可以实现单表实体映射,也可以实现数据关联技术。常见的有:一对一关联、一对多关联、多对多关联。利用关联技术可以实现级联数据操作,简化代码编写。

项目源码地址:
https://gitee.com/tirklee/leaspring
在这里插入图片描述

实例中所用工具类

package com.xiyue.leaspring.util;

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

/**
 * 定义一个用于操作JPA的工厂程序类,主要功能是负责
 * EntityManager与EntityManagerFactory接口的对象管理
 * @author xiyue
 *
 */
public class JPAEntityFactory {
	private static final String PERSISTENCE_UNIT = "LEASPRING";//持久化单元
	private static EntityManagerFactory entityManagerFactory;//定义连接工厂
	private static ThreadLocal<EntityManager> entityThreadLocal =
			new ThreadLocal<EntityManager>();//保存EntityManager接口对象
	static {//静态代码块获取EntityManagerFactory实例
		rebuildEntityManagerFactory();//实例化EntityManagerFactory接口对象
	}
	
	private JPAEntityFactory() {}
	/**
	 * 该方法的主要功能是获取EntityManagerFactory接口对象
	 */
	private static void rebuildEntityManagerFactory() {
		entityManagerFactory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT);
	}

	/**
	 * 获取EntityManagerFactory接口对象
	 * @return EntityManagerFactory接口实例
	 */
	public static EntityManagerFactory getEntityManagerFactory() {
		if(entityManagerFactory==null) {
			rebuildEntityManagerFactory();
		}
		return entityManagerFactory;
	}
	
	/**
	 * 获取EntityManager对象,不同的线程获取各自的EntityManager接口对象
	 * @return EntityManager接口实例
	 */
	public static EntityManager getEntityManager() {
		EntityManager entityManager = entityThreadLocal.get();//获取EntityManager接口对象
		if(entityManager==null) {//如果没有实例化对象
			if(entityManagerFactory==null) {//未连接工厂
				rebuildEntityManagerFactory();//创建工厂实例
			}//创建新的EntityManager接口对象
			entityManager = entityManagerFactory.createEntityManager();
			entityThreadLocal.set(entityManager);//保存对象信息
		}
		return entityManager;
	}
	
	public static void close() {
		EntityManager entityManager = entityThreadLocal.get();
		if(entityManager!=null) {//已保存EntityManager
			entityManager.close();
			entityThreadLocal.remove();//从ThreadLocal中删除对象
		}
	}
	
}

一对一数据关联

一对一数据关联是数据表数据的垂直拆分,即为了提高数据库操作性能,可以将一张信息内容很多的数据表拆分为若干张数据表。例如,本次将创建一张公司信息表与公司信息详情表,
在这里插入图片描述

  1. 创建数据库脚本
drop DATABASE if EXISTS  lesapring1;
CREATE  DATABASE leaspring1 character SET UTF8; 
use leaspring1;

CREATE TABLE company(
    cid BIGINT  AUTO_INCREMENT ,
    cname VARCHAR(50),
    constraint pk_cid PRIMARY KEY (cid)
)engine=innodb;

use leaspring1;
create table details(
    did BIGINT  AUTO_INCREMENT,
    address VARCHAR(50),
    captial DOUBLE ,
    cid BIGINT ,
    constraint pk_did PRIMARY key (did),
    constraint fk_cid foreign key (cid) references company(cid)on delete CASCADE 
)engine=innodb;
  1. 定义Company持久化类
package com.xiyue.leaspring.po;

import java.io.Serializable;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;

@SuppressWarnings("serial")
@Entity
@Table(name = "company")
public class Company implements Serializable {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long cid;
	private String cname;
	@OneToOne(mappedBy = "company",cascade = CascadeType.ALL)//一对一数据关联,级联更新
	private Details details;//公司详情
	public Long getCid() {
		return cid;
	}
	public void setCid(Long cid) {
		this.cid = cid;
	}
	public String getCname() {
		return cname;
	}
	public void setCname(String cname) {
		this.cname = cname;
	}
	public Details getDetails() {
		return details;
	}
	public void setDetails(Details details) {
		this.details = details;
	}
	@Override
	public String toString() {
		return "Company [cid=" + cid + ", cname=" + cname + ", details=" + details + "]";
	}
	
}
  1. 定义Detail持久化类
package com.xiyue.leaspring.po;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;

@SuppressWarnings("serial")
@Entity
@Table(name="details")
public class Details implements Serializable {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long did;
	private String address;
	private Double capital;
	@OneToOne//一对一关联
	@JoinColumn(name = "cid",
			referencedColumnName = "cid",unique = true)//设置关联数据列
	private Company company;//公司详情数据一个公司
	public Long getDid() {
		return did;
	}
	public void setDid(Long did) {
		this.did = did;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public Double getCapital() {
		return capital;
	}
	public void setCapital(Double capital) {
		this.capital = capital;
	}
	public Company getCompany() {
		return company;
	}
	public void setCompany(Company company) {
		this.company = company;
	}
	@Override
	public String toString() {
		return "Details [did=" + did + ", address=" + address + ", capital=" + capital + ", company=" + company + "]";
	}
}

通过这两个持久化类的关系不难看出,各自的实体类都保存了对方的引用,这样就可以通过类的关系体现类对象的一对一关联。Company类定义中,为了可以在持久化Company对象时同时保存Details数据,使用了cascade级联配置。所有的级联关系都在javax.persistence.CascadeType枚举类中定义,使用ALL表示所有操作都进行级联。也可以选择持久化级联(CascadeType.PERSIST)、更新级联(CascadeType.MERGE)或删除级联(CascadeType.REMOVE)等配置项。

  1. 编写测试类,增加新数据
package com.xiyue.leaspring.po;

import org.junit.Test;

import com.xiyue.leaspring.util.JPAEntityFactory;

public class JPARelations {

	@Test
	public void testAddCompanyAndDetails(){
		Company company = new Company();//创建持久化类对象
		company.setCname("万里雪花香九霄");//设置属性
		Details details = new Details();//创建持久化类对象
		details.setAddress("清空完杀叫月明");//设置属性
		details.setCapital(50000.0);//设置属性
		company.setDetails(details);//设置一对一关联
		details.setCompany(company);//设置一对一关联
		JPAEntityFactory.getEntityManager().getTransaction().begin();//开启事务
		JPAEntityFactory.getEntityManager().persist(company);//持久化数据
		JPAEntityFactory.getEntityManager().getTransaction().commit();//提交事务
		JPAEntityFactory.close();
	}
}
Hibernate: 
    insert 
    into
        company
        (cname) 
    values
        (?)
Hibernate: 
    insert 
    into
        details
        (address, capital, cid) 
    values
        (?, ?, ?)

本程序中配置了一对一关联关系,所以当通过持久化类设置好Company与Details对象引用后,就可以自动实现数据持久化关系匹配。

  1. Company数据信息保存在两张数据表中,下面执行数据查询处理。
package com.xiyue.leaspring.po;

import org.junit.Test;

import com.xiyue.leaspring.util.JPAEntityFactory;

public class JPARelations {
	@Test
	public void testFindCompanyAndDetails(){
		Company company = JPAEntityFactory.getEntityManager().find(Company.class,1L);
		JPAEntityFactory.close();
	}
	
}
Hibernate: 
    select
        company0_.cid as cid1_0_0_,
        company0_.cname as cname2_0_0_,
        details1_.did as did1_2_1_,
        details1_.address as address2_2_1_,
        details1_.capital as capital3_2_1_,
        details1_.cid as cid4_2_1_ 
    from
        company company0_ 
    left outer join
        details details1_ 
            on company0_.cid=details1_.cid 
    where
        company0_.cid=?

通过查询可以发现,虽然查询的是Company实体对象,但由于Company与Details中的数据是一个整体,所以默认会使用多表连接同时查询Details对应的数据。这种多表连接在数据量大时会产生庞大的笛卡儿积,造成访问性能的下降。为了解决这个问题,最好的做法是使用两次查询。

  1. 修改持久化类定义,避免多表关联查询。
    在这里插入图片描述
    进行关联配置时,可以定义数据的抓取策略,该策略通过javax.persistence.FetchType枚举类设置,可以定义为同时抓取(FetchType.EAGER)或延迟抓取(FetchType.LAZY)。这里配置为延迟抓取,这样再次执行查询程序时可以得到如下的执行信息。
Hibernate: 
    select
        company0_.cid as cid1_0_0_,
        company0_.cname as cname2_0_0_ 
    from
        company company0_ 
    where
        company0_.cid=?
Hibernate: 
    select
        details0_.did as did1_2_0_,
        details0_.address as address2_2_0_,
        details0_.capital as capital3_2_0_,
        details0_.cid as cid4_2_0_ 
    from
        details details0_ 
    where
        details0_.cid=?

在实际开发中,经过两次查询处理是标准做法。在进行一对一关联中,此配置尤为重要。

一对多数据关联

在这里插入图片描述

  1. 一对多关联数据库创建脚本
drop DATABASE if EXISTS  lesapring1;
CREATE  DATABASE leaspring1 character SET UTF8; 
use leaspring1;

CREATE TABLE company(
    cid BIGINT  AUTO_INCREMENT ,
    cname VARCHAR(50),
    constraint pk_cid PRIMARY KEY (cid)
)engine=innodb;

use leaspring1;
CREATE TABLE dept(
  deptno bigint AUTO_INCREMENT,
  dname varchar(255),
  cid bigint,
  constraint pk_deptno PRIMARY KEY (deptno),
  constraint fk_cid1 FOREIGN KEY (cid) REFERENCES company(cid) on delete cascade
) ENGINE=InnoDB DEFAULT CHARSET=utf8
  1. 定义Company实体类
package com.xiyue.leaspring.po;

import java.io.Serializable;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@SuppressWarnings("serial")
@Entity
@Table(name = "company")
public class Company implements Serializable {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long cid;
	private String cname;
	@OneToMany(mappedBy = "company",cascade = CascadeType.PERSIST)//一对多关联
	private List<Dept> depts;
	
	public List<Dept> getDepts() {
		return depts;
	}
	public void setDepts(List<Dept> depts) {
		this.depts = depts;
	}
	public Long getCid() {
		return cid;
	}
	public void setCid(Long cid) {
		this.cid = cid;
	}
	public String getCname() {
		return cname;
	}
	public void setCname(String cname) {
		this.cname = cname;
	}
	@Override
	public String toString() {
		return "Company [cid=" + cid + ", cname=" + cname + ", depts=" + depts + "]";
	}
	
}

本程序中使用List集合描述多个部门间的对应关系,同时定义了cascade=Cascade Type.PERSIST持久化时的数据级联处理。这样在保存Company对象数据时,如果depts集合有数据,则会自动保存所有部门信息。

实际开发处理。
------实际开发中,很少会在一对多关联中配置级联CascadeType.PERSIST,因为多数情况下是先存在“一”方数据,而后再添加对应的“多”方数据。以一条新闻数据保存为例,一条新闻会属于一个分类,一个分类有多条新闻,那么肯定是分类这个“一”方先存在才可以创建“多”方新闻数据。

3.定义dept实体类。

package com.xiyue.leaspring.po;

import java.io.Serializable;
import java.util.Date;

import javax.persistence.Cacheable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Version;

import org.hibernate.annotations.ManyToAny;

@SuppressWarnings("serial")
@Cacheable(true)
@Entity
@Table(name = "dept")
public class Dept implements Serializable {//持久化类

	@Id//主键类
	@GeneratedValue(strategy = GenerationType.IDENTITY)//主键生成方式
	private Long deptno;//字段映射(属性名称=字段名称)
	private String dname;
	@ManyToOne
	@JoinColumn(name="cid")
	private Company company;
	public Long getDeptno() {
		return deptno;
	}
	public void setDeptno(Long deptno) {
		this.deptno = deptno;
	}
	public String getDname() {
		return dname;
	}
	public void setDname(String dname) {
		this.dname = dname;
	}
	public Company getCompany() {
		return company;
	}
	public void setCompany(Company company) {
		this.company = company;
	}
	@Override
	public String toString() {
		return "Dept [deptno=" + deptno + ", dname=" + dname + ", company=" + company + "]";
	}
}

  1. 编写测试程序,增加公司与部门数据。
@Test
public void testAddCompanyAndDepts(){
	Company company = new Company();//创建持久化类对象 实例话“一”方对象
	company.setCname("含笑太牛云你说就是牛");//设置属性
	List<Dept> allDepts = new ArrayList<Dept>();//保存所有的部门信息
	for(int i=0;i<10;i++) {
		Dept dept = new Dept();
		dept.setDname("哇嘎就十三"+i);
		dept.setCompany(company);
		allDepts.add(dept);
	}
	JPAEntityFactory.getEntityManager().getTransaction().begin();//开启事务
	JPAEntityFactory.getEntityManager().persist(company);//持久化数据
	JPAEntityFactory.getEntityManager().getTransaction().commit();//提交事务
	JPAEntityFactory.close();
}
Hibernate: 
    insert 
    into
        company
        (cname) 
    values
        (?)
Hibernate: 
    insert 
    into
        dept
        (cid, dname) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        dept
        (cid, dname) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        dept
        (cid, dname) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        dept
        (cid, dname) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        dept
        (cid, dname) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        dept
        (cid, dname) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        dept
        (cid, dname) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        dept
        (cid, dname) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        dept
        (cid, dname) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        dept
        (cid, dname) 
    values
        (?, ?)

本程序首先通过Company与Dept定义了彼此之间的引用关系,随后由于存在级联配置,所以当保存Company对象时会自动保存所有的部门信息。
提示:先保存“多”方。
在JPA开发中会自动帮助开发者维护关联关系,如果说现在创建了一个新的部门与一个新的公司,并且保存的是dept类对象,则在dept与company数据增加完成后自动更新dept表(关联字段cid)以维护关联关系,但是这样的做法明显不符合正常逻辑。

  1. 查询公司
@Test
public void testFindCompanyAndDepts(){
	Company company = JPAEntityFactory.getEntityManager().find(Company.class,3L);
	JPAEntityFactory.close();
}
Hibernate: 
    select
        company0_.cid as cid1_0_0_,
        company0_.cname as cname2_0_0_ 
    from
        company company0_ 
    where
        company0_.cid=?

本程序实现了公司信息查询,但是根据查询语句来看,只是查询了company一张数据表的信息,并没有查询dept表信息,这是因为在JPA中考虑到一对多关联问题,所以默认启动了数据延迟加载,如果此时需要加载部门信息,则可以调用Company类中的集合方法(自动加载“多”方数据)。
6.查询公司信息与对应的全部部门信息。

@Test
public void testFindCompanyAndDepts(){
	Company company = JPAEntityFactory.getEntityManager().find(Company.class,3L);
	company.getDepts().size();
	JPAEntityFactory.close();
}
Hibernate: 
    select
        company0_.cid as cid1_0_0_,
        company0_.cname as cname2_0_0_ 
    from
        company company0_ 
    where
        company0_.cid=?
Hibernate: 
    select
        depts0_.cid as cid3_1_0_,
        depts0_.deptno as deptno1_1_0_,
        depts0_.deptno as deptno1_1_1_,
        depts0_.cid as cid3_1_1_,
        depts0_.dname as dname2_1_1_ 
    from
        dept depts0_ 
    where
        depts0_.cid=?

本程序在EntityManager关闭之前利用company.getDepts().size()获取了“多方”数据,但是如果说此操作发生在EntityManager关闭后,则程序会抛出org.hibernate.LazyInitialization Exception异常。

  1. 查询指定部门数据。
@Test
public void testFindDepts(){
	Dept dept = JPAEntityFactory.getEntityManager().find(Dept.class,3L);
	JPAEntityFactory.close();
}
Hibernate: 
    select
        dept0_.deptno as deptno1_1_0_,
        dept0_.cid as cid3_1_0_,
        dept0_.dname as dname2_1_0_,
        company1_.cid as cid1_0_1_,
        company1_.cname as cname2_0_1_ 
    from
        dept dept0_ 
    left outer join
        company company1_ 
            on dept0_.cid=company1_.cid 
    where
        dept0_.deptno=?

本程序查询了指定部门数据,但是通过查询结果可以发现,默认情况下为了保证Dept与Company的整体性,所以查询采用多表关联一次性查询出指定编号的部门与对应的公司信息,这样做的性能并不高,所以需要修改Dept抓取策略。

  1. 修改Dept持久化类配置抓取策略。
    在这里插入图片描述
    本程序设置了Company数据的抓取策略,再次执行时会发现只产生部门数据,不会查询公司数据,而在EntityManager未关闭前可以利用Dept类中的company属性实现公司数据加载。

  2. 加载部门与公司数据。

@Test
public void testFindDepts(){
	Dept dept = JPAEntityFactory.getEntityManager().find(Dept.class,3L);
	dept.getCompany().getCname();
	JPAEntityFactory.close();
}
Hibernate: 
    select
        dept0_.deptno as deptno1_1_0_,
        dept0_.cid as cid3_1_0_,
        dept0_.dname as dname2_1_0_ 
    from
        dept dept0_ 
    where
        dept0_.deptno=?
Hibernate: 
    select
        company0_.cid as cid1_0_0_,
        company0_.cname as cname2_0_0_ 
    from
        company company0_ 
    where
        company0_.cid=?

此时采用了两个查询语句获取了全部的数据,但是需要注意的是,如果现在执行的是dept.getCompany().getCid()命令,则不会查询company表,因为dept表中保存有cid外键数据。

多对多数据关联

多对多数据关联可以理解为“一对多”与“多对一”的关联组合。在进行多对多配置时,通常会引入一张关联表,保存两张实体表的关系。
在这里插入图片描述

  1. 编写数据库创建脚本
drop DATABASE  if EXISTS  leaspring3;
CREATE database leaspring3 character SET UTF8;
use leaspring3;
--1.用户数据表
create table member(
    mid VARCHAR(50),
    name VARCHAR(50),
    constraint pk_mid primary key (mid)
)engine=innodb;
--2.角色数据表
create table role(
    rid VARCHAR(50),
    title VARCHAR(50),
    constraint pk_rid primary key (rid)
)engine=innodb;
--用户角色关系表
create table member_role(
    mid varchar(50),
    rid varchar(50),
    constraint fk_mid1 FOREIGN KEY (mid)REFERENCES member(mid),
    constraint fk_rid1 FOREIGN KEY (rid)REFERENCES role(rid)
)engine=innodb;


insert into role(rid,title)values("company","公司管理");
insert into role(rid,title)values("dept","部门管理");
insert into role(rid,title)values("emp","雇员管理");
insert into role(rid,title)values("salgrade","薪资管理");
insert into role(rid,title)values("sale","销售管理");
insert into role(rid,title)values("market","市场管理");
insert into role(rid,title)values("project","项目管理");

在给出的数据库脚本中,核心的操作流程是通过用户找到对应的角色;同时,维护一个用户信息时,也一定会进行member_role这个中间关系表的数据更新。

  1. 定义Member实体类,一个Member(用户)包含多个Role(角色)对象。
package com.xiyue.leaspring.po;

import java.io.Serializable;
import java.util.List;

import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;

@SuppressWarnings("serial")
public class Member implements Serializable {

	@Id
	private String mid;
	private String name;
	@ManyToMany(fetch = FetchType.LAZY)//启用延迟加载
	@JoinTable(name = "member_role",//描述一个关联表
			joinColumns = {@JoinColumn(name="mid")},//member与member_role表的连接
			inverseJoinColumns = {@JoinColumn(name="rid")})//通过Member找到Role中的rid的数据
	private List<Role> roles;
	public String getMid() {
		return mid;
	}
	public void setMid(String mid) {
		this.mid = mid;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public List<Role> getRoles() {
		return roles;
	}
	public void setRoles(List<Role> roles) {
		this.roles = roles;
	}
	@Override
	public String toString() {
		return "Member [mid=" + mid + ", name=" + name + ", roles=" + roles + "]";
	}
}
  1. 定义Role实体类,一个Role(角色)对应多个Member(用户)。
package com.xiyue.leaspring.po;

import java.io.Serializable;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.Id;

@SuppressWarnings("serial")
@Entity
public class Role implements Serializable {

	@Id
	private String rid;
	private String title;
	@ManyToMany(mappedBy = "roles",fetch = FetchType.LAZY)//多对多关联
	private List<Member> members;

	public String getRid() {
		return rid;
	}

	public void setRid(String rid) {
		this.rid = rid;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public List<Member> getMembers() {
		return members;
	}

	public void setMembers(List<Member> members) {
		this.members = members;
	}

	@Override
	public String toString() {
		return "Role [rid=" + rid + ", title=" + title + ", members=" + members + "]";
	}
	
	
}

上述程序定义了两个表实体类:Member和Role,但没有为member_role中间关系表定义实体类。这张表的数据维护是由Member类负责的,当用户进行增加、修改或查询操作时,将自动维护此表数据。

4.编写测试类,实现用户数据增加。

@Test
public void testMemberAdd() {
	//member_role关联表里是rid信息,下面定义的都是rid内容
	String rids[] = new String[] {"company","dept","emp"};//定义角色编号
	//设置一堆的Role对象只是为了获取一个rid属性内容,但是关联关系必须要求产生Role集合对象
	List<Role> allRoles = new ArrayList<Role>();//保存角色信息
	Member member = new Member();//实例化持久类对象
	member.setMid("xiyueliushui");//设置数据
	member.setName("天穹圣戟");//设置数据
	for (int i = 0; i < rids.length; i++) {//配置用户角色
		Role role = new Role();//实例化持久类对象
		role.setRid(rids[i]);//设置角色
		allRoles.add(role);//向集合保存角色信息
	}
	member.setRoles(allRoles);//一个用户有多个角色
	JPAEntityFactory.getEntityManager().getTransaction().begin();//开启事务
	JPAEntityFactory.getEntityManager().persist(member);//持久化数据
	JPAEntityFactory.getEntityManager().getTransaction().commit();//提交事务
	JPAEntityFactory.close();
}
Hibernate: 
    insert 
    into
        member
        (name, mid) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        member_role
        (mid, rid) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        member_role
        (mid, rid) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        member_role
        (mid, rid) 
    values
        (?, ?)

上述程序实现了用户数据的创建,创建用户的同时为其分配角色。虽然member_role表中只需要一个rid数据即可,但是在JPA开发框架中必须依靠持久类来进行数据设置,所以准备了一个List集合保存角色信息(主要保存的是rid)。在持久化Member对象时,会自动根据关联关系维护member_role表中的数据。

  1. member_role表中只需要一个rid数据即可,但是在JPA开发框架中必须依靠持久类来进行数据设置,所以准备了一个List集合保存角色信息(主要保存的是rid)。在持久化Member对象时,会自动根据关联关系维护member_role表中的数据。
@Test
public void testFindMember(){
	Member member = JPAEntityFactory.getEntityManager().find(Member.class,"xiyuelishui");
	member.getRoles().size();
	JPAEntityFactory.close();
}
Hibernate: 
    select
        member0_.mid as mid1_3_0_,
        member0_.name as name2_3_0_ 
    from
        member member0_ 
    where
        member0_.mid=?
Hibernate: 
    select
        roles0_.mid as mid1_4_0_,
        roles0_.rid as rid2_4_0_,
        role1_.rid as rid1_5_1_,
        role1_.title as title2_5_1_ 
    from
        member_role roles0_ 
    inner join
        role role1_ 
            on roles0_.rid=role1_.rid 
    where
        roles0_.mid=?

上述程序在进行用户对应角色信息查询时(member.getRoles().size()语句发出查询),由于需要通过member_role数据表才可以确定member表与role表的数据关联关系,所以默认使用了内连接方式。
提示:多对多建议使用原生SQL查询。
实际上对于此时的数据表结构,很多情况下开发者需要的可能只是一个角色编号信息(例如,在系统开发进行角色与权限认证时,需要的往往只是一个ID编号),所以在这样的状态下为了提升查询效率,可以使用原生SQL实现角色信息查询。

使用原生SQL查询用户角色信息。

@Test
public void testFindMemberSQL(){
	Member member = JPAEntityFactory.getEntityManager().find(Member.class,"xiyueliushui");
	String sql = "select rid from member_role where mid=:mid";//原生SQL
	System.out.println("***用户ID:"+member.getMid()+"、真实姓名:"+member.getName());
	Query query = JPAEntityFactory.getEntityManager().createNativeQuery(sql);
	query.setParameter("mid", member.getMid());
	List<Object> allRids = query.getResultList();
	System.out.println("***用户角色:"+allRids);
	JPAEntityFactory.close();
}
Hibernate: 
    select
        member0_.mid as mid1_3_0_,
        member0_.name as name2_3_0_ 
    from
        member member0_ 
    where
        member0_.mid=?
***用户ID:xiyueliushui、真实姓名:天穹圣戟
Hibernate: 
    select
        rid 
    from
        member_role 
    where
        mid=?
***用户角色:[company, dept, emp]

虽然本程序没有采用级联关系实现数据加载,但是由于不再采用多表关联的形式查询,整体的执行效率将获得极大提升,所以对于数据关联操作,笔者强烈建议:如果不是必须的情况下不建议使用,更多时候单表处理性能会更佳,而且也更加灵活。

  1. 多对多关联中,还有一个麻烦的问题,就是member_role关系表的数据维护处理。例如,用户角色更新时应先将对应的所有member_role中的数据删除,而后再重新增加member_role表数据。这种操作可以直接利用JPA自动来完成维护。
@Test
public void testMemberEidt() {
	//member_role关联表里是rid信息,下面定义的都是rid内容
	String rids[] = new String[] {"market","project","sale"};//定义角色编号
	//设置一堆的Role对象只是为了获取一个rid属性内容,但是关联关系必须要求产生Role集合对象
	List<Role> allRoles = new ArrayList<Role>();//保存角色信息
	Member member = new Member();//实例化持久类对象
	member.setMid("xiyueliushui");//设置数据
	member.setName("天穹圣戟");//设置数据
	for (int i = 0; i < rids.length; i++) {//配置用户角色
		Role role = new Role();//实例化持久类对象
		role.setRid(rids[i]);//设置角色
		allRoles.add(role);//向集合保存角色信息
	}
	member.setRoles(allRoles);//一个用户有多个角色
	JPAEntityFactory.getEntityManager().getTransaction().begin();//开启事务
	JPAEntityFactory.getEntityManager().merge(member);//持久化数据
	JPAEntityFactory.getEntityManager().getTransaction().commit();//提交事务
	JPAEntityFactory.close();//关闭连接
}
Hibernate: 
    select
        member0_.mid as mid1_3_0_,
        member0_.name as name2_3_0_ 
    from
        member member0_ 
    where
        member0_.mid=?
Hibernate: 
    select
        roles0_.mid as mid1_4_0_,
        roles0_.rid as rid2_4_0_,
        role1_.rid as rid1_5_1_,
        role1_.title as title2_5_1_ 
    from
        member_role roles0_ 
    inner join
        role role1_ 
            on roles0_.rid=role1_.rid 
    where
        roles0_.mid=?
Hibernate: 
    update
        member 
    set
        name=? 
    where
        mid=?
Hibernate: 
    delete 
    from
        member_role 
    where
        mid=?
Hibernate: 
    insert 
    into
        member_role
        (mid, rid) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        member_role
        (mid, rid) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        member_role
        (mid, rid) 
    values
        (?, ?)

本程序涉及的数据表较多,同时维持对象持久化状态,执行了许多次查询后才开始进行相应数据的更新处理。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
JPA 中,如果你想要关联表的部分字段,可以使用 `@JoinColumn` 注解和 `@ManyToOne` 或 `@OneToOne` 注解来定义关联关系。然后,你可以使用 `@JoinColumn` 注解的 `referencedColumnName` 属性来指定关联表中的字段。 下面是一个示例: ```java @Entity @Table(name = "employee") public class Employee { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @ManyToOne @JoinColumn(name = "department_id", referencedColumnName = "id") private Department department; // Getter and Setter methods } @Entity @Table(name = "department") public class Department { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; // Getter and Setter methods } ``` 在上面的示例中,`Employee` 实体类与 `Department` 实体类通过 `department_id` 字段进行关联。如果你只想要获取关联表 `Department` 的部分字段,可以在 `Department` 实体类中定义一个新的类似于 DTO(数据传输对象)的类,该类仅包含需要的字段。 例如,假设你只想要获取部门的名称(字段 `name`),你可以创建一个名为 `DepartmentDto` 的类: ```java public class DepartmentDto { private String name; public DepartmentDto(String name) { this.name = name; } // Getter and Setter methods } ``` 然后,在 `Employee` 实体类中,你可以添加一个返回 `DepartmentDto` 类型的方法,用于获取关联表 `Department` 的部分字段: ```java @Entity @Table(name = "employee") public class Employee { // ... public DepartmentDto getDepartmentName() { return new DepartmentDto(department.getName()); } // ... } ``` 这样,当你查询 `Employee` 实体时,调用 `getDepartmentName()` 方法就可以获取到部门的名称。 请注意,上述示例中的代码仅为演示目的,并未完整展示 JPA 的所有配置和方法。实际使用时,你可能需要根据自己的业务需求进行适当的调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

追Star仙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值