言成言成啊 | Kit Chen's Blog

JPA换个角度实现持久层

源码

一、ORM

1.1 回顾JDBC

在以往的与数据库的交互中,我们一般是如下图操作的

问题

  1. 操作繁琐
  2. 占位符赋值麻烦

那么如何解决呢?

  1. 建立实体类和表的关系
  2. 建立实体类中属性和表中字段的关系

这样,我们就可以直接通过调用方法,来实现增删改查。比如增加

1
obj.save(user);// 相当于执行 insert into user values(user.getId(),user.getName);

1.2 ORM思想

思想:操作实体类就相当于操作数据库表

建立两个映射关系

  1. 实体类和表的映射关系
  2. 实体类中属性和表中字段的映射关系

好处:不用关注SQL语句

目前市面上,有好多实现ORM思想的框架

  1. mybatis
  2. hibernate
  3. jpa

1.3 Hibernate框架

Hibernate是一个开放源代码的对象关系映射框架。

它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架。

Hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。

二、JPA概述

2.1 JPA规范

市面上有很多ORM框架,Sun公司为了实现统一,自己定义了一套ORM规范,就叫JPA(Java Persistence API)。内部是由接口和抽象类组成。

JDBC规范,如果要操纵数据库,需要用到驱动,具体实现的逻辑由驱动提供。JDBC规范的好处是,如果我们更换了数据库,只需更换数据库驱动即可,Java代码仍然可以复用。

JPA规范,实现方式有Hibernate、topLink等。JPA规范的好处是,更换实现方式,代码仍然可以复用,只需要修改实现方式即可。

2.2 JPA优势

优势

  1. 任何符合JPA标准的框架都遵循同样的架构,提供相同的访问API,这保证了基于JPA开发的应用经过少量的修改就能在不同的JPA框架下运行。
  2. JPA框架支持大数据集、事务、并发等容器级事务。
  3. JPA的查询语言是面向对象而非面向数据库的,它以面向对象的自然语法构造查询语句。JPA定义了JPQL,它是针对实体的一种查询语言,操作对象是实体,而不是关系数据库的表,而且能够支持批量更新和修改、Join、Group By、Having等通常只有SQL才能够提供的高级查询特性,甚至还能够支持子查询。
  4. JPA中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系。

2.3 JPA与Hibernate的关系

JPA规范本质上就是一种ORM规范,注意不是ORM框架,因为JPA并未提供ORM实现,它只是制定了一些规范,提供了一些编程的API接口,但是具体的实现则由服务厂商来提供实现。

三、JPA入门案例

3.1 需求

保存客户到数据库的客户表中

1
2
3
4
5
6
7
8
9
10
11
/*创建客户表*/ 
CREATE TABLE customer (
cust_id BIGINT ( 32 ) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
cust_name VARCHAR ( 32 ) NOT NULL COMMENT '客户名称(公司名称)',
cust_source VARCHAR ( 32 ) DEFAULT NULL COMMENT '客户信息来源',
cust_industry VARCHAR ( 32 ) DEFAULT NULL COMMENT '客户所属行业',
cust_level VARCHAR ( 32 ) DEFAULT NULL COMMENT '客户级别',
cust_address VARCHAR ( 128 ) DEFAULT NULL COMMENT '客户联系地址',
cust_phone VARCHAR ( 64 ) DEFAULT NULL COMMENT '客户联系电话',
PRIMARY KEY ( `cust_id` )
) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;

3.2 环境

步骤

  1. maven导入坐标
  2. 添加log4j配置文件
  3. 配置jpa的核心配置文件
    • 位置:配置到类路径下的一个叫做META-INF的文件夹下
    • 命名:persistence.xml
    • 约束配置:idea右键new->EditFileTemplate->Other->JPA->Deployment descriptors
  4. 编写客户实体类
  5. 配置实体类和表、类中属性和表中字段的映射关系
  6. 保存客户到数据库中

maven中导入坐标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>top.meethigher</groupId>
<artifactId>SpringDataJPA-notes</artifactId>
<version>1.0</version>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.hibernate.version>5.4.30.Final</project.hibernate.version>
</properties>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<!--hibernate对jpa的支持包-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${project.hibernate.version}</version>
</dependency>
<!--c3p0-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>${project.hibernate.version}</version>
</dependency>
<!--log日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.12</version>
</dependency>

