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 |
用一段程序来示范
@Transactional
public void save(){ //New 状态
Task t = new Task();
t.setTaskName("task" + new Date().getTime());
t.setCreateTime(new Date()); //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 class UserRepositoryImpl { @PersistenceContext
private EntityManager entityManager; @Transactional
public void add(User user) {
entityManager.persist(user); }
@Transactional
public User update(User user) {
User userUpdate = entityManager.find(User.class, user.getId());
userUpdate.setAddress(user.getAddress());
userUpdate.setName(user.getName());
userUpdate.setPhone(user.getPhone());
return userUpdate;
}
@Transactional
public User addOrUpdate(User user) {
return entityManager.merge(user);
}
@Transactional
public void delete(User user) {
entityManager.remove(user);
} public User findOne(Integer id) {
return entityManager.find(User.class, id);
} public List<User> findAll() {
String queryString = "select u from User u";
Query query = entityManager.createQuery(queryString);
return query.getResultList();
}
}
3. Spring Data JPA实体基础映射
3.1 表映射
@Entity //表示该类为JPA实体类
@Table(name="t_user") //对应数据库中哪张表
public class User { @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 class User { @Column(name="name_", length=60, nullable=false,unique=true,insertable=false)
private String name; //分表存储
@Column(table = "t_address", name="street_", length = 100)
private String street;
3.5 内嵌实体
在定义实体时可能需要将某几个的属性剥离出放到另外一个实体中,以使程序更有层次感,并且当其他实体也需要这几个属性时,我们也不需要再定义这几个属性,把存放这几个属性的实体重新引用即可,操作方法如下:
@Embeddable //标识该实体可嵌入到其他实体中
public class Comment {
@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"))
})
private Comment comment;
内嵌实体在数据库中不会一点单独的表格存放,而是跟数组实体存放于同一表格中。
3.6 实体类代码
import java.math.BigDecimal;
import java.util.Date;
import javax.persistence.*;
import org.hibernate.annotations.GenericGenerator; @Entity
@Table(name="t_user",catalog="",schema="")
@SecondaryTables({
@SecondaryTable(name = "t_address",pkJoinColumns=@PrimaryKeyJoinColumn(name="address_id"))
})
public class User { @Id //标明主键
@GeneratedValue //主键生成策略
@Column(name="id_")
private Integer 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)
private String name; @Column(name="address_", length=60, nullable=false)
private String address; @Column(name="phone_", length=11, nullable=true,columnDefinition="CHAR(10) default '000'")
private String phone; @Column(name="inCome_", precision=12, scale=2)
private BigDecimal inCome; @Temporal(TemporalType.DATE)
private Date 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)
private String note; //分表存储
@Column(table = "t_address", name="street_", length = 100)
private String street;
//分表存储
@Column(table = "t_address", name="city_")
private String city;
//分表存储
@Column(table = "t_address", name="conutry_",length = 20)
private String 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。
@Entity
public class Person {
//略
@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,三张表存在两个主外键关系。
@Entity
public class Depart {
//略 @OneToMany
@JoinTable(name = "depart_employee", //name:关联表
joinColumns = @JoinColumn(name = "depart_id",referencedColumnName="did"), //joinColumns:关系的拥有者与关联表的关系
inverseJoinColumns = @JoinColumn(name = "employee_id",referencedColumnName="eid"))//inverseJoinColumns:关系的被拥有者与关联表的关系
private List<Employee> employees;
4.2.2 从表增加外键方式
在员工表(employee2)中添加一个depart_id字段,它作为外键关联部门表(depart2)的主键id。
@Entity
public class Depart2 {
//略 @OneToMany
@JoinColumn(name="depart_id",referencedColumnName="id")
private List<Employee2> employee2s;
4.3 多对多实体映射的概念的实现方法
多对多的实现也是通过中间表,方法同一对多的中间表实现方式。
@Entity
public class Teacher {
//略 @ManyToMany
@JoinTable(name = "teacher_student",
joinColumns = @JoinColumn(name = "teacher_id",referencedColumnName="tid"),
inverseJoinColumns = @JoinColumn(name = "student_id",referencedColumnName="sid"))
private List<Student> students;
@Entity
public class Student {
//略 @ManyToMany(mappedBy = "students")
private List<Teacher> teachers;
4.4 级联策略和懒加载
以@OneToOne为例,当我希望删除人员信息时,也将其地址信息删除,则可使用级联策略;当我想要查询人员信息(主实体)时,并不想同时查询出其地址信息(子实体),可以设置懒加载。
@Entity
public class Person {
@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 class Item {
/* @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;
每个类策略实际上是每个类一个表策略,这种策略要求主键不能使用自增的方式,如上面的代码,采用表中获取的方式。
三张表各自存放自己的完整信息,表之间没有任何的关联关系。虽然他们各自存放各自的数据,但主键是连续的。即三个表共用一套主键生成策略(三个表的主键都从另一个表中获取)。
这种策略查询效率高,同时也不存在大量空字段的浪费。
Spring Data JPA实体详解的更多相关文章
- spring data jpa使用详解
https://blog.csdn.net/liuchuanhong1/article/details/52042477 使用Spring data JPA开发已经有一段时间了,这期间学习了一些东西, ...
- Spring Data操作Redis详解
Spring Data操作Redis详解 Redis是一种NOSQL数据库,Key-Value形式对数据进行存储,其中数据可以以内存形式存在,也可以持久化到文件系统.Spring data对Redis ...
- spring+springMVC+JPA配置详解(使用缓存框架ehcache)
SpringMVC是越来越火,自己也弄一个Spring+SpringMVC+JPA的简单框架. 1.搭建环境. 1)下载Spring3.1.2的发布包:Hibernate4.1.7的发布包(没有使用h ...
- Spring Data JPA实体的生命周期总结
目录 四种状态 API示例 persist remove merge refresh 参考链接 四种状态 首先以一张图,简单介绍写实体生命周期中四种状态之间的转换关系: 瞬时(New):瞬时对象,刚N ...
- idea 从数据库快速生成Spring Data JPA实体类
第一步,调出 Persistence 窗口. File—>Project Structure—>model—> + —>JPA 第二步:打开 Persistence窗口 配置 ...
- spring data jpa实体类映射配置
@Entity:用来标志实体类,知名这是一个和数据库表映射的实体类 @Id注解指明这个属性映射为数据库的主键 @GeneratedValue注解默认使用主键生成方式为自增,hibernate会自动生成 ...
- SpringBoot学习笔记:Spring Data Jpa的使用
更多请关注公众号 Spring Data Jpa 简介 JPA JPA(Java Persistence API)意即Java持久化API,是Sun官方在JDK5.0后提出的Java持久化规范(JSR ...
- 【ORM框架】Spring Data JPA(一)-- 入门
本文参考:spring Data JPA入门 [原创]纯干货,Spring-data-jpa详解,全方位介绍 Spring Data JPA系列教程--入门 一.Spring Data JPA介 ...
- Spring Data JPA在Spring Boot中的应用
1.JPA JPA(Java Persistence API)是Sun官方提出的Java持久化规范.它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据.他的出现主要是为了简 ...
随机推荐
- NGUI 3.5教程(一)安装NGUI 3.5.8
写在前面: 网上找的NGUI教程,都是基于2.x版本号的.为了能配合教程学着做,我也是下载了各种NGUI 2.x版本号.可是在导入的时候,或多或少都报错(我用的Unity 的版本号是4.3.2).无奈 ...
- mysql的TABLE_SCHEMA的sql和information_schema表, MySQL管理一些基础SQL语句, Changes in MySQL 5.7.2
3.查看库表的最后mysql修改时间, 如果第一次新建的表可能还没有update_time,所以这里用了ifnull,当update_time为null时用create_time替代 select T ...
- 学习笔记:AC自动机
话说AC自动机有什么用......我想要自动AC机 AC自动机简介: 首先简要介绍一下AC自动机:Aho-Corasick automation,该算法在1975年产生于贝尔实验室,是著名的多模匹配 ...
- OpenCV教程(41) 人脸特征检测
在OpenCV中,自带着Harr分类器人脸特征训练的文件,利用这些文件,我们可以很方面的进行人脸,眼睛,鼻子,表情等的检测. 人脸特征文件目录: ../opencv2.46/op ...
- 遇到Elements in iteration expect to have 'v-bind:key' directives.' 这个错误
解决方式一:更改VS Code编辑器的vetur配置 错误提示: [vue-language-server] Elements in iteration expect to have 'v-bind: ...
- C/C++/Java 程序计时功能函数
编写程序肯定要使用计时功能,来判断程序的执行时间.今天Google了一下,自己就梳理总结一下: (1)C/C++程序计时 C/C++中使用的计时函数是clock(). C语言中的头文件对应是#incl ...
- SQL Server中的database checkpoint
基于性能方面的考虑, 数据库引擎会在内存(buffer cache)中执行数据库数据页(pages)的修改, 不会再每次做完修改之后都把修改了的page写回到磁盘上. 更准确的说, 数据库引擎定期在每 ...
- iOS开发-CoreMotion框架(加速计和陀螺仪)
CoreMotion是一个专门处理Motion的框架,其中包含了两个部分加速度计和陀螺仪,在iOS4之前加速度计是由UIAccelerometer类来负责采集数据,现在一般都是用CoreMotion来 ...
- word 文档如何加密
给Word文档加密主要有以下几个方法:文件加密文件菜单设置:1.打开需要加密的Word文档.2.选“文件”的“另存为”,出现“另存为”对话框,在“工具”中选“常规选项”,出现“保存”选项卡.3.分别在 ...
- [Android系列—] 2. Android 项目文件夹结构与用户界面的创建
前言 在 [Android系列-] 1. Android 开发环境搭建与Hello World 这一篇中介绍了怎样高速搭建Android开发环境, 并成功了建立一个没有不论什么代码更改的 Androi ...