avatar

SpringBoot2.X集成SpringDataJPA(一)

JPA概述

是一套规范,mybatis,hiberante是产品

mybatis SQL语句用户自己去写

hibernate 不需要手动写SQL,程序运行时会自动生成并执行,如果非要写SQL语句,HIbernate建议使用面向对象的查询语句来完成

环境搭建

创建空maven工程并导入依赖

pom.xml文件如下

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
<?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>cn.itcast</groupId>
<artifactId>springDataJpaDemo</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>

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

<dependencies>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</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>

<!-- Mysql and MariaDB -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
</dependencies>

</project>

添加配置文件

在resource目录下创建文件夹META-INF,在里面创建persistence.xml文件

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
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<!--需要配置persistence-unit节点
持久化单元:
name:持久化单元名称
transaction-type:事务管理的方式
JTA:分布式事务管理
RESOURCE_LOCAL:本地事务管理
-->
<persistence-unit name="hibernateConfig" transaction-type="RESOURCE_LOCAL">
<!--jpa的实现方式 -->
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

<!--可选配置:配置jpa实现方的配置信息-->
<properties>
<!-- 数据库信息
用户名,javax.persistence.jdbc.user
密码, javax.persistence.jdbc.password
驱动, javax.persistence.jdbc.driver
数据库地址 javax.persistence.jdbc.url
-->
<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.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql:///jpa"/>

<!--配置jpa实现方(hibernate)的配置信息
显示sql : false|true
自动创建数据库表 : hibernate.hbm2ddl.auto
create : 程序运行时创建数据库表(如果有表,先删除表再创建)
update :程序运行时创建表(如果有表,不会创建表)
none :不会创建表

-->
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.hbm2ddl.auto" value="update" />
</properties>
</persistence-unit>
</persistence>

快速上手

创建表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
USE `jpa`;
DROP TABLE IF EXISTS `cst_customer`;

