jpa 定义中间表实体_Spring Data JPA实体详解

1. Spring Data JPA实体概述

JPA提供了一种简单高效的方式来管理Java对象(POJO)到关系数据库的映射,此类Java对象称为JPA实体或简称实体。实体通常与底层数据库中的单个关系表相关联,每个实体的实例表示数据库表格中的某一行。

2. Spring Data JPA实体管理器

2.1 实体管理器概述

实体管理器(EntityManager)用于管理系统中的实体,它是实体与数据库之间的桥梁,通过调用实体管理器的相关方法可以把实体持久化到数据库中,同时也可以把数据库中的记录打包成实体对象。

2.2 实体管理器的常用方法

2.2.1 实体的四种状态

在此之前我们要先了解实体的状态及其转换,见下图

JPA实体生命周期有四种状态

新建状态(New):对象在保存进数据库之前为临时状态。此时数据库中没有该对象的信息,该对象的ID属性也为空。如果没有被持久化,程序退出时临时状态的对象信息将丢失。

托管状态(Managed):对象在保存进数据库后或者从数据库中加载后、并且没有脱离Session时为持久化状态。这时候数据库中有对象的信息,改对象的id为数据库中对应记录的主键值。由于还在Session中,持久化状态的对象可以执行任何有关数据库的操作,例如获取集合属性的值等。

游离状态(Datached):是对象曾经处于持久化状态、但是现在已经离开Session了。虽然分离状态的对象有id值,有对应的数据库记录,但是已经无法执行有关数据库的操作。例如,读取延迟加载的集合属性,可能会抛出延迟加载异常。

删除状态(Removed):删除的对象,有id值,尚且和Persistence Context有关联,但是已经准备好从数据库中删除。

状态名

作为java对象存在

在实体管理器中存在

在数据库存在

New

Y

N

N

Managed

Y

Y

Y

Datached

N

N

N

Removed

Y

Y

N

用一段程序来示范

@Transactionalpublic voidsave(){//New 状态

Task t = newTask();

t.setTaskName("task" + newDate().getTime());

t.setCreateTime(newDate());//Managed状态

em.persist(t); //实体类t已经有id t.getId();

t.setTaskName("kkk"); //更新任务名称,这时,如果提交事务,则直接将kkk更新到数据库//Detached状态 事务提交或者调用em.clear都直接将实体任务状态变为Detached

em.clear();

t.setTaskName("kkk"); //更新数据不会更新到数据库//Removed状态

em.remove(t);

}

2.2.2  实体管理器的常用方法

对应于实体的四种状态,实体管理器有四种常用的方法,分别是:persist / merge / clear / remove,结合状态图,可以判断,对于不同状态下的实体,各个方法操作结果会有不同:

对于不同状态下的实体,persist 操作结果如下:

新建状态:实体状态迁移到托管状态

托管状态:实体状态不发生改变,但会执行数据库的insert操作

游离状态:方法的调用将会抛出异常信息

删除状态:实体将重返托管状态

对于不同状态下的实体,merge操作结果如下:

新建状态:系统会执行数据库insert操作,同时返回一个托管状态的实体

托管状态:实体状态不发生改变

游离状态:系统将实体的修改保存到数据库,同时返会一个托管状态的实体

删除状态:方法调用将抛出异常

对于不同状态下的实体,refresh 操作结果如下:

新建状态:系统会执行数据库insert操作,同时返回一个托管状态的实体

托管状态:实体状态不发生改变,但会执行数据库的update操作

游离状态:实体状态将返回托管状态

删除状态:方法调用将抛出异常

对于不同状态下的实体,remove 操作结果如下:

新建状态:方法调用将抛出异常

托管状态:实体状态变成删除状态

分离状态:方法调用将抛出异常

删除状态:不发生任何操作

2.2.3 利用实体管理器管理实体(实现实体的CURD)

public classUserRepositoryImpl {

@PersistenceContextprivateEntityManager entityManager;

@Transactionalpublic voidadd(User user) {

entityManager.persist(user);

}

@TransactionalpublicUser update(User user) {

User userUpdate= entityManager.find(User.class, user.getId());

userUpdate.setAddress(user.getAddress());

userUpdate.setName(user.getName());

userUpdate.setPhone(user.getPhone());returnuserUpdate;

}

@TransactionalpublicUser addOrUpdate(User user) {returnentityManager.merge(user);

}

@Transactionalpublic voiddelete(User user) {

entityManager.remove(user);

}publicUser findOne(Integer id) {return entityManager.find(User.class, id);

}public ListfindAll() {

String queryString= "select u from User u";

Query query=entityManager.createQuery(queryString);returnquery.getResultList();

}

}

