JPA学习笔记-EJB-04JPA关联映射总结

更新时间:2023-07-27 09:00:01 阅读量: 实用文档 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

JPA学习笔记-EJB-04JPA关联映射总结

刘岩

Email:suhuanzheng7784877@

1. 前言

感觉JPA关联映射实现感觉比Hibernate配置文件的关联映射几乎一样,只是引入了“零配置”这个概念。劣者自己在做一个东西的时候用到了关联映射,在此将它做一个总结。留给自己回顾用。

2. 需求

现在遇到这么一个简单的需求,要求做一个小小的用户权限管理系统。不同的系统人员属于不同的角色组,不同的角色组有不同的操作权限(细粒度:显示不同的操作菜单)。那么基于以上需求可以得出这样的业务图。

3. 建模

从以上业务图我们不难得出以下模型。

UxAdmin:代表用户

UxRole:代表角色组

UxSysMenu:代表系统菜单

得出相关类图如下

UxAdmin(用户)

可以看出里面有一个Set<UxRole>,

代表角色实体不能重复,当然判断UxRole是否重复的算法需要在UxRole里自己实现,此为后话暂且不表。

的一个中间桥梁,根据用户找到用户的角色,再根据角色才能找到该用户能使用的操作菜单。并且角色组下的用户和菜单都不能重复。

UxSysMenu(菜单)

其中Set<UxRole>是菜单实体到角色的映射。

4. 实体类代码

有了类图,就有了实体类代码了。

import org.apache.struts2.json.annotations.JSON;

/** * liuyan */ @Entity @Table(name = "ux_admin") public class UxAdmin implements java.io.Serializable { // Fields private Integer id; private String admin; private String password; private Set<UxRole> roles; // Constructors /** default constructor */ public UxAdmin() { } /** minimal constructor */ public UxAdmin(Integer id) { this.id = id; } /** full constructor */ public UxAdmin(Integer id, String admin, String password) { this.id = id; this.admin = admin; this.password = password; } // Property accessors @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", unique = true, nullable = false) public Integer getId() { return this.id; } public void setId(Integer id) {4 / 23

说明1:不要理睬getRoles()的@JSON(serialize = false)注解,这个和JPA无关 说明2:@ManyToMany(fetch = ZY)注解,说明UxAdmin与UxRole是多对多的关系,采用的是默认的懒加载机制,而且从UxRole来看,是双向的多对多关联。所以必须采用专门的维护关系表来维护多对多关联关系。

说明3:@JoinTable( )注解,利用表名为ux_sys_user_role作为关系维护表。UxAdmin实体表用字段adminId作为ux_sys_user_role的外键,用于维护UxAdmin实体。roleId是维护另一个实体——UxRole的roleId字段。之所以用Set作为集合关联,是因为需求不能让用户的角色重复。inverseJoinColumns是定义指向非所有者主表的外键列。

UxSysMenu

this.smenuname = smenuname; this.iparentid = iparentid; this.isvisible = isvisible; this.ilevel = ilevel; } /** full constructor */ public UxSysMenu(Integer imenuid, String smenuname, String smenuurl, Integer iparentid, Integer iorder, String simgico, String isvisible, Integer ilevel) { this.imenuid = imenuid; this.smenuname = smenuname; this.smenuurl = smenuurl; this.iparentid = iparentid; this.iorder = iorder; this.simgico = simgico; this.isvisible = isvisible; this.ilevel = ilevel; } //去掉双向关联映射,为了更新菜单时不影响角色实体记录 private Set<UxRole> uxRoles; @JSON(serialize = false) @ManyToMany(fetch = ZY) @JoinTable(name = "ux_sys_role_menu", joinColumns = { @JoinColumn(name = "menuId") }, inverseJoinColumns = { @JoinColumn(name = "roleId") }) public Set<UxRole> getUxRoles() { return uxRoles; } public voi

