Spring Boot JPA

Spring Boot JPA 入门实战

Posted by leone on 2019-01-12

Spring Boot JPA

开发工具

系统: windows10

开发工具: Intellij IDEA 2018.2.6

springboot: 2.0.6.RELEASE

jdk: 1.8.0_192

maven: 3.6.0

数据库:mysql-8.0.15

jpa简介

什么是jpa

JPA(Java Persistence API)是SUN官方推出的Java持久化规范,它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据。它的出现主要是为了简化现有的持久化开发工作和整合ORM技术,结束现在Hibernate,TopLink,JDO等ORM框架各自为营的局面。值得注意的是,JPA是在充分吸收了现有Hibernate,TopLink,JDO 等ORM框架的基础上发展而来的,具有易于使用,伸缩性强等优点。从开发社区的反映上看,JPA手动极大的支持和赞扬,其中就包括了Spring与EJB3.0的开发团队,着眼未来几年的技术走向,JPA作为ORM领域的标准化者的目标应该不难实现。
JPA包括一下三方面的技术

  • ORM映射元数据

JPA支持XML和JDK5.0注释两种元数据形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中。

  • Java持久化API

用来操作实体对象,执行CRUD操作,框架在后台替我们完成所有的事情,开发者可以从繁琐的JDBC和SQL代码中解脱出来。

  • 查询语言(JPQL)

这是持久化操作中很重要的一个方面,通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合。

JPA的优势

  • 标准化

JPA 是 JCP 组织发布的 Java EE 标准之一,因此任何声称符合 JPA 标准的框架都遵循同样的架构,提供相同的访问 API,这保证了基于JPA开发的企业应用能够经过少量的修改就能够在不同的JPA框架下运行。

  • 对容器级特性的支持

JPA 框架中支持大数据集、事务、并发等容器级事务,这使得 JPA 超越了简单持久化框架的局限,在企业应用发挥更大的作用。

  • 简单易用,集成方便

JPA的主要目标之一就是提供更加简单的编程模型:在JPA框架下创建实体和创建Java 类一样简单,没有任何的约束和限制,只需要使用 javax.persistence.Entity进行注释;JPA的框架和接口也都非常简单,没有太多特别的规则和设计模式的要求,开发者可以很容易的掌握。JPA基于非侵入式原则设计,因此可以很容易的和其它框架或者容器集成。

  • 可媲美JDBC的查询能力

JPA的查询语言是面向对象而非面向数据库的,它以面向对象的自然语法构造查询语句,可以看成是Hibernate HQL的等价物。JPA定义了独特的JPQL(Java Persistence Query Language),JPQL是EJB QL的一种扩展,它是针对实体的一种查询语言,操作对象是实体,而不是关系数据库的表,而且能够支持批量更新和修改、JOIN、GROUP BY、HAVING 等通常只有 SQL 才能够提供的高级查询特性,甚至还能够支持子查询。

  • 支持面向对象的高级特性

JPA 中能够支持面向对象的高级特性,如类之间的继承、多态和类之间的复杂关系,这样的支持能够让开发者最大限度的使用面向对象的模型设计企业应用,而不需要自行处理这些特性在关系数据库的持久化。

jpa常用注解

  • @Table

标注实体和表的对应关系,若表名和类型相同时,省略@Table,比如类Users 和表 users若不相同时,必须有@Table,并设置name,为该类对应的表名。@Table(name=“users”)

  • @Entity

Entity说明这个class是实体类,并且使用默认的orm规则,即class名对应数据库表中表名,class字段名即表中的字段名。

  • @Id

声明当前field为映射表中的主键列。 id值的获取方式有五种:TABLE, SEQUENCE, IDENTITY, AUTO, NONE

  • @Transient

表示该属性并不是一个到数据库表的字段的映射,指定的这些属性不会被持久化,ORM框架将忽略该属性。 如果一个属性并非数据库表的字段映射。就务必将其标示为@Transient。否则,ORM框架默认其注解为@Basic

  • @Version

Version指定实体类在乐观事务中的version属性。在实体类重新由EntityManager管理并且加入到乐观事务中时,保证完整性。每一个类只能有一个属性被指定为version,version属性应该映射到实体类的主表上。

  • @Column

Column元数据定义了映射到数据库的列的所有属性:列名,是否唯一,是否允许为空,是否允许更新等。

  • @Lob
    Lob指定一个属性作为数据库支持的大对象类型在数据库中存储。使用LobType这个枚举来定义Lob是二进制类型还是字符类型。LobType枚举类型说明:BLOB 二进制大对象,Byte[]或者Serializable的类型可以指定为BLOB。 CLOB 字符型大对象,char[]、Character[]或String类型可以指定为CLOB。

  • @GeneratedValue

