摘要
工作中用到了mybatis,之后又用到了类似的持久层jpa,当时是真的给我惊艳了,觉的这玩意可真是牛逼。
甚至不用手动来操作数据库了,直接面向对象,方便了许多。
正文
源码
一、ORM
1.1 回顾JDBC
在以往的与数据库的交互中,我们一般是如下图操作的
问题
- 操作繁琐
- 占位符赋值麻烦
那么如何解决呢?
- 建立实体类和表的关系
- 建立实体类中属性和表中字段的关系
这样,我们就可以直接通过调用方法,来实现增删改查。比如增加
1
| obj.save(user);// 相当于执行 insert into user values(user.getId(),user.getName);
|
1.2 ORM思想
思想:操作实体类就相当于操作数据库表
建立两个映射关系
- 实体类和表的映射关系
- 实体类中属性和表中字段的映射关系
好处:不用关注SQL语句
目前市面上,有好多实现ORM思想的框架
- mybatis
- hibernate
- 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优势
优势
- 任何符合JPA标准的框架都遵循同样的架构,提供相同的访问API,这保证了基于JPA开发的应用经过少量的修改就能在不同的JPA框架下运行。
- JPA框架支持大数据集、事务、并发等容器级事务。
- JPA的查询语言是面向对象而非面向数据库的,它以面向对象的自然语法构造查询语句。JPA定义了JPQL,它是针对实体的一种查询语言,操作对象是实体,而不是关系数据库的表,而且能够支持批量更新和修改、Join、Group By、Having等通常只有SQL才能够提供的高级查询特性,甚至还能够支持子查询。
- 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 环境
步骤
- maven导入坐标
- 添加log4j配置文件
- 配置jpa的核心配置文件
- 位置:配置到类路径下的一个叫做META-INF的文件夹下
- 命名:persistence.xml
- 约束配置:idea右键
new->EditFileTemplate->Other->JPA->Deployment descriptors
- 编写客户实体类
- 配置实体类和表、类中属性和表中字段的映射关系
- 保存客户到数据库中
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:事务管理方式
- JTA:分布式事务管理。适用于不同的表存在于不同的数据库中
- 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
- create:程序运行时创建表,如果有表,那就先删除再创建
- update:程序运行时创建表,如果有表,不会创建表
- 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&useUnicode=true&characterEncoding=UTF-8&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 + '\'' +
'}';
}
}
|
☆配置映射关系
使用注解配置
- 实体类和表的映射关系
- 类中属性和表中字段的映射关系
注解
- @Entity:声明该类是一个实体类
- @Table:配置实体类和表的映射关系
- @Id:表示当前属性或者变量是主键
- @GeneratedValue
- strategy:配置主键的生成策略
- GenerationType.IDENTITY,表示自增,前提是底层数据库必须支持自动增长,如mysql,postgresql
- GenerationType.SEQUENCE,表示序列,前提是底层数据库必须支持序列,如oracle
- GenerationType.TABLE,jpa提供的一种机制,通过在数据库中多建立一张数据库表的形式,用来存储下一个的值,帮助我们完成自增(就跟我当初做的住房公积金系统的需求一样)
- GenerationType.AUTO,由程序自动选择策略
- @Column:配置实体类属性和表中字段的映射关系
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操作步骤
- 加载配置文件创建实体管理工厂
- Persistence:静态方法,createEntityManagerFactory根据持久化单元名称创建实体管理工厂
- 根据实体管理工厂,创建实体管理器
- EntityManagerFactory:获取EntityManagerFactory对象,createEntityManager创建实体管理器
- 内部维护了数据库信息
- 内部维护了缓存信息
- 维护了所有的实体管理对象
- 在创建EntityManagerFactory的过程中,会根据配置创建数据库表
- EntityManagerFactory的创建过程比较浪费资源、耗时
- 特点:EntityManagerFactory是一个线程安全的对象,也就是多个线程访问同一个EntityManagerFactory不会有线程安全问题
- 如何解决EntityManagerFactory的创建过程浪费资源、耗时的问题?
- 静态代码块的形式创建一个公共的EntityManagerFactory的对象
- 创建事务对象,开启事务
- EntityManager对象:实体类管理器
- beginTransaction:创建事务对象
- presist:保存
- merge:更新
- remove:删除
- find/getReference:根据id查询
- class参数:查询的数据结果需要包装成的实体类类型的的字节码
- id参数:要查询的主键的取值
- getReference获取到的是一个动态代理对象,可以通过断点看到。而且,该方法采用的是懒加载的方式。
- find获取到的就是对象本身,是立即加载。
- Transaction对象:事务
- begin:开启事务
- commit:提交事务
- rollback:回滚
- 增删改查操作
- 提交事务
- 释放资源
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
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);
}
}
|