d setUxRoles(Set<UxRole> uxRoles) { this.uxRoles = uxRoles; } // Property accessors @Id @Column(name = "imenuid", unique = true, nullable = false) public Integer getImenuid() { return this.imenuid; }

7 / 23

public void setImenuid(Integer imenuid) { this.imenuid = imenuid; } @Column(name = "smenuname", nullable = false, length = 512) public String getSmenuname() { return this.smenuname; } public void setSmenuname(String smenuname) { this.smenuname = smenuname; } @Column(name = "smenuurl", length = 128) public String getSmenuurl() { return this.smenuurl; } public void setSmenuurl(String smenuurl) { this.smenuurl = smenuurl; } @Column(name = "iparentid", nullable = false) public Integer getIparentid() { return this.iparentid; } public void setIparentid(Integer iparentid) { this.iparentid = iparentid; } @Column(name = "iorder") public Integer getIorder() { return this.iorder; } public void setIorder(Integer iorder) { this.iorder = iorder; } @Column(name = "simgico", length = 64) public String getSimgico() { return this.simgico; }8 / 23

public void setSimgico(String simgico) { this.simgico = simgico; } @Column(name = "isvisible", nullable = false, length = 8) public String getIsvisible() { return this.isvisible; } public void setIsvisible(String isvisible) { this.isvisible = isvisible; } @Column(name = "ilevel", nullable = true) public Integer getIlevel() { return this.ilevel; } public void setIlevel(Integer ilevel) { this.ilevel = ilevel; } /** * 是否主键相等 */ @Override public boolean equals(Object obj) { if(obj instanceof UxSysMenu){ UxSysMenu uxSysMenu = (UxSysMenu)obj; if(uxSysMenu.imenuid == this.imenuid){ return true; } } return false; } @Override public int hashCode() { // TODO Auto-generated method stub return this.imenuid;9 / 23

超级父类Object的equals(Object obj)、hashCode()方法,这样在别的管理实体的Set<UxSysMenu>就可以区分集合中的元素哪些实体是重复的。

下面来看角色实体

UxRole

@ManyToMany(fetch = ZY) @JoinTable(name = "ux_sys_role_menu", joinColumns = { @JoinColumn(name = "roleId") }, inverseJoinColumns = { @JoinColumn(name = "menuId") }) private Set<UxSysMenu> menus;

@ManyToMany(fetch = ZY) @JoinTable(name = "ux_sys_user_role", joinColumns = { @JoinColumn(name = "roleId") }, inverseJoinColumns = { @JoinColumn(name = "adminId") }) private Set<UxAdmin> uxAdmins; // Constructors /** default constructor */ public UxRole() { } /** minimal constructor */ public UxRole(Integer id) { this.id = id; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getRoleName() { return roleName; } public void setRoleName(String roleName) { this.roleName = roleName; } public String getRoleDesc() { return roleDesc; }

11 / 23

public void setRoleDesc(String roleDesc) { this.roleDesc = roleDesc; } @JSON(serialize = false) public Set<UxSysMenu> getMenus() { return menus; } public void setMenus(Set<UxSysMenu> menus) { this.menus = menus; } @JSON(serialize = false) public Set<UxAdmin> getUxAdmins() { return uxAdmins; } public void setUxAdmins(Set<UxAdmin> uxAdmins) { this.uxAdmins = uxAdmins; } /** * 是否主键相等 */ @Override public boolean equals(Object obj) { if(obj instanceof UxRole){ UxRole uxRole = (UxRole)obj; if(uxRole.id == this.id){ return true; } } return false; } @Override public int hashCode() { // TODO Auto-generated method stub return this.id; }12 / 23

说明:角色实体双向关联了人员实体和菜单实体,让人员和菜单建立起了一个桥梁,当然,因为人员对应的角色有可能重复,所以在角色类中必须实现equals和hashCode

5. 用户实体管理的增删改查总结

增加、查询、删除操作都和单独操作单表的操作一样,所以可以放心使用JPA接口去操作。

不同1:更新用户本实体信息,首先先将原实体的关联实体集合查询出来,放到临时变量中,之后赋值给欲更新的对象,最后调用更新接口更新主实体,保证主实体与关联实体的关系。

代码如下:

实体,角色作为从属实体。就直接将新的从属实体集合从数据库查询出来,对主实体赋其值,之后更新就好了,如果没有更新的对象,设置为null即可,表示没有任何的角色与该用户关联,用户没有任何角色。

代码如下:

6. 菜单实体管理的增删改查总结

从类图就可以看得出来,用户实体与菜单实体差不多。所以操作也大同小异。

查询、增加、删除操作和单实体操作没什么分别。直接操作即可,关键就是更新记录,和用户实体一样。先将关联集合实体查询出来放到临时变量中,之后给目标更新实体赋值、再调用更新接口更新实体。

代码如下:

7.在角色管理中依然是更新操作区别于其他操作

不同1:单实体信息更新

可以看出也是先将角色关联的菜单集合、用户集合都临时记住,之后再更新主实体。保证关系不会因为角色实体的更新而丢失。

不同2:更新角色的从属菜单关系

代码如下

8. 1 VS N 和N VS 1

说完了最复杂的多对多关系后,剩下的骨头就好啃了。我们就先来看一对多和多对一。 假设目前项目有这样一个需求:建立一个类似足球俱乐部转会的管理系统球员可以转会到其他的俱乐部,而俱乐部看队员不顺眼也可以开除队员,一个队员在一个时刻只能从属于一家俱乐部,一个俱乐部在一个时刻内可以有多个运动员。由此,我们不难得出以下模型。

巴塞罗那,可以知道它有梅西、哈维、伊涅斯塔 ,一提到C罗可以知道他在皇家马德里踢球,一提到厄齐尔也知道他在皇家马德里。

由此我们得到了类图

说明:咱们暂且只关心TeamVO,看出运动员里面有俱乐部实体。

那咱们来看俱乐部类图

俱乐部里面有Set<PlayersVO>,由此可以得出结论,运动员和俱乐部是双向1对多的关系,在运动员这边来看就是多对1(多个人员对应一个俱乐部嘛),从俱乐部这边来看就是1对多关系(1个俱乐部有多个运动员)。

下面是两个类的代码:

import java.util.Set; import javax.persistence.Basic; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.Lob; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.Table; /** * 运动员实体 * * @author 刘岩 */ @Entity @Table(name = "player") public class PlayersVO implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", unique = true, nullable = false) private Integer id; @Column(name = "name") private String name; @ManyToOne(optional=true, fetch = ZY) @JoinColumn(name = "teamId") private TeamVO teamVO; @ManyToOne(fetch = ZY) @JoinColumn(nullable=false,name = "nationalityId") private NationalityVO nationalityVO; @Lob @Basic(fetch = ZY)17 / 23

@Column(name = "pic") private byte[] pic; @Lob @Basic(fetch = ZY) @Column(name = "mess") private String mess; @ManyToMany(cascade = CascadeType.REFRESH, fetch = ZY) @JoinTable(name = "plays_position", joinColumns = { @JoinColumn(name = "playerId") }, inverseJoinColumns = { @JoinColumn(name = "positionId") }) private Set<PositionVO> positions; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { = name; } public Set<PositionVO> getPositions() { return positions; } public void setPositions(Set<PositionVO> positions) { this.positions = positions; } public TeamVO getTeamVO() { return teamVO; } public void setTeamVO(TeamVO teamVO) { this.teamVO = teamVO; }18 / 23

Optional:是否允许该字段为null。(运动员可以没有俱乐部,原拜仁门将——伦辛曾失业半年多呢)。

fetch = ZY:抓取策略为懒加载。

本文来源:https://www.bwwdw.com/article/ixmm.html

Top