什么是JPA?
JPA(Java Persistence API),中文名Java持久层API,是Java持久化规范,它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据。
JPA的出现主要是为了简化现有的持久化开发工作和整合ORM技术,结束现在Hibernate,TopLink,JDO等ORM框架各自为营的局面。
JPA是在充分吸收了现有Hibernate,TopLink,JDO 等ORM框架的基础上发展而来的,具有易于使用,伸缩性强等优点。总的来说,JPA包括以下3方面的技术:
1、ORM映射元数据
JPA支持XML和JDK5.0注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中;
2、Java持久化API
用来操作实体对象,执行CRUD操作,框架在后台替代我们完成所有的事情,开发者从繁琐的JDBC和SQL代码中解脱出来。
3、查询语言(JPQL)
这是持久化操作中很重要的一个方面,通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合。
那么闲话少说,下面正式开始springboot和jpa的整合
maven依赖
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
application.yml配置文件
spring:
#数据库配置
datasource:
url: jdbc:mysql://localhost:3306/test?useSSL=false&Unicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
tomcat:
max-active: 20
max-idle: 8
min-idle: 8
#JPA(java持久层API)配置
jpa:
#指定数据库管理系统
database: MYSQL
#显示或者不记录每个sql查询
show-sql: true
#create:
#每次应用启动的时候会重新根据实体建立表,之前的表和数据都会被删除。
#create-drop:
#和上面的功能一样,但是多了一样,就是在应用关闭的时候,也就是sessionFactory一关闭,会把表删除。
#update:
#最常用的,第一次启动根据实体建立表结构,之后启动会根据实体的改变更新表结构,之前的数据都在。
#validate:
#会验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值,运行程序会校验实体字段与数据库已有的表的字段类型是否相同,不同会报错
hibernate:
ddl-auto: update
#命名策略
naming:
strategy: org.hibernate.cfg.ImprovedNamingStrategy
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
#定义数据处理的时间格式,比如在实体类中
jackson:
time-zone: UTC
date-format: yyyy-MM-dd HH:mm:ss
#指定项目能接收的时间格式
mvc:
format:
date: yyyy-MM-dd HH:mm:ss
创建实体类
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
@ApiModel("User的实体类")
@Data//创建get,set方法
@Accessors(chain = true)//开启支持链式操作
@Entity
public class User implements Serializable {
@ApiModelProperty(value = "主键id")
@Id
//主键生成策略,自增,IDENTITY表示数据库自动生成
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;//Long 对应数据库中的bigint
@ApiModelProperty("用户名")
@Column(nullable = false,unique = true,length = 50)//字段非空,唯一,长度50
private String name;
@ApiModelProperty("用户地址")
@Column
private String addr;
@ApiModelProperty("用户生日")
@Column(nullable = false)
// @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date birthDate;//Date 对应数据库的datetime
}
注解说明
- @Entity :表明是一个实体类
- @Table :对应的数据表名
- @Id :主键
- @GeneratedValue:主键生成策略
- @Column : 映射表对应的字段名
- @Basic : 表示该属性是表字段的映射。 如果实体的字段上没有任何注解默认就是@Basic
- @Transient : 表示该属性不是表字段的映射
- @Lob : 将属性映射成支持的大对象类型 ,如Clob、Blob
- @IdClass 联合主键,一般不用也不推荐用
- @Temporal : 用来设置Date类型的属性映射到对应精度的字
- @Enumerated 直接映射枚举类型的字段
创建dao层
import org.springframework.data.jpa.repository.JpaRepository;
import com.lcj.po.User;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* 该接口主要提供了多条件查询的支持,并且可以在查询中添加分页与排序。
* 注意: JpaSpecificationExecutor 是单独存在。完全独立
*
* JpaRepository中的泛型1:实体类 泛型2:主键映射类型
*/
@Repository
public interface UserRepository extends JpaSpecificationExecutor<User>, JpaRepository<User, Long> {
/**
* nativeQuery :表示 该sql 是否是标准的 sql语句。
* 默认值为false 。执行的时候会告诉底层这是一个非标准的sql语句,底层会去执行hibernate解析hql语句为标准的sql语句
* true 标准该sql 为标准的sql语句
* SELECT * FROM t_users where name=?1 and id=?2
* 这个 “?1”的1 表示是获取入参的第一个参数 “?2” 的2 表示获取入参的第二个参数
*
* @param id
* @return
*/
@Query(value = "select name from user where id=?1", nativeQuery = true)
String queryUserName(Long id);
@Query(value = "update User set name =?1 where id=?2")
@Modifying
//需要执行一个更新操作
void updateUsersNameById(String name, Integer id);
@Query(value = "SELECT * FROM user where name=?1 ", nativeQuery = true)
List<User> queryByNameUseSQL(String name);
}
上面的已经可以做简单的查询了,但是我们查询不可能那么简单,多条件查询,where,in,=,is null,order by等条件,但是简单的查询无法满足我们的要求,但是我们UserRepository 继承了JpaSpecificationExecutor类,可以满足我们的要求,简单介绍他的使用
/**
* 根据名称查询对应的用户
*
* @param name
*/
public void findUsersByName(String name) {
Specification<User> specification = new Specification<User>() {
/**
* 封装了单个的查询条件
* @param root 查询对象的属性的封装
* @param criteriaQuery 封装了需要执行查询中的各个部分的信息: select from order by 等信息
* @param criteriaBuilder 查询条件的构造器 定义不同的查询条件的
* @return
*/
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
//封装条件,相当于where name ='张三'
Predicate pre = criteriaBuilder.equal(root.get("name"), name);
return pre;
}
};
List<User> users = userRepository.findAll(specification);
}
/**
* 多条件查询,使用and关联查询条件
*
* @param name
* @param addr
*/
public void findUsersByConditions(String name, String addr) {
Specification<User> spec = new Specification<User>() {
/***
* Predicate :封装了单个的查询条件
* @param root 查询对象的属性的封装
* @param criteriaQuery 封装了需要执行查询中的各个部分的信息: select from order by 等信息
* @param criteriaBuilder CriteriaBuilder:查询条件的构造器 定义不同的查询条件的
* @return
*/
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
// where name ='张三' and addr='上海'
List<Predicate> list = new ArrayList<>();
list.add(criteriaBuilder.equal(root.get("name"), name));
list.add(criteriaBuilder.equal(root.get("addr"), addr));
Predicate[] arr = new Predicate[list.size()];
return criteriaBuilder.and(list.toArray(arr));
}
};
List<User> list = userRepository.findAll(spec);
}
/**
* 多个条件查询,使用or关联查询条件
*
* @param name
* @param addr
* @param id
*/
public void findUsersByConditions(String name, String addr, Long id) {
/*
* Specification 封装了一个查询的对象
*/
Specification<User> spec = new Specification<User>() {
/***
* Predicate :封装了单个的查询条件
* @param root 查询对象的属性的封装
* @param query 封装了需要执行查询中的各个部分的信息: select from order by 等信息
* @param cb CriteriaBuilder:查询条件的构造器 定义不同的查询条件的
*/
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// where (name ='张山' and addr=上海) or id=2
return cb.or(cb.and(cb.equal(root.get("name"), name), cb.equal(root.get("addr"), addr)), cb.equal(root.get("id"), id));
}
};
List<User> list = userRepository.findAll(spec);
}
上面的代码是通过创建一个Specification对象,在内部类中重写方法,并返回Specification对象,Specification对象就相当于是对查询条件的封装,然后再用JpaRepository进行查询
下面为了方便我们使用,对Specification进行了封装成工厂类
import org.springframework.data.jpa.domain.Specification;
import javax.persistence.criteria.Order;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Date;
public class SpecificationFactory {
// 时区对象
private static final ZoneOffset ZONE_OFFSET = ZoneOffset.of("+8");
// 日期时间格式化对象
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 模糊匹配头部, like %?1
*
* @param fieldName 实体中的字段名称
* @param value 固定值
* @return 查询条件的封装对象
*/
public static Specification likeStart(String fieldName, String value) {
return (root, query, cb) -> cb.like(root.get(fieldName), "%" + value);
}
/**
* 模糊匹配尾部, like ?1%
*
* @param fieldName 实体中的字段名称
* @param value 固定值
* @return 查询条件的封装对象
*/
public static Specification likeEnd(String fieldName, String value) {
return (root, query, cb) -> cb.like(root.get(fieldName), value + "%");
}
/**
* 完全模糊匹配 , like %?1%
*
* @param fieldName 实体中的字段名称
* @param value 固定值
* @return 查询条件的封装对象
*/
private static Specification like(String fieldName, String value) {
return likeBuild(fieldName, "%" + value + "%");
}
private static Specification likeBuild(String fieldName, String value) {
return (root, query, cb) -> cb.like(root.get(fieldName), "%" + value + "%");
}
/**
* 任意值相等比较
*
* @param fieldName 实体中的字段名称
* @param value 比较值
* @return 查询条件的封装对象
*/
public static <T> Specification eq(String fieldName, T value) {
return (root, query, cb) -> cb.equal(root.get(fieldName), value);
}
/**
* 比较日期区间
*
* @param fieldName 实体中的字段名称
* @param min 最小日期值
* @param max 最大日期值
* @return 查询条件的封装对象
*/
public static Specification betweenDate(String fieldName, Date min, Date max) {
LocalDateTime lmin = LocalDateTime.ofInstant(min.toInstant(), ZONE_OFFSET);
LocalDateTime lmax = LocalDateTime.ofInstant(max.toInstant(), ZONE_OFFSET);
return (root, query, cb) -> cb.between(root.get(fieldName).as(String.class), DATE_TIME_FORMATTER.format(lmin), DATE_TIME_FORMATTER.format(lmax));
}
/**
* 比较任意值的区间
*
* @param fieldName 实体中的字段名称
* @param min 最小值
* @param max 最大值
* @param
* @return 查询条件的封装对象
*/
public static <T extends Comparable> Specification between(String fieldName, T min, T max) {
return (root, query, cb) -> cb.between(root.get(fieldName), min, max);
}
/**
* 数值大于比较
*
* @param fieldName 实体中的字段名称
* @param value 比较值
* @param
* @return 查询条件的封装对象
*/
public static <T extends Number> Specification gt(String fieldName, T value) {
return (root, query, cb) -> cb.gt(root.get(fieldName).as(Number.class), value);
}
/**
* 数值大于等于比较
*
* @param fieldName 实体中的字段名称
* @param value 比较值
* @param
* @return 查询条件的封装对象
*/
public static <T extends Comparable> Specification gte(String fieldName, T value) {
return (root, query, cb) -> cb.greaterThanOrEqualTo(root.get(fieldName), value);
}
/**
* 数值小于比较
*
* @param fieldName 实体中的字段名称
* @param value 比较值
* @param
* @return 查询条件的封装对象
*/
public static <T extends Number> Specification lt(String fieldName, T value) {
return (root, query, cb) -> cb.lt(root.get(fieldName).as(Number.class), value);
}
/**
* 数值小于等于比较
*
* @param fieldName 实体中的字段名称
* @param value 比较值
* @param
* @return 查询条件的封装对象
*/
public static <T extends Comparable> Specification lte(String fieldName, T value) {
return (root, query, cb) -> cb.lessThanOrEqualTo(root.get(fieldName), value);
}
/**
* 字段为null条件
* @param fieldName 实体中的字段名称
* @return 查询条件的封装对象
*/
public static Specification isNull(String fieldName){
return (root, query, cb) -> cb.isNull(root.get(fieldName));
}
/**
* 字段不为null条件
* @param fieldName 实体中的字段名称
* @return 查询条件的封装对象
*/
public static Specification isNotNull(String fieldName){
return (root, query, cb) -> cb.isNotNull(root.get(fieldName));
}
/**
* in 条件
* @param fieldName
* @param values
* @return
*/
public static Specification in(String fieldName,Object...values){
return (root, query, cb) -> root.get(fieldName).in(values);
}
/**
* 升序
* @param fieldName
* @return
*/
public static Specification asc(String fieldName) {
return (root,query,cb)-> query.orderBy(cb.asc(root.get(fieldName))).getRestriction();
}
/**
* 降序
* @param fieldName
* @return
*/
public static Specification desc(String fieldName) {
return (root,query,cb)-> query.orderBy(cb.desc(root.get(fieldName))).getRestriction();
}
/**
* 排序
* @param fieldName
* @return
*/
public static Specification orderBy(String...fieldName){
return (root, query, cb) -> {
Order desc = cb.desc(root.get(fieldName[0]));
Order asc = cb.asc(root.get(fieldName[1]));
return query.orderBy(desc, asc).getRestriction();
};
}
}
service层
package com.lcj.service.iml;
import com.lcj.dao.UserRepository;
import com.lcj.factory.SpecificationFactory;
import com.lcj.po.User;
import com.lcj.service.UserService;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
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.Date;
import java.util.List;
import java.util.Optional;
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserRepository userRepository;
@Override
public void addUser(User user) {
userRepository.save(user);
}
@Override
public User queryUserById(Long id) {
User user = userRepository.findById(id).get();
return user;
}
@Override
public List<User> queryAllUser() {
List<User> users = userRepository.findAll();
return users;
}
@Override
public void updateUserById(User user) {
userRepository.save(user);
}
@Override
public void deleteUserById(Long id) {
userRepository.deleteById(id);
}
@Override
public String queryUserName(Long id) {
String userName = userRepository.queryUserName(id);
return userName;
}
/**
* 根据名称分页查询对应的用户,
* @param page 当前页码从0开始
* @param size 当前页的数量
* @param name 查询条件,name
* @return
*/
public Page<User> findUsersByName(Integer page, Integer size,String name) {
//创建PageRequest的实例,设置分页参数
PageRequest pageRequest = PageRequest.of(page, size, Sort.Direction.ASC, "id");
Specification<User> specification = new Specification<User>() {
/**
* 封装了单个的查询条件
* @param root 查询对象的属性的封装
* @param criteriaQuery 封装了需要执行查询中的各个部分的信息: select from order by 等信息
* @param criteriaBuilder 查询条件的构造器 定义不同的查询条件的
* @return
*/
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
//封装条件,相当于where name ='张三'
Predicate pre = criteriaBuilder.equal(root.get("name"), name);
return pre;
}
};
return userRepository.findAll(specification, pageRequest);
}
/**
* 多条件查询,使用and关联查询条件
*
* @param name
* @param addr
*/
public void findUsersByConditions(String name, String addr) {
Specification<User> spec = new Specification<User>() {
/***
* Predicate :封装了单个的查询条件
* @param root 查询对象的属性的封装
* @param criteriaQuery 封装了需要执行查询中的各个部分的信息: select from order by 等信息
* @param criteriaBuilder CriteriaBuilder:查询条件的构造器 定义不同的查询条件的
* @return
*/
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
// where name ='张三' and addr='上海'
List<Predicate> list = new ArrayList<>();
list.add(criteriaBuilder.equal(root.get("name"), name));
list.add(criteriaBuilder.equal(root.get("addr"), addr));
Predicate[] arr = new Predicate[list.size()];
return criteriaBuilder.and(list.toArray(arr));
}
};
List<User> list = userRepository.findAll(spec);
}
/**
* 多个条件查询,使用or关联查询条件
*
* @param name
* @param addr
* @param id
*/
public void findUsersByConditions(String name, String addr, Long id) {
/*
* Specification 封装了一个查询的对象
*/
Specification<User> spec = new Specification<User>() {
/***
* Predicate :封装了单个的查询条件
* @param root 查询对象的属性的封装
* @param query 封装了需要执行查询中的各个部分的信息: select from order by 等信息
* @param cb CriteriaBuilder:查询条件的构造器 定义不同的查询条件的
*/
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// where (name ='张山' and addr=上海) or id=2
return cb.or(cb.and(cb.equal(root.get("name"), name), cb.equal(root.get("addr"), addr)), cb.equal(root.get("id"), id));
}
};
List<User> list = userRepository.findAll(spec);
}
public List<User> testJspSpecification(String name) {
//where name='张三'
Specification spec = SpecificationFactory.eq("name", name);
List<User> list = userRepository.findAll(spec);
return list;
}
public void testJspSpecification(User user) {
// 构造条件
Specification specification = SpecificationFactory.lt("birthDate", new Date().getTime());
specification = specification.and(SpecificationFactory.likeEnd("name", user.getName()));
specification = specification.and(SpecificationFactory.isNull("addr"));
// 打印结果
List<User> systemUserList = this.userRepository.findAll(specification);
for (User user1 : systemUserList) {
System.out.println(user1);
}
}
/**
* 根据地址查询所有人,然后按id降序排列
* @param addr
* @return
*/
public List<User> findAllByAddr(String addr, String order){
Specification specification = SpecificationFactory.eq("addr", addr);
specification=specification.and(SpecificationFactory.desc(order));
List<User> users = userRepository.findAll(specification);
return users;
}
}
测试类
package com.lcj;
import com.lcj.dao.UserRepository;
import com.lcj.factory.SpecificationFactory;
import com.lcj.po.User;
import com.lcj.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.jpa.domain.Specification;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
@SpringBootTest(classes = SpringbootJpaApplication.class)
public class SpringbootJpaApplicationTests {
@Resource
private UserService userService;
@Resource
private UserRepository userRepository;
@Test
public void test() {
User user = new User()
.setName("张三")
.setAddr("湖南")
.setBirthDate(new Date());
userService.addUser(user);
}
@Test
public void test1() {
User user = userService.queryUserById(1L);
System.out.println(user.toString());
}
@Test
public void test2() {
List<User> users = userService.queryAllUser();
System.out.println(users);
}
@Test
public void test3() {
User user = userService.queryUserById(1L);
user.setAddr("海南");
userService.updateUserById(user);
System.out.println(user.toString());
}
@Test
public void test4() {
userService.deleteUserById(1L);
}
@Test
public void test5() {
String name = userService.queryUserName(1L);
System.out.println(name);
}
@Test
public void test6() {
List<User> users = userService.findAllByAddr("上海", "id");
System.out.println(users);
}
@Test
public void test7() {
//查询name不为null或者addr是上海
Specification spe = SpecificationFactory.isNotNull("name");
spe.or(SpecificationFactory.eq("addr", "上海"));
List<User> users = userRepository.findAll(spe);
System.out.println(users);
}
}