详解SpringBoot JPA常用注解的使用方法
作者:zhiguo.zheng 发布时间:2023-12-09 17:10:31
1. 简介
Jpa 是一套ORM 的规范
hibernate 不就是一个 ORM 框架也提供了对于 JPA 的实现
JPA(Java Persistence API):java 持久化 API
2. 常用注解
2.1 @Entity
标注当前类为实体类,将映射到指定的数据库表中
@Entity
public class Users {
}
2.2 @Table
一般与 @Entity 注解一起使用,如果数据库表名和类名一致时不使用 @Table 注解也是可以的,
否则需要使用 @Table 注解来指定表名
@Entity
@Table(name="t_users")
public class Users {
}
2.3 @Id 、@GeneratedValue、@SequenceGenerator、@Column
2.3.1 @Id
用于将实体类的属性映射为主键
2.3.2 @GeneratedValue
指定主键生成策略
package javax.persistence;
/**
* 策略类型
*/
public enum GenerationType {
/**
* 通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植
*/
TABLE,
/**
* 通过序列产生主键,通过 @SequenceGenerator 注解指定序列名
* MySql 不支持这种方式
* Oracle 支持
*/
SEQUENCE,
/**
* 采用数据库 ID自增长的方式来自增主键字段
* Oracle 不支持这种方式;
*/
IDENTITY,
/**
* 缺省值,JPA 根据数据库自动选择
*/
AUTO;
private GenerationType() {
}
}
2.3.3 @SequenceGenerator
2.3.4 @Column
当实体类属性名和数据库列名不一致时必须要使用此注解
@Entity
@Table(name="t_users")
public class Users {
@Id
@Column(name = "user_id")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_seq")
@SequenceGenerator(name = "user_seq", sequenceName = "user_seq")
private Long userId;
}
2.4 @Transient
表示当前属性无需映射到数据库中
2.5 @Temproal
主要针对 Date 类型的属性使用,可以通过该注解指定时间的精度
@Entity
@Table(name="t_users")
public class Users {
@Temporal(TemporalType.DATE)
private Date time1;
@Temporal(TemporalType.TIME)
private Date time2;
@Temporal(TemporalType.TIMESTAMP)
private Date time3;
}
3. EntityManagerFactory
类似与 hibernate 的 SessionFactory
4. EntityManager 实体的四种状态
新建状态: 新创建还未拥有持久性主键持久化状态: 已经拥有持久性主键并和持久化建立了上下文关系游离状态: 拥有持久性主键,但没有和持久化建立上下文关系删除状态: 拥有持久性主键,并且和持久化建立了上下文关系,但是从数据库中删除了
4.1 find(Class entityClass, Object primaryKey)
类似于 hibernate 中 session 的 get()
find 如果没有查询到会返回 null
4.2 getReference(Class entityClass, Object primaryKey)
类似与 hibernate 中 session 的 load()
只有当真正获取对象中的属性时,才会去执行查询的 sql 语句,getReference() 只是返回了一个代理对象
getReference 如果没有查询到不会返回 null , 会抛出 EntityNotFoundException
注意:使用此方法可能出现懒加载异常的情况,也就是我们还没有去获取实体类中的属性值,结果 EntityManager 就已经被关闭了
4.3 persist
类似与 hibernate 中 session 的 save()
注意:执行方法时传入的对象不能为主键设置值会抛出异常
4.4 remove
类似与 hibernate 中 session 的 delete()
注意:该方法只能删除持久化对象,而不能删除游离状态的对象(hibernate 可以)
/**
* 删除游离态(失败)
*/
public void testRemove(){
Users user = new Users();
Users.setUserId(1);
entityManager.remove(customer);
}
/**
* 删除持久化状态(成功)
*/
public void testRemove(){
Users user = entityManager.find(Users.class, 1);
entityManager.remove(user);
}
4.5 merge(T entity)
类似与 hibernate 中 session 的 saveOrUpdate()
// 新建状态
public void testMerge(){
Users user= new Users();
// 省略一系列的set
// user.set.....
Users newUser = entityManager.merge(user);
// user.getUserId() == null ==> true
// newUser.getUserId() == null ==> false
}
4.6 flush()
类似与 hibernate 中 session 的 flush()
将上下文中所有未保存的实体保存到数据库中
4.6 refresh()
类似与 hibernate 中 session 的 refresh()
刷新所有实体的属性值
5. EntityTransaction
EntityManager.getTransaction()
5.1 begin
5.2 commit
5.3 rollback
6. 映射关系
6.1 单向一对多
以用户和订单之间的关系为例,一个用户有多个订单,一个订单只属于一个用户
对于一对多关系的 insert,无论是先插入多的一方还是一的一方都会产生额外的 update 语句,因为多的一端在 insert 时不会插入外键的列
/**
* 订单和用户是多对一的关系
*/
@Entity
@Table(name="t_order")
public class Order {
// lazy为懒加载,默认为eager立即查询
@ManyToOne(fetch=FetchType.Lazy)
// @JoinColumn标注字段是一个类,userId为该类的主键
@JoinColumn(name="user_id")
private Users user;
}
6.2 单向多对一
以用户和订单之间的关系为例,一个用户有多个订单,一个订单只属于一个用户
对于多对一关系的 insert,最好先保存一的一端然后在保存多的一端。
如果先保存多的一端再保存一的一端,为了维护外键的关系,需要对多的一端进行额外的update的操作
/**
* 订单和用户是多对一的关系
*/
@Entity
@Table(name="t_order")
public class Order {
// lazy为懒加载,默认为eager立即查询
@ManyToOne(fetch=FetchType.Lazy)
// @JoinColumn标注字段是一个类,userId为该类的主键
@JoinColumn(name="user_id")
private Users user;
}
6.3 双向多对一
以用户和订单之间的关系为例,一个用户有多个订单,一个订单只属于一个用户
双向多对一就是以上两个的结合,同时使用 @OneToMany 和 @ManyToOne
/**
* 用户和订单是一对多的关系
*/
@Entity
@Table(name="t_users")
public class User {
// 如果两侧都要描述关联关系的话,维护关联关系的任务要交给多的一方
// 使用 @OneToMany 了 mappedBy 的代表不维护关联关系,也就是不会产生额外的update语句
// @OneToMany 和 @JoinColumn 不能同时使用会报错
@OneToMany(mappedBy="user")
private Set<Orders> orders;
}
/**
* 订单和用户是多对一的关系
*/
@Entity
@Table(name="t_orders")
public class Order {
// lazy为懒加载,默认为eager立即查询
@ManyToOne(fetch=FetchType.Lazy)
// @JoinColumn标注字段是一个类,userId为该类的主键
@JoinColumn(name="user_id")
private Users user;
}
6.4 双向一对一
以学校和校长之间的关系为例,一个学校只有一个校长,一个校长也只属于一个学校
一方使用 @OneToMany + @JoinColumn,另一方使用 @OneToOne(mappedBy=“xx”)
具体由哪一方维护关联关系都可以,这里我们以学校一端维护关联关系为例
保存时先保存不维护关联关系的一方(也就是使用@OneToOne(mappedBy=“xx”)的一方),否则会产生额外的 update 语句
/**
* 学校
*/
@Entity
@Table(name="t_school")
public class School {
// 默认为eager立即查询
@OneToOne
// 添加唯一约束
@JoinColumn(name="school_master_id", unique = true)
private SchoolMaster schoolMaster;
}
/**
* 校长
*/
@Entity
@Table(name="t_school_master")
public class SchoolMaster {
// 不维护关联关系要使用 mappedBy
@OneToOne(mappedBy="schoolMaster")
private School school;
}
6.5 双向多对多
以学生和课程之间的关系为例,一个学生可以选多门课,一个课程也有多个学生,多对多需要一个中间表,也就是选课表
维护关联关系的一方需要使用 @JoinTable
关联关系也是只有一方维护即可,这里我们由学生表进行维护
/**
* 学生
*/
@Entity
@Table(name="t_student")
public class Student {
@GeneratedValue
@Id
private Long student_id;
// 要使用 set 集合接收
// 默认为lazy懒加载
@ManyToMany
// name 为中间表的表名
@JoinTable(name="t_student_choose_course",
// name 为与中间表与当前表所关联的字段的名称,referencedColumnName 为当前表中与中间表关联的字段的名称
joinColumns={@JoinColumn(name="student_id", referencedColumnName="student_id")},
// name 为与中间表与多对多另一方表所关联的字段的名称,referencedColumnName 为多对多另一方与中间表关联的字段的名称
inverseJoinColumns={@JoinColumn(name="course_id", referencedColumnName="course_id")})
private Set<Course> courses;
}
/**
* 课程
*/
@Entity
@Table(name="t_course")
public class Course {
@GeneratedValue
@Id
private Long course_id;
// 要使用 set 集合接收
// 默认为lazy懒加载
@ManyToMany(mappedBy="courses")
private Set<Student> students;
}
7. 二级缓存
开启了二级缓存之后,缓存是可以跨越 EntityManager 的,
默认是一级缓存也就是在一个 EntityManager 中是有缓存的
二级缓存可以实现,关闭了 EntityManager 之后缓存不会被清除
使用 @Cacheable(true) 开启二级缓存
8. JPQL
8.1 查询接口
8.1.1 createQuery
public void testCreateQuery(){
// 这里我们使用了一个 new Student,因为我们是查询 Student 中的部分属性,如果不适用 new Student 查询返回的结果就不是 Student 类型而是一个 Object[] 类型的 List
// 也可以在实体类中创建对应的构造器,然后使用如下这种 new Student 的方式,来把返回结果封装为Student 对象
String jpql = "SELECT new Student(s.name, s.age) FROM t_student s WHERE s.student_id > ?";
// setParameter 时下标是从1开始的
List result = entityManager.createQuery(jpql).setParameter(1, 1).getResultList();
}
8.1.2 createNamedQuery
需要在类上使用 @NamedQuery 注解,事先声明 sql 语句
@NamedQuery(name="testNamedQuery", query="select * from t_student WHERE student_id = ?")
@Entity
@Table(name="t_student")
public class Student {
@GeneratedValue
@Id
private Long student_id;
@Column
private String name;
@Column
private int age;
}
public void testCreateNamedQuery(){
Query query = entityManager.createNamedQuery("testNamedQuery").setParameter(1, 3);
Student student = (Student) query.getSingleResult();
}
8.1.3 createNativeQuery
public void testCreateNativeQuery(){
// 本地sql的意思是只能在数据库中执行的sql语句
String sql = "SELECT age FROM t_student WHERE student_id = ?";
Query query = entityManager.createNativeQuery(sql).setParameter(1, 18);
Object result = query.getSingleResult();
}
8.2 关联查询
存在一对多关系时,当我们查询一的一端时,默认多的一端是懒加载。此时我们如果想要一次性查询出所有的数据就需要使用关联查询
注意: 下面 sql 中的重点就是要加上 fetch u.orders,表示要查询出用户所关联的所有订单
public void testLeftOuterJoinFetch(){
String jpql = "FROM t_users u LEFT OUTER JOIN FETCH u.orders WHERE u.id = ?";
Users user = (Users) entityManager.createQuery(jpql).setParameter(1, 123).getSingleResult();
}
来源:https://blog.csdn.net/showadwalker/article/details/124513927
猜你喜欢
- 原文地址:http://www.javayihao.top/detail/84一:概述由于springboot项目,不管是java工程还是w
- 分页是Java Web项目常用的功能,昨天在Spring MVC中实现了简单的分页操作和搜索分页,在此记录一下。使用的框架为(MyBatis
- 这几天恰好和朋友谈起了递归,忽然发现不少朋友对于“尾递归”的概念比较模糊,网上搜索一番也没有发现讲解地完整详细的资料,于是写了这么一篇文章,
- 本文实例为大家分享了Android实现录音声波图的具体代码,供大家参考,具体内容如下图像类:package com.akm.test;/**
- 本文实例为大家分享了C#请求http向网页发送数据、网页接收,供大家参考,具体内容如下首先,我们需要的是什么东西?用POST方式请求http
- HashMap相同key累加valueimport java.util.HashMap;import java.util.Map;publi
- 前言但是没有合理的架构,大家写出来的代码很可能是一大堆的复制粘贴。比如十几个页面,都有这个关注按钮。然后,你是不是也要写十几个地方呢 然后修
- 1、SpringMVC验证@Validated的使用第一步:编写国际化消息资源文件编写国际化消息资源ValidatedMessage.pro
- 金山公司面试题:一个字符串中可能包含a~z中的多个字符,如有重复,如String data="aavzcadfdsfsdhshgW
- 下载maven 解压路径: 打开环境变量:右键此电脑-属性-高级系统设置-高级-环境变量添加以下系统变量:测试:win+
- 网络应用分为客户端和服务端两部分,而Socket类是负责处理客户端通信的Java类。通过这个类可以连接到指定IP或域名的服务器上,并且可以和
- 1.MyBatisX插件在使用mybatis或者mybatis-plus时,我们可以安装IDEA的MyBatis的插件 - MyBatisX
- RocketMQ发送消息我们在使用RocketMQ发送消息时,一般都会使用DefaultMQProducer,类型的代码如下:Default
- C#定义多行字符串的方式在定义的前面加上@符号: string aa = @"asdfsdfsd &n
- 本文研究的主要是Java中后台线程的相关问题,具体介绍如下。以前从来没有听说过,java中有后台线程这种东西。一般来说,JVM(JAVA虚拟
- 这篇文章主要介绍了Spring @Transactional注解失效解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的
- 一、进程与线程的概念(1)在传统的操作系统中,程序并不能独立运行,作为资源分配和独立运行的基本单位都是进程。在未配置 OS 的系统中,程序的
- 方法一:需要调用win32api,winform、wpf通用[DllImport("user32.dll")]publi
- 最近正好也没什么可忙的,就回过头来鼓捣过去的知识点,到Servlet部分时,以前学习的时候硬是把从上到下的继承关系和接口实现记得乱七八糟。这
- 今天在开发的过程中,遇到java.lang.ExceptionInInitializerError异常,百度查了一下,顺便学习学习,做个笔记