CREATE TABLE `cst_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 DEFAULT CHARSET=utf8;

insert into `cst_customer`(`cust_id`,`cust_name`,`cust_source`,`cust_industry`,`cust_level`,`cust_address`,`cust_phone`) values (1,'黑马程序员',NULL,'教育',NULL,NULL,NULL),(2,'传智播客',NULL,'教育',NULL,NULL,NULL),(3,'酷丁鱼',NULL,'教育',NULL,NULL,NULL),(4,'传智汇',NULL,'教育',NULL,NULL,NULL),(5,'传智专修学院',NULL,'教育',NULL,NULL,NULL),(6,'谷歌',NULL,'教育',NULL,NULL,NULL);

创建实体类

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
package cn.itcast.domain;
import javax.persistence.*;

public class Customer {
private Long custId; //客户的主键
private String custName;//客户名称
private String custSource;//客户来源
private String custLevel;//客户级别
private String custIndustry;//客户所属行业
private String custPhone;//客户的联系方式
private String custAddress;//客户地址
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 getCustLevel() {
return custLevel;
}
public void setCustLevel(String custLevel) {
this.custLevel = custLevel;
}
public String getCustIndustry() {
return custIndustry;
}
public void setCustIndustry(String custIndustry) {
this.custIndustry = custIndustry;
}

public String getCustPhone() {
return custPhone;
}

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

public String getCustAddress() {
return custAddress;
}

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

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

通过注解配置映射关系

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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package cn.itcast.domain;

import javax.persistence.*;

/**
* 客户的实体类
* 配置映射关系
*
*
* 1.实体类和表的映射关系
* @Entity:声明实体类
* @Table : 配置实体类和表的映射关系
* name : 配置数据库表的名称
* 2.实体类中属性和表中字段的映射关系
*
*
*/
@Entity
@Table(name = "cst_customer")
public class Customer {

/**
* @Id:声明主键的配置
* @GeneratedValue:配置主键的生成策略
* strategy
* GenerationType.IDENTITY :自增,mysql
* * 底层数据库必须支持自动增长(底层数据库支持的自动增长方式,对id自增)
* GenerationType.SEQUENCE : 序列,oracle
* * 底层数据库必须支持序列
* GenerationType.TABLE : jpa提供的一种机制,通过一张数据库表的形式帮助我们完成主键自增
* GenerationType.AUTO : 由程序自动的帮助我们选择主键生成策略
* @Column:配置属性和字段的映射关系
* name:数据库表中字段的名称
*/
@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_level")
private String custLevel;//客户级别

@Column(name="cust_industry")
private String custIndustry;//客户所属行业

@Column(name="cust_phone")
private String custPhone;//客户的联系方式

@Column(name="cust_address")
private String custAddress;//客户地址

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 getCustLevel() {
return custLevel;
}

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

public String getCustIndustry() {
return custIndustry;
}

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

public String getCustPhone() {
return custPhone;
}

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

public String getCustAddress() {
return custAddress;
}

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

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

编写测试类

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
package cn.itcast.test;

import cn.itcast.domain.Customer;
import cn.itcast.utils.JpaUtils;
import org.junit.Test;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
/**
* 测试jpa的保存
* 案例:保存一个客户到数据库中
* Jpa的操作步骤
* 1.加载配置文件创建工厂(实体管理器工厂)对象
* 2.通过实体管理器工厂获取实体管理器
* 3.获取事务对象,开启事务
* 4.完成增删改查操作
* 5.提交事务(回滚事务)
* 6.释放资源
*/
public class JpaTest {

/**
* 添加用户
*/
@Test
public void testSave() {
//1.加载配置文件创建工厂(实体管理器工厂)对象
EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
//2.通过实体管理器工厂获取实体管理器
EntityManager em = factory.createEntityManager();
//3.获取事务对象,开启事务
EntityTransaction tx = em.getTransaction(); //获取事务对象
tx.begin();//开启事务
//4.完成增删改查操作:保存一个客户到数据库中
Customer customer = new Customer();
customer.setCustName("传智播客");
customer.setCustIndustry("教育");
//保存,
em.persist(customer); //保存操作
//5.提交事务
tx.commit();
//6.释放资源
em.close();
factory.close();
}
}

可能出现的错误信息

问题1:Cannot resolve xxx

解决办法

​ 1.alt+enter 选择数据源 assign data source

​ 2.选择数据库连接(如果没有数据源请先添加,教程地址:https://www.jianshu.com/p/d7db42636e63)

问题2:java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException

解决办法

​ 该问题是因为jdk1.9没有JAXB相关API的jar包,添加依赖即可

1
2
3
4
5
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>

工具类抽取

抽取理由

理由1:EntityManagerFactory对象每次创建都需要读取配置文件,比较耗时

理由2:该对象是线程安全的在多线程环境中不会出现线程安全问题

工具类代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package cn.itcast.utils;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class JpaUtils {

private static EntityManagerFactory factory;

static {
//1.加载配置文件,创建entityManagerFactory
factory = Persistence.createEntityManagerFactory("myJpa");
}

/**
* 获取EntityManager对象
*/
public static EntityManager getEntityManager() {
return factory.createEntityManager();
}
}

使用工具类改进测试代码

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
package cn.itcast.domain;

import cn.itcast.utils.JpaUtils;
import org.junit.Test;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class CustomerTest {
/**
* 添加用户
*/
@Test
public void testSave() {
//1.创建EntityManager
EntityManager em = JpaUtils.getEntityManager();
//2.获取事务对象,开启事务
EntityTransaction tx = em.getTransaction(); //获取事务对象
tx.begin();//开启事务


//3.完成增删改查操作:保存一个客户到数据库中
Customer customer = new Customer();
customer.setCustName("传智播客");
customer.setCustIndustry("教育");
//保存,
em.persist(customer); //保存操作


//4.提交事务
tx.commit();
//5.释放资源
em.close();
}
}

升级Junit测试代码

观察以下代码思考问题

@After @Before @Test回顾

执行顺序 @Before > @Test > @After

改造思路

将初始化EntityManager和EntityTransaction,包括开启事务的操作放到@Before中

将提交事务和释放资源的代码放到@after中

让其他测试类继承该BaseTest类

BaseTest代码

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
package cn.itcast.domain;

import cn.itcast.utils.JpaUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;

public class BaseTest {
public EntityManager entityManager;
public EntityTransaction tx;
@Before
public void init(){
entityManager = JpaUtils.getEntityManager();
//3.获取事务对象,开启事务
tx = entityManager.getTransaction(); //获取事务对象
tx.begin();
System.out.println("1.初始化EntityManager和EntityTransaction对象完毕");
System.out.println("2.开启事务,开始执行自定义(CRUD)操作");
}
@After
public void destory(){
tx.commit();
entityManager.close();

System.out.println("3.提交事务");
System.out.println("4.释放资源");
}

}

基本操作

增加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
public class CustomerTest extends BaseTest {
/**
* 添加用户
*/
@Test
public void testSave() {
//4.完成增删改查操作:保存一个客户到数据库中
Customer customer = new Customer();
customer.setCustName("传智播客");
customer.setCustIndustry("教育");
//保存,
entityManager.persist(customer); //保存操作
}
}

运行结果

删除

1
2
3
4
5
6
7
8
9
10
11
12
13
package cn.itcast.domain;

import org.junit.Test;

public class Test4 extends BaseTest {
@Test
public void testDelete(){
Customer customer = entityManager.find(Customer.class, 3L);
if(customer != null){
entityManager.remove(customer);//删除
}
}
}

修改

1
2
3
4
5
6
7
8
9
10
public class Test5 extends BaseTest {
@Test
public void testUpdate(){
Customer customer = entityManager.find(Customer.class, 1L);
if(customer != null){
customer.setCustName("黑马程序员");
entityManager.merge(customer);//更新
}
}
}

查询

根据ID查询

1
2
3
4
5
6
7
8
9
10
package cn.itcast.domain;
import org.junit.Test;
public class CustomerTest2 extends BaseTest {
@Test
public void testFindById(){
//这里因为主键是Long类型,所以要传入Long类型的数据
Customer customer = entityManager.find(Customer.class, 1L);
System.out.println(customer);
}
}

运行结果

延迟查询

代码

1
2
3
4
5
6
7
8
9
package cn.itcast.domain;
import org.junit.Test;
public class CustomerTest3 extends BaseTest {
@Test
public void testLazyFindById(){
Customer customer = entityManager.getReference(Customer.class, 1L);
System.out.println(customer);
}
}

延迟查询和立即查询的区别

延迟加载(懒加载)

​ 得到的是一个动态代理对象

​ 什么时候用,什么使用才会查询

立即查询

​ 调用查询方法就会马上执行SQL语句

使用延迟查询在控制台的结果

使用的是getRerence延迟加载方法,并且没有获取结果对象,所以不会执行SQL语句

立即查询在控制台的结果

使用的是find方法,虽然没有获取结果对象,但是会执行SQL语句,只要调用方法就会执行

面向对象语句(JPQL)查询数据库

语法

SQL : select * from 表名

​ select * from 表名 order by 字段名 desc

JPQL/HQL: from 实体类名

##7.1 查询全部

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package cn.itcast.domain;

import org.junit.Test;

import javax.persistence.Query;
import java.util.List;

public class Test6 extends BaseTest{
@Test
public void testFindAll(){
//String jpql = "from cn.itcast.domain.Customer";
//简写 这里的Customer是类名
String jpql = "from Customer ordery by custId asc";
Query query = entityManager.createQuery(jpql);
List<Customer> customers = query.getResultList();

//遍历结果
for (Customer customer : customers) {
System.out.println(customer);
}
}
}

倒叙查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package cn.itcast.domain;

import org.junit.Test;

import javax.persistence.Query;
import java.util.List;

public class Test7 extends BaseTest{
@Test
public void testFindAllDesc(){

//简写 这里的Customer是类名,根据ID倒叙查询
Query query = entityManager.createQuery("from Customer order by custId desc ");
List<Customer> customers = query.getResultList();

//遍历结果
for (Customer customer : customers) {
System.out.println(customer);
}
}
}

统计查询

1
2
3
4
5
6
7
@Test
public void testFindCount(){
//简写 这里的Customer是类名
Query query = entityManager.createQuery("select count(*) from Customer ");
Long cnt = (Long)query.getSingleResult();//只能装成Long类型
System.out.println(cnt);
}

控制台打印

1
2
3
4
5
6
1.初始化EntityManager和EntityTransaction对象完毕
2.开启事务,开始执行自定义(CRUD)操作
Hibernate: select count(*) as col_0_0_ from cst_customer customer0_
3
3.提交事务
4.释放资源

分页查询

需求

代码

注意这里不能使用limit语句!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void testFindByPage(){
//简写 这里的Customer是类名
Query query = entityManager.createQuery("from Customer");
query.setFirstResult(2);//从索引为2的位置开始查询
query.setMaxResults(2);//查询2条记录
List<Customer> list = query.getResultList();

//遍历
for (Customer customer : list) {
System.out.println(customer);
}
}

查询结果

条件查询

提示:方式1比较老,建议使用方式2和方式3

方式1使用?占位符(旧)

1
2
3
4
5
6
7
8
9
10
@Test
public void testCondition1(){
Query query = entityManager.createQuery("from Customer where custName = ?");
query.setParameter(1,"传智播客");
List<Customer> list = query.getResultList();

for (Customer customer : list) {
System.out.println(customer);
}
}

1那里会报红线,忽略就好了

方式2使用命名参数占位符

1
2
3
4
5
6
7
8
9
10
@Test
public void testCondition(){
Query query = entityManager.createQuery("from Customer where custName = :custName");
query.setParameter("custName","传智播客");
List<Customer> list = query.getResultList();

for (Customer customer : list) {
System.out.println(customer);
}
}

方式3 使用JPA占位符

1
2
3
4
5
6
7
8
9
10
11
@Test
public void testCondition3(){
//这里索引从0开始计数,但是在书写的时候这个数字随便写,就写个9都没问题
Query query = entityManager.createQuery("from Customer where custName = ?");
query.setParameter("1","传智播客");
List<Customer> list = query.getResultList();

for (Customer customer : list) {
System.out.println(customer);
}
}
文章作者: 微信:hao_yongliang
文章链接: https://haoyongliang.gitee.io/2019/07/13/SpringBoot/SpringBoot2.X%E9%9B%86%E6%88%90SpringDataJPA(%E4%B8%80)/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 郝永亮的主页
打赏
  • 微信
    微信
  • 支付寶
    支付寶

评论