</dependencies>

</project>

添加log4j配置文件

在resources目录下添加即可

可以参照mybatis中的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#############
# 输出到控制台
#############

# log4j.rootLogger日志输出类别和级别:只输出不低于该级别的日志信息DEBUG < INFO < WARN < ERROR < FATAL
# WARN:日志级别 CONSOLE:输出位置自己定义的一个名字 logfile:输出位置自己定义的一个名字
log4j.rootLogger=debug,CONSOLE,logfile
# 配置CONSOLE输出到控制台
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
# 配置CONSOLE设置为自定义布局模式
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
# 配置CONSOLE日志的输出格式 [frame] 2019-08-22 22:52:12,000 %r耗费毫秒数 %p日志的优先级 %t线程名 %C所属类名通常为全类名 %L代码中的行号 %x线程相关联的NDC %m日志 %n换行
log4j.appender.CONSOLE.layout.ConversionPattern=[frame] %d{yyyy-MM-dd HH:mm:ss,SSS} - %-4r %-5p [%t] %C:%L %x - %m%n

################
# 输出到日志文件中
################

# 配置logfile输出到文件中 文件大小到达指定尺寸的时候产生新的日志文件
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
# 保存编码格式
log4j.appender.logfile.Encoding=UTF-8
# 输出文件位置此为项目根目录下的logs文件夹中
log4j.appender.logfile.File=logs/root.log
# 后缀可以是KB,MB,GB达到该大小后创建新的日志文件
log4j.appender.logfile.MaxFileSize=10MB
# 设置滚定文件的最大值3 指可以产生root.log.1、root.log.2、root.log.3和root.log四个日志文件
log4j.appender.logfile.MaxBackupIndex=3
# 配置logfile为自定义布局模式
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n

##########################
# 对不同的类输出不同的日志文件
##########################

# club.bagedate包下的日志单独输出
log4j.logger.club.bagedate=DEBUG,bagedate
# 设置为false该日志信息就不会加入到rootLogger中了
log4j.additivity.club.bagedate=false
# 下面就和上面配置一样了
log4j.appender.bagedate=org.apache.log4j.RollingFileAppender
log4j.appender.bagedate.Encoding=UTF-8
log4j.appender.bagedate.File=logs/bagedate.log
log4j.appender.bagedate.MaxFileSize=10MB
log4j.appender.bagedate.MaxBackupIndex=3
log4j.appender.bagedate.layout=org.apache.log4j.PatternLayout
log4j.appender.bagedate.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n

☆配置jpa核心文件

在Maven下的resources文件夹下创建META-INF文件夹。

在META-INF文件夹下添加persistence.xml。

利用idea中的jpa模板来复制xml约束,步骤如图。

配置持久化单元节点persistence-unit

  • name:持久化单元名称
  • transaction-type:事务管理方式
    1. JTA:分布式事务管理。适用于不同的表存在于不同的数据库中
    2. RESOURCE_LOCAL:本地事务管理。所有的表都存在于一个数据库中

配置jpa实现方式

  • provider标签:org.hibernate.jpa.HibernatePersistenceProvider

配置数据库信息property

  • javax.persistence.jdbc.user:用户名

  • javax.persistence.jdbc.password:密码

  • javax.persistence.jdbc.driver:驱动

  • javax.persistence.jdbc.url:数据库地址

配置jpa实现方(hibernate)信息property

  • hibernate.show_sql:显示sql,值为true或false,默认false
  • hibernate.hbm2ddl.auto:自动创建数据库,值有create、update、none,默认update
    1. create:程序运行时创建表,如果有表,那就先删除再创建
    2. update:程序运行时创建表,如果有表,不会创建表
    3. none:不会创建表

persistence.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
<!--配置persistence-unit节点-->
<persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
<!--jpa的实现方式-->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<!--可选配置:-->
<properties>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="root"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url " value="jdbc:mysql://localhost:3306/springdatajpa?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="create"/>
</properties>
</persistence-unit>
</persistence>