3. Spring Data JPA实体基础映射

3.1 表映射

@Entity //表示该类为JPA实体类

@Table(name="t_user") //对应数据库中哪张表

public classUser {

@Column(name="phone_", length=11, nullable=true,columnDefinition="CHAR(10) default '000'") //对应数据库表中哪个列字段及对该字段的自定义

private String phone;

3.2 主键映射

@Id //标明主键

@GeneratedValue //主键生成策略

@Column(name="id_")private Integer id;

更多的主键生成策略,详见3.6 的总体代码

3.3 字段映射和约束条件

@Column(name="phone_", length=11, nullable=true,columnDefinition="CHAR(10) default '000'") //对应数据库中哪个列及对该字段的自定义

private String phone;

3.4 单实体多表格存储

通常一个实体对应于一个表格,即表格中的所有的实体属性都存放于一张表,如果将实体的属性分配到多个表格存放,就涉及到单实体多表格存储

@Entity

@Table(name="t_user",catalog="",schema="")

@SecondaryTables({//指明存放的第二张表

@SecondaryTable(name = "t_address",pkJoinColumns=@PrimaryKeyJoinColumn(name="address_id"))

})public classUser {

@Column(name="name_", length=60, nullable=false,unique=true,insertable=false)privateString name;//分表存储

@Column(table = "t_address", name="street_", length = 100)private String street;

3.5 内嵌实体

在定义实体时可能需要将某几个的属性剥离出放到另外一个实体中,以使程序更有层次感,并且当其他实体也需要这几个属性时,我们也不需要再定义这几个属性,把存放这几个属性的实体重新引用即可,操作方法如下:

@Embeddable //标识该实体可嵌入到其他实体中

public classComment {

@Column(name="title_",length=100)

String title;

@Column(name="content_")

String content;

/*//被剥离出的属性

@Column(name="title_",length=100)

String title;

@Column(name="content_")

String content;*/@Embedded//引入该实体

@AttributeOverrides({ //罗列出所有需要重新命名的属性

@AttributeOverride(name = "title", column = @Column(name = "user_title")),

@AttributeOverride(name= "content", column = @Column(name = "user_content"))

})privateComment comment;

内嵌实体在数据库中不会一点单独的表格存放,而是跟数组实体存放于同一表格中。

3.6 实体类代码

importjava.math.BigDecimal;importjava.util.Date;import javax.persistence.*;importorg.hibernate.annotations.GenericGenerator;

@Entity

@Table(name="t_user",catalog="",schema="")

@SecondaryTables({

@SecondaryTable(name= "t_address",pkJoinColumns=@PrimaryKeyJoinColumn(name="address_id"))

})public classUser {

@Id//标明主键

@GeneratedValue //主键生成策略

@Column(name="id_")privateInteger id;/*@Id

@GeneratedValue(generator="uuidGenerator")

@GenericGenerator(name="uuidGenerator",strategy="uuid")

@Column(name="id_",length=32)

private String id;*/

/*@Id

@GeneratedValue(strategy = GenerationType.AUTO)

@Column(name="id_")

private Integer id;*/

/**

@Id

@GeneratedValue(strategy=GenerationType.IDENTITY)

@Column(name="id_")

private Integer id;*/

/*@Id

@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "idGenerator")

@SequenceGenerator(name = "idGenerator",sequenceName="mySeq",allocationSize=1)

@Column(name="id_")

private Integer id;*/

/*@Id

@GeneratedValue(strategy = GenerationType.TABLE, generator = "userGenerator")

@TableGenerator(name = "userGenerator",table="pk_generator",

pkColumnName="gen_name",

valueColumnName="gen_value",

pkColumnValue="user_pk",

initialValue=0,

allocationSize=1)

@Column(name="id_")

private Integer id;*/@Column(name="name_", length=60, nullable=false,unique=true,insertable=false)privateString name;

@Column(name="address_", length=60, nullable=false)privateString address;

@Column(name="phone_", length=11, nullable=true,columnDefinition="CHAR(10) default '000'")privateString phone;

@Column(name="inCome_", precision=12, scale=2)privateBigDecimal inCome;

@Temporal(TemporalType.DATE)privateDate birthday;//Date 日期型,精确到年月日,例如“2008-08-08”//Time 时间型,精确到时分秒,例如“20:00:00”//Timestamp 时间戳,精确到纳秒,例如“2008-08-08 20:00:00.000000001”

@Lob

@Column(name="pic_")

@Basic(fetch=FetchType.LAZY)private byte[] pic;

@Lob

@Column(name="note_")

@Basic(fetch=FetchType.LAZY)privateString note;//分表存储

@Column(table = "t_address", name="street_", length = 100)privateString street;//分表存储

@Column(table = "t_address", name="city_")privateString city;//分表存储

@Column(table = "t_address", name="conutry_",length = 20)privateString conutry;

@Column(name="title_",length=100)

String title;

@Column(name="content_")

String content;/*@Embedded //引入该实体

@AttributeOverrides({ //罗列出所有需要重新命名的属性

@AttributeOverride(name = "title", column = @Column(name = "user_title")),

@AttributeOverride(name = "content", column = @Column(name = "user_content"))

})

private Comment comment;*/

//省略get/set方法

}

4. Spring Data JPA实体高级映射

4.1 一对一实体映射的概念和实现方法

如下例,人员表(person)和地址表(adddress),person表是关系的拥有者,表中的address_id字段关联着address表的主键id。

@Entitypublic classPerson {//略

@OneToOne

@JoinColumn(name="address_id",referencedColumnName="aid")//name:主表的外键字段; referencedColumnName:从表的主键//如果关联的字段有多个,采用如下注解//@JoinColumns(value={@JoinColumn(name="address_id",referencedColumnName="aid"),@JoinColumn(name="address_id2",referencedColumnName="aid2")})

private Address address;

4.2 一对多实体映射的概念和实现方法

部门表(depart)和员工表(employee),一个部门可以有多个员工,一对多关系可以采用如下两种实现方法。

4.2.1 中间表方式

创建中间表(depart_employee),表中存放两个表的主键。通过部门id可查询关联员工的id,三张表存在两个主外键关系。

@Entitypublic classDepart {//略

@OneToMany

@JoinTable(name= "depart_employee", //name:关联表

joinColumns = @JoinColumn(name = "depart_id",referencedColumnName="did"), //joinColumns:关系的拥有者与关联表的关系

inverseJoinColumns = @JoinColumn(name = "employee_id",referencedColumnName="eid"))//inverseJoinColumns:关系的被拥有者与关联表的关系

private List employees;

4.2.2 从表增加外键方式

在员工表(employee2)中添加一个depart_id字段,它作为外键关联部门表(depart2)的主键id。

@Entitypublic classDepart2 {//略

@OneToMany

@JoinColumn(name="depart_id",referencedColumnName="id")private List employee2s;

4.3 多对多实体映射的概念的实现方法

多对多的实现也是通过中间表,方法同一对多的中间表实现方式。

@Entitypublic classTeacher {//略

@ManyToMany

@JoinTable(name= "teacher_student",

joinColumns= @JoinColumn(name = "teacher_id",referencedColumnName="tid"),

inverseJoinColumns= @JoinColumn(name = "student_id",referencedColumnName="sid"))private Liststudents;

@Entitypublic classStudent {//略

@ManyToMany(mappedBy= "students")private List teachers;

4.4 级联策略和懒加载

以@OneToOne为例,当我希望删除人员信息时,也将其地址信息删除,则可使用级联策略;当我想要查询人员信息(主实体)时,并不想同时查询出其地址信息(子实体),可以设置懒加载。

@Entitypublic classPerson {

@OneToOne(cascade={CascadeType.REFRESH,CascadeType.REMOVE},fetch=FetchType.LAZY)//@JoinColumn(name="address_id",referencedColumnName="aid")

private Address address;

5. Spring Data JPA实体继承

5.1 实体继承的概念

继承[extends]想必已不陌生,对于JPA来说,我们不但要考虑如何实现Java端的继承关系,还要考虑如何持久化到数据库中。JPA为此提供了三种策略,如下:

5.2 实体继承策略

继承关系如图,继承策略的注解主要应用于父类Item。

5.2.1 继承策略之单一表策略

@Entity

@Inheritance(strategy=InheritanceType.SINGLE_TABLE)public class Item {

执行单一表策略会将所有实体的信息存放于一张表中,它的优点是信息存放于一张表,查询效率较高,缺点是大量字段为空,浪费存储空间。

如果类名过长或需要更改鉴别字段的名称,可对鉴别字段及可选值自定义:

@Entity

@Inheritance(strategy=InheritanceType.SINGLE_TABLE)

@DiscriminatorColumn(name="ITYPE",discriminatorType=discriminatorType.CHAR) //声明鉴别字段的字段名,类型

@DiscriminatorValue("I") //该表在鉴别字段列显示的值

public class Item {

@Entity

@DiscriminatorValue("P")public class Phone extends Item {

@Entity

@DiscriminatorValue("B")public class Book extends Item {

效果如下

5.2.2 继承策略之连接表策略

@Entity

@Inheritance(strategy=InheritanceType.JOINED)public class Item {

连接表策略会生成三张表,通过共享主键彼此关联。

这种策略避免了空字段的浪费,但由于采用表关联查询,当数据量过大时,查询效率较低。

5.2.3 继承策略之每个类策略

@Entity

@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)public classItem {/*@Id

@GeneratedValue(strategy = GenerationType.AUTO)*/@Id

@GeneratedValue(strategy= GenerationType.TABLE, generator = "ItemGenerator")

@TableGenerator(name= "ItemGenerator",table="pk_generator",

pkColumnName="gen_name",

valueColumnName="gen_value",

pkColumnValue="item_pk",

initialValue=0,

allocationSize=1)private Long id;

每个类策略实际上是每个类一个表策略,这种策略要求主键不能使用自增的方式,如上面的代码,采用表中获取的方式。

三张表各自存放自己的完整信息,表之间没有任何的关联关系。虽然他们各自存放各自的数据,但主键是连续的。即三个表共用一套主键生成策略(三个表的主键都从另一个表中获取)。

这种策略查询效率高,同时也不存在大量空字段的浪费。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/298639.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

为什么魂斗罗只有128KB却可以实现那么长的剧情?

全世界只有3.14 % 的人关注了爆炸吧知识现代程序员A和1980年代游戏程序员B的对话:A:为什么你用128KB能实现这么多画面、音乐、动画?B:128KB还不够么?其实为了表现力已经相当奢侈了,加了很多不重要的细节。A…

asp.net core 6 新特性,支持HTTP/3 端点发布

???? 序言Http3 协议构建在UDP的基础上,是的,就这么神奇,以前被誉为不稳定大神的UDP,现在承担起可靠通讯协议的底层协议。为了消除UDP的不确定性,在UDP协议之上,新增了QUIC协议。使用QUIC协议代替TCP协…

imac android studio,Mac安装Android Studio的时候忘记安装Avd Manager怎么办?

工作最近得闲,准备学下React-Native。MAC下安装Android Studio的时候,忘记安装Avd Manager。无奈之下删除Android Studio准备重装,却发现有很多配置文件残留在系统上,导致再次重装的时候完全跳过了Avd Manager的配置。在网上找了一…

浮点数赋值给整数_初学者专题:变量和赋值

作者:老齐对于初学Python者,除了看书(《跟老齐学Python:轻松入门》或者《Python大学实用教程》,均为电子工业出版社出版)、或者看视频(网易云课堂、CSDN上均有老齐的视频课程),还要进行专题性总结。比如本文&#xff0…

分析android动画模块[转]

2019独角兽企业重金招聘Python工程师标准>>> 作者: 刘鹏 日期: 2009-03-07 Android 平台提供了两类动画,一类是 Tween 动画,即通过对场景里的对象不断做图像变换(平移、缩放、旋转)产生动画效果;第二类是 Frame 动画,即…

[转载]锦上添花Sybase数据库题目大网罗-4

Q.如何点窜IP地点窜改后的interfaces文件? A.布置SYBASE Adapive Server的呆滞IP地点窜改后, 应点窜 interfaces 文件及有关的设置。   假设 interfaces 文件中哄骗的是呆滞名而不是 IP 地点, 则不需求变化。 但假设客户端连合效力器使   用的是效力器的 转载于…

Nginx+Tomcat动静态资源分离

1 创建用户、用户组 useradd -g users www passwd www //设置密码,否则该用户不可用 groupadd -g 888 www //创建用户组 gpasswd -a www www //将用户www加入用户组www 2 下载nginx、pcre(正则使用) wget http://blog.s135.com/soft/linux/nginx_php/nginx/nginx-0.…

当你的手被502粘住了要怎么做?

1 看清楚,这是大裤衩子▼这才是 jk 裙▼2 天天看你们骂海王男友其实我有一个河神男友▼3 当你的手被502粘住了怎么办?▼4 不能指望食堂阿姨了自己动手丰衣足食▼5 当你被别人夸奖时▼6 宅在家里也能拍大片▼7 是我本人没错了▼你点的每个赞&#…

excel 如何替换带上标的文字_如何在Excel中批量提取中文汉字和英文字母

1. 在数据源的右边分别命名新的两列为“中文名”和“英文名”,然后将数据源复制到“中文名”这一列。2. 为了确保英文名字可以连在一起,这里我们在有空格的英文名字中间添加一个“-”。选择“中文名”下所有数据源,按CtrlF,点击“…

国内最大.NET平台重金招募中 你竟然还不知道?

各位路过的捎带眼瞅见的不小心翻到的英雄好汉请留步~让小编来带你康康这个胆敢号称国内最大的.NET的平台到底有何惊人之处一难道是因为它有--“最敢”的生态开放平台技术驱动商业创新,数字引领企业的未来,明源云通过多年专业积累,融合多种创新…

GChemPaint-绘制化学分子布局

Toy Posted in AppsRSSTrackbackGChemPaint 是专门用来绘制 2D 的化学分子布局软件,它合用于 GNOME 桌面。经由历程它所绘制的化学分子布局可以被嵌入到其他的应用程序中。Download GChemPaint 0.7.91版权声明: 原创作品,允许转载&#xff0c…

JAVA编程经验汇总 (载)

1. 关于动态加载机制?? 学习Java比C更容易理解OOP的思想,毕竟C还混合了不少面向过程的成分。很多人都能背出来Java语言的特点,所谓的动态加载机制等等。当然概念往往是先记住而后消化的,可有多少人真正去体会过动态加载的机制&a…

软件开发有哪些规范和标准_深圳APP软件开发的流程是什么?

深圳APP软件开发的流程是什么?APP软件开发是根据用户需求构建软件系统或设备的软件部分的过程,是一项系统工程,包括需求捕获,需求分析,系统设计,系统实现和系统测试。APP软件通常以某种编程语言实现&#x…

第十六周项目3-有相同数字?

输入两个数组中要存放的元素个数及元素值(不超过50个),判断这两个数组中是否有相同的数字。 当两组数中有相同数字时,输出YES,否则,输出NO /* * Copyright (c) 2014,烟台大学计算机学院 * All right reserv…

一键对频对讲机好吗_对讲机的基础知识你都知道多少呢?

对讲机或PMR是手持式便携式无线电,可在单个共享频带上使用无线电波进行无线通信。这使Walkie Talkies成为以低成本与大范围人群保持联系的最有效方法之一。双向无线电功能强大,易于使用且易于使用。它们特别适合在户外或在手机或平板电脑过于精致或超出信…

女儿学会走路了,是不是该教她学Python了?

1 哈哈哈,20个人其实都是精准客户▼2 搬沙发一定不要穿的太正式!▼3 横竖都是死让我先打完这场游戏先▼4 只要你学会这支舞你就是你妈的好朋友▼5 你能体会到这只阿拉斯加的惆怅吗?▼6 这双鞋一看就是全球限量有钱都买不到的那种▼7 我…

html游戏怎么做,HTML5 制做“游戏”的一个基本流程

怎样用HTML5 Canvas制作一个简单的游戏为了让大家清楚HTML5制作游戏的简单流程,所以先了制作一个非常简单的游戏,来看一看这个过程。游戏非常简单,无非就是英雄抓住怪物就得分,然后游戏重新开始,怪物出现在地图的随机位…

.NET 6 的 docker 镜像可以有多小

.NET 6 的 docker 镜像可以有多小?Intro最近写了一个小玩具,一个命令行调用 HTTP API 的工具,介绍可以参考:动手造轮子 —— dotnet-HTTPie,最近在使用 System.CommandLine 重构的同时,也在尝试减少 docker 镜像的大小…

开源播放器 Banshee 发布 1.0 beta 2

Banshee也是个很是不错的开源播放器,同时也是SuSE的内置播放器,默许支持靠山播放,消息区域告诉,可以快速跳过以后曲目,评分等等,也支持全局热键,媒体库支持音频和视频.新版本提供了对网络视频和PodCast的优秀支持.SuSE/Ubuntu/Fedora Linux用户可以从源中直接安置,也可以下载安…

仓库每天的账怎样做_新年第一站,济南:仓储匠人仓库问题解决与实战力培训...

【时间地点】1月15-16日,济南站济南市天桥区无影山北路88号祥云酒店5楼(师范路西口)【报名方式】直接加微信号“taishan33976”,注明姓名公司名仓储匠人。如果您认识小编的任意一位同事,欢迎直接找他们报名。【培训价格】1960元/人&#xff0…