用于标注主键的生成策略,通过 strategy 属性指定。默认情况下,JPA自动选择一个最适合底层数据库的主键生成策略:javax.persistence.GenerationType中定义了以下几种可供选择的策略:IDENTITY:采用数据库 ID自增长的方式来自增主键字段,Oracle 不支持这种方式;AUTO: JPA自动选择合适的策略,是默认选项;SEQUENCE:通过序列产生主键,通过@SequenceGenerator 注解指定序列名,MySql 不支持这种方式TABLE:通过表产生主键,框架借由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植。

创建项目和crud测试

  • 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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<?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">

<groupId>com.andy</groupId>
<artifactId>spring-boot-data</artifactId>
<version>1.0.7.RELEASE</version>
<modelVersion>4.0.0</modelVersion>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.spring.platform</groupId>
<artifactId>platform-bom</artifactId>
<version>Cairo-SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

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

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>

<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.0.3.RELEASE</version>
<configuration>
<!--<mainClass>${start-class}</mainClass>-->
<layout>ZIP</layout>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

</project>
  • User.java
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
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;

/**
* <p>
*
* @author Leone
* @since 2018-01-24
**/
@Entity
@Table(name = "t_user")
public class User implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userId;

@Column(columnDefinition = "varchar(128) not null comment '账号'")
private String account;

@Column(columnDefinition = "varchar(128) not null comment '密码'")
private String password;

@Column(columnDefinition = "text comment '简介'")
private String description;

@Column(columnDefinition = "int(11) not null comment '年龄'")
private Integer age;

@Column(columnDefinition = "timestamp not null default current_timestamp comment '创建时间'")
private Date createTime;

@Column(columnDefinition = "bit not null comment '是否删除'")
private boolean deleted;

public User() {
}

public User(Long userId, String account, String password, String description, Integer age, Date createTime, Boolean deleted) {
this.userId = userId;
this.account = account;
this.password = password;
this.description = description;
this.age = age;
this.createTime = createTime;
this.deleted = deleted;
}

// getter setter...

}
  • UserRepository.java

由于 UserRepository 继承了 JpaSpecificationExecutor 所以jpa自带了一些通用的方法无需我们自己实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import org.apache.ibatis.annotations.Param;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;

import java.util.List;

/**
* @author Leone
* @since 2018-05-11
**/
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {

@Query(value = "select * from t_user where account = ?1", nativeQuery = true)
User findUserByAccount(@Param("account") String account);

@Modifying
@Query("update User set deleted = 1 where userId in ?1")
Integer delByIds(List<Integer> ids);

}
  • UserService.java
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
import com.andy.data.entity.User;
import com.andy.data.repository.jpa.UserRepository;
import com.andy.data.repository.mybatis.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.List;

/**
* @author Leone
* @since 2018-07-08
**/
@Service
public class UserService {

@Autowired
private UserRepository userRepository;

/**
* 查询所用
*
* @return
*/
public List<User> getUsers() {
return userRepository.findAll();
}

/**
* 根据主键查找
*
* @param userId
* @return
*/
public User getUser(Long userId) {
return userRepository.findById(userId).orElse(null);
}

/**
* 修改
*
* @param user
* @return
*/
public User update(User user) {
return userRepository.save(user);
}

/**
* 删除
*
* @param userId
*/
public void delete(Long userId) {
userRepository.deleteById(userId);
}

/**
* 条件查询分页
*
* @param pageable
* @param description
* @return
*/
public Page<User> page(Pageable pageable, String description, Integer account) {
Specification<User> specification = (Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) -> {
List<Predicate> list = new ArrayList<>();

list.add(criteriaBuilder.equal(root.get("deleted").as(Integer.class), 0));

if (!StringUtils.isEmpty(description)) {
list.add(criteriaBuilder.like(root.get("description"), "%" + description + "%"));
}

if (!StringUtils.isEmpty(account)) {
list.add(criteriaBuilder.equal(root.get("account").as(String.class), account));
}

Predicate[] predicates = new Predicate[list.size()];
criteriaQuery.where(list.toArray(predicates));
criteriaQuery.orderBy(criteriaBuilder.asc(root.get("userId")));
return criteriaQuery.getRestriction();
};
return userRepository.findAll(specification, pageable);
}

}
  • 启动类
1
2
3
4
5
6
7
8
9
10
/**
* @author Leone
* @since 2018-05-11
**/
@SpringBootApplication
public class JpaApplication {
public static void main(String[] args) {
SpringApplication.run(JpaApplication.class, args);
}
}

传送门