编写客户实体类

根据数据库来创建实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package top.meethigher.demo01.domain;

/**
* Customer
*
* @author kit chen
* @github https://github.com/meethigher
* @blog https://meethigher.top
* @time 2021/4/10
*/
public class Customer {
private Long custId;
private String custName;
private String custSource;
private String custIndustry;
private String custLevel;
private String custAddress;
private String custPhone;

public Long getCustId() {
return custId;
}

public void setCustId(Long custId) {
this.custId = custId;
}

public String getCustName() {
return custName;
}

public void setCustName(String custName) {
this.custName = custName;
}

public String getCustSource() {
return custSource;
}

public void setCustSource(String custSource) {
this.custSource = custSource;
}

public String getCustIndustry() {
return custIndustry;
}

public void setCustIndustry(String custIndustry) {
this.custIndustry = custIndustry;
}

public String getCustLevel() {
return custLevel;
}

public void setCustLevel(String custLevel) {
this.custLevel = custLevel;
}

public String getCustAddress() {
return custAddress;
}

public void setCustAddress(String custAddress) {
this.custAddress = custAddress;
}

public String getCustPhone() {
return custPhone;
}

public void setCustPhone(String custPhone) {
this.custPhone = custPhone;
}

@Override
public String toString() {
return "Customer{" +
"custId=" + custId +
", custName='" + custName + '\'' +
", custSource='" + custSource + '\'' +
", custIndustry='" + custIndustry + '\'' +
", custLevel='" + custLevel + '\'' +
", custAddress='" + custAddress + '\'' +
", custPhone='" + custPhone + '\'' +
'}';
}
}

☆配置映射关系

使用注解配置

  1. 实体类和表的映射关系
  2. 类中属性和表中字段的映射关系

注解

  1. @Entity:声明该类是一个实体类
  2. @Table:配置实体类和表的映射关系
    • name:配置数据库表的名称
  3. @Id:表示当前属性或者变量是主键
  4. @GeneratedValue
    • strategy:配置主键的生成策略
      • GenerationType.IDENTITY,表示自增,前提是底层数据库必须支持自动增长,如mysql,postgresql
      • GenerationType.SEQUENCE,表示序列,前提是底层数据库必须支持序列,如oracle
      • GenerationType.TABLE,jpa提供的一种机制,通过在数据库中多建立一张数据库表的形式,用来存储下一个的值,帮助我们完成自增(就跟我当初做的住房公积金系统的需求一样)
      • GenerationType.AUTO,由程序自动选择策略
  5. @Column:配置实体类属性和表中字段的映射关系
    • name:配置表中字段的名称
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
@Entity
@Table(name="customer")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "cust_id")
private Long custId;
@Column(name = "cust_name")
private String custName;
@Column(name = "cust_source")
private String custSource;
@Column(name = "cust_industry")
private String custIndustry;
@Column(name = "cust_level")
private String custLevel;
@Column(name = "cust_address")
private String custAddress;
@Column(name = "cust_phone")
private String custPhone;

public Long getCustId() {
return custId;
}

public void setCustId(Long custId) {
this.custId = custId;
}

public String getCustName() {
return custName;
}

public void setCustName(String custName) {
this.custName = custName;
}

public String getCustSource() {
return custSource;
}

public void setCustSource(String custSource) {
this.custSource = custSource;
}

public String getCustIndustry() {
return custIndustry;
}

public void setCustIndustry(String custIndustry) {
this.custIndustry = custIndustry;
}

public String getCustLevel() {
return custLevel;
}

public void setCustLevel(String custLevel) {
this.custLevel = custLevel;
}

public String getCustAddress() {
return custAddress;
}

public void setCustAddress(String custAddress) {
this.custAddress = custAddress;
}

public String getCustPhone() {
return custPhone;
}

public void setCustPhone(String custPhone) {
this.custPhone = custPhone;
}

@Override
public String toString() {
return "Customer{" +
"custId=" + custId +
", custName='" + custName + '\'' +
", custSource='" + custSource + '\'' +
", custIndustry='" + custIndustry + '\'' +
", custLevel='" + custLevel + '\'' +
", custAddress='" + custAddress + '\'' +
", custPhone='" + custPhone + '\'' +
'}';
}
}

测试保存客户到数据库中

如果是update或者create时,添加实体类字段,会添加相应的字段。区别在于一个是直接在原有数据基础上添加字段,另一个是删除表再添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class JPATest01 {
@Test
public void testSave() {
/*1. 加载配置文件创建工厂(实体管理类工厂)对象*/
EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
/*2. 通过实体管理类工厂获取实体管理器*/
EntityManager em = factory.createEntityManager();
/*3. 获取事务对象,开启事务*/
EntityTransaction ts = em.getTransaction();
ts.begin();
/*4. 完成增删改查操作*/
Customer customer=new Customer();
customer.setCustName("美杜莎");
customer.setCustIndustry("蛇人族");
customer.setCustAddress("斗破苍穹");
customer.setCustLevel("斗皇强者");
customer.setCustPhone("保密");
customer.setCustSource("保密");
em.persist(customer);
/*5. 提交事务(回滚事务)*/
ts.commit();
/*6. 释放资源*/
em.close();
factory.close();

}
}

四、JPA基本操作

4.1 步骤

jpa操作步骤

  1. 加载配置文件创建实体管理工厂
    • Persistence:静态方法,createEntityManagerFactory根据持久化单元名称创建实体管理工厂
  2. 根据实体管理工厂,创建实体管理器
    • EntityManagerFactory:获取EntityManagerFactory对象,createEntityManager创建实体管理器
      • 内部维护了数据库信息
      • 内部维护了缓存信息
      • 维护了所有的实体管理对象
      • 在创建EntityManagerFactory的过程中,会根据配置创建数据库表
      • EntityManagerFactory的创建过程比较浪费资源、耗时
      • 特点:EntityManagerFactory是一个线程安全的对象,也就是多个线程访问同一个EntityManagerFactory不会有线程安全问题
      • 如何解决EntityManagerFactory的创建过程浪费资源、耗时的问题?
        • 静态代码块的形式创建一个公共的EntityManagerFactory的对象
  3. 创建事务对象,开启事务
    • EntityManager对象:实体类管理器
      • beginTransaction:创建事务对象
      • presist:保存
      • merge:更新
      • remove:删除
      • find/getReference:根据id查询
        • class参数:查询的数据结果需要包装成的实体类类型的的字节码
        • id参数:要查询的主键的取值
        • getReference获取到的是一个动态代理对象,可以通过断点看到。而且,该方法采用的是懒加载的方式。
        • find获取到的就是对象本身,是立即加载。
    • Transaction对象:事务
      • begin:开启事务
      • commit:提交事务
      • rollback:回滚
  4. 增删改查操作
  5. 提交事务
  6. 释放资源

4.2 实现

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
public class JPATest02 {
/**
* 保存
*/
@Test
public void testSave() {
EntityManager em = JpaUtils.getEntityManager();
EntityTransaction ts = em.getTransaction();
ts.begin();
Customer customer=new Customer();
customer.setCustName("美杜莎");
customer.setCustIndustry("蛇人族");
customer.setCustAddress("斗破苍穹");
customer.setCustLevel("斗皇强者");
customer.setCustPhone("保密");
customer.setCustSource("保密");
em.persist(customer);
ts.commit();
em.close();
}

/**
* 查询
*/
@Test
public void testFind() {
EntityManager em = JpaUtils.getEntityManager();
EntityTransaction ts = em.getTransaction();
ts.begin();
/**
* find:根据id查询,立即加载
* getReference:根据id查询,通过代理实现懒加载
*/
// Customer customer = em.find(Customer.class, 1L);//注意此处是1L,否则会报Long转Integer错误
Customer customer = em.getReference(Customer.class, 1L);
ts.commit();
em.close();
}

/**
* 删除
* 先查再删
*/
@Test
public void testDelete() {
EntityManager em = JpaUtils.getEntityManager();
EntityTransaction ts = em.getTransaction();
ts.begin();
/**
* 删除
* 1. 先查
* 2. 再删
*/
Customer reference = em.getReference(Customer.class, 1L);
em.remove(reference);
ts.commit();
em.close();
}

/**
* 修改
* 先查再改
*/
@Test
public void testUpdate(){
EntityManager em = JpaUtils.getEntityManager();
EntityTransaction ts = em.getTransaction();
ts.begin();
/**
* 删除
* 1. 先查
* 2. 再删
*/
Customer reference = em.getReference(Customer.class, 3L);
reference.setCustName("胡列娜");
em.merge(reference);
ts.commit();
em.close();
}
}

五、JPQL复杂查询

5.1 概念

JPQL全称Java Persistence Query Language,Java持久化查询语言,以面向对象的表达式,将SQL语法和简单语义绑定在一起,可以被编译成所有主流数据库服务器上的SQL。

SQL:查询的是表和表中的字段

JPQL:查询的是实体类和表中的属性

5.2 操作

首先准备单元测试的环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class JPQLTest {
private static EntityManager em;
private static EntityTransaction ts;
@BeforeClass
public static void beforeClass() throws Exception {
em = JpaUtils.getEntityManager();
ts = em.getTransaction();
ts.begin();
System.out.println("获取对象并开启事务");
}

@AfterClass
public static void afterClass() throws Exception {
ts.commit();
em.close();
System.out.println("提交事务并释放对象");
}
}

查询全部

SQL:select * from customer

JPQL:from top.meethigher.Customer(也可以省略全类名,只用类名即可)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 查询全部
*/
@Test
public void testFindAll() {
String jpql="from Customer";
//创建Query对象,Query才是执行JPQL的对象
Query query = em.createQuery(jpql);
//发送查询,并封装结果集
List resultList = query.getResultList();
//遍历
for (Object o :
resultList) {
System.out.println(o);
}
}

排序查询

SQL:select * from customer order by cust_id desc

JPQL:from top.meethigher.Customer order by custId desc(也可以省略全类名,只用类名即可)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 排序查询
*/
@Test
public void testFindOrById() {
String jpql="from Customer order by custId desc ";
//创建Query对象,Query才是执行JPQL的对象
Query query = em.createQuery(jpql);
//发送查询,并封装结果集
List resultList = query.getResultList();
//遍历
for (Object o :
resultList) {
System.out.println(o);
}
}

统计查询

SQL:select count(cust_id) from customer

JPQL:select count(custId) from top.meethigher.Customer (也可以省略全类名,只用类名即可)

1
2
3
4
5
6
7
8
9
10
/**
* 统计查询
*/
@Test
public void testFindCount() {
String jpql="select count(custId) from Customer";
Query query = em.createQuery(jpql);
Object singleResult = query.getSingleResult();
System.out.println(singleResult);
}

分页查询

SQL

  • 标准写法:select * from customer limit ?,?
  • 省略写法:如果是从0开始,那么可省略。select * from customer limit ?

JPQL:from top.meethigher.Customer (也可以省略全类名,只用类名即可)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 分页查询
*/
@Test
public void testFindByPage() {
String jpql="from Customer";
Query query = em.createQuery(jpql);
//分页查询
//起始索引
query.setFirstResult(1);
//每页查询的条数
query.setMaxResults(2);
List resultList = query.getResultList();
for (Object o :
resultList) {
System.out.println(o);
}
}

条件查询

查询名称含有xx关键字的客户

SQL:select * from customer where cust_name like ?

JPQL:from top.meethigher.Customer where custName like ?1(也可以省略全类名,只用类名即可)

  • 默认索引是从1开始
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 条件查询
*/
@Test
public void testFindByCondition() {
String jpql="from Customer where custName like ?1";
Query query = em.createQuery(jpql);
//条件查询
/**
* 占位符参数
* 第一个参数:占位符索引位置,从1开始
* 第二个参数:取值
*/
query.setParameter(1,"%美%");
List resultList = query.getResultList();
for (Object o :
resultList) {
System.out.println(o);
}
}
发布:2021-04-11 14:54:38
修改:2021-04-12 00:05:56
链接:https://meethigher.top/blog/2021/jpa/
标签:java 
付款码 打赏 分享
阅读量