
(点击上方公众号,可快速关注)
SpringBoot微服务使用JPA告别繁琐的XML和SQL
什么是JPA?
全称Java Persistence API,可以通过注解或者XML描述【对象-关系表】之间的映射关系,并将实体对象持久化到数据库中。
JPA的主要目标之一就是提供更加简单的编程模型:在JPA框架下创建实体和创建Java 类一样简单,没有任何的约束和限制,只需要使用 javax.persistence.Entity进行注释,JPA的框架和接口也都非常简单,没有太多特别的规则和设计模式的要求,开发者可以很容易地掌握。
JPA基于非侵入式原则设计,因此可以很容易地和其它框架或者容器集成。
JPA为我们提供一下支持:
1、ORM映射元数据功能:JPA支持XML和注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中;如:@Entity、@Table、@Column、@Transient等注解。
3、JPQL查询语言支持:通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合。如:from User u where u.name = ?
说了这么多,但是JPA其实仅仅是一种规范,仅仅定义了一些接口,所以使用时是需要实现的,而Hibernate就是实现了JPA接口的ORM框架。
什么是Spring Data?
Spring Data是一个用于简化数据库访问,并支持云服务的开源框架,并支持map-reduce框架和云计算数据服务。它还支持基于关系型数据库的数据服务,如Oracle RAC等。
对于拥有海量数据的项目,可以用SpringData来简化项目的开发,就如SpringFrameWork对JDBC、ORM的支持一样,SpringData会让数据的访问变得更加方便。
下图是SpringData框架下一些模块关系:
什么是Spring Data JPA?
Spirng Data JPA是Spring提供的一套简化JPA开发的框架,是Spring Data框架中的一个模块。它可以按照约定好的【方法命名规则】写dao层接口,就可以在不写接口实现的情况下,实现对数据库的访问和操作。
同时提供了很多除了CRUD之外的功能,如分页、排序、复杂查询等等。Spring Data JPA 可以理解为 JPA 规范的再次封装抽象,底层还是使用了 Hibernate 的 JPA 技术实现。
JPA、Hibernate、Spirng Data JPA、ORM之间的关系如下图所示:
我们在SpringBoot微服务应用中集成的就是SpringDataJPA。集成方法非常简单,本例中使用Mysql5.7数据库:
首先,依赖包:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
接着,在配置文件中开启,下面的例子是使用Mysql5.7数据库时的配置:
spring:
# 数据源配置
datasource:
url: jdbc:mysql://localhost:3306/test_db?useSSL=false&allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
initialization-mode: always
data: classpath:static/data.sql
# 连接池类型
type: com.zaxxer.hikari.HikariDataSource
hikari:
pool-name: XtoadHikariPool
minimum-idle: 5
maximum-pool-size: 15
auto-commit: true
idle-timeout: 30000
max-lifetime: 1800000
connection-timeout: 30000
connection-test-query: SELECT 1
# JPA配置
jpa:
# 控制台打印SQL
show-sql: true
open-in-view: true
# 数据库使用mysql
database: mysql
hibernate:
# 自动更新或者创建数据表结构
ddl-auto: update
properties:
hibernate:
# 数据库方言配置
dialect: org.hibernate.dialect.MySQL57Dialect
# 格式化SQL
format_sql: true
下面我们就可以做代码开发了。
第一步、建立实体类和数据表进行映射,并且配置好映射关系:
package com.xtoad.ecms.baseinfo.model;
import com.xtoad.ecms.baseinfo.enums.Sex;
import com.xtoad.ecms.common.web.base.BaseModel;
import org.hibernate.annotations.Table;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.ManyToMany;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import java.util.Date;
import java.util.List;
/**
* 用户实体类
*
* @author xtoad
* @date 2021/02/25
*/
@Entity // 告诉JPA这是一个实体类(和数据表映射的类)
@Table(appliesTo = "user", comment = "用户表") // @Table来指定和哪个数据表对应;如果省略默认表名就是user;
public class User extends BaseModel {
private static final long serialVersionUID = -1616905035103332302L;
/**
* ID
*/
@Id // @Id 说明时主键ID
@GeneratedValue(strategy = IDENTITY) // 自动增长
private Long id;
/**
* 姓名
*/
@Column(nullable = true, columnDefinition = "varchar(100) comment '姓名'") // @Column指定表结构中的列,长度指定也可以参考下面的字段
private String name;
/**
* 登录名
*/
@Column(nullable = false, unique = true,length = 50, columnDefinition = " comment '登录名'")
private String loginNo;
/**
* 昵称
*/
@Column(nullable = true, columnDefinition = "varchar(100) comment '昵称'")
private String nickName;
/**
* 生日
*/
@Temporal(TemporalType.DATE) // @Temporal指定日期存储类型
@Column(nullable = true, columnDefinition = "date comment '生日'")
private Date birthday;
/**
* 性别
*/
@Column(nullable = false, columnDefinition = "varchar(10) comment '性别'")
@Enumerated(EnumType.STRING) // @Enumerated 指定枚举存储类型
private Sex sex;
//////// 忽略了get 和set 及其构造方法等,详细可以参照文末的完整源码
}
我们启动工程,会自动在数据库中创建user表,如果我们修改了该实体类,每次重启会自动更新表结构。
第二步、编写Repository接口来操作实体类对应的数据表(Dao层)
package com.xtoad.ecms.baseinfo.repository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
import com.xtoad.ecms.baseinfo.model.User;
import java.util.List;
/**
* 用户类Repository类
*
* @author xtoad
* @date 2021/02/25
*/
@Repository // @Repository声明该接口是一个持久层接口, 继承JpaRepository实现对数据库的基本操作,继承PagingAndSortingRepository实现分页
public interface IUserRepository extends JpaRepository<User, Long>, PagingAndSortingRepository<User, Long> {
/**
* 分页查询所有用户
*
* @param pageable
* @return
*/
@Override
Page<User> findAll(Pageable pageable);
/**
* 根据姓名模糊查询用户
*
* @param name 姓名
* @return 查询结果
*/
// 会自动生成SQL select name, loginNo …… from user where name like '%name%'
List<User> findByNameContaining(final String name);
/**
* 根据登录名查询用户信息
*
* @param email 邮箱
* @param phone 电话
* @param loginNo 登录名
* @return 查询结果
*/
// 会自动生成SQL select name, loginNo …… from user where email = 'email' or phone = 'phone' or loginNo = 'loginNo'
User findByEmailOrPhoneOrLoginNo(String email, String phone, String loginNo);
}
第三步、在Service层调用IUserRepository 即可:
package com.xtoad.ecms.baseinfo.service.impl;
import com.xtoad.ecms.baseinfo.converter.UserConverter;
import com.xtoad.ecms.baseinfo.dto.UserDTO;
import com.xtoad.ecms.baseinfo.model.User;
import com.xtoad.ecms.baseinfo.repository.IUserRepository;
import com.xtoad.ecms.baseinfo.service.IUserService;
import com.xtoad.ecms.common.web.UserContext;
import com.xtoad.ecms.common.web.exception.BusinessException;
import com.xtoad.ecms.common.web.exception.ResultCodeEnum;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Optional;
/**
* 用户服务实现类
*
* @author xtoad
* @date 2021/02/25
*/
@Service
public class UserServiceImpl implements IUserService {
@Resource
private IUserRepository userRepository;
@Resource
private UserConverter userConverter;
/**
* 根据id查询用户
*
* @param id 用户id
* @return 查询结果
*/
@Override
public UserDTO getUserById(final Long id) {
Optional<User> user = userRepository.findById(id);
if (user.isPresent()) {
return userConverter.toDto(user.get());
}
throw new BusinessException(ResultCodeEnum.NOT_FOUND);
}
/**
* 查询全部用户,不分页
*
* @return 查询结果
*/
@Override
public List<UserDTO> getAllUser() {
List<User> allUsers = this.userRepository.findAll(Sort.by(Sort.Direction.DESC,"id"));
return userConverter.toDtoList(allUsers);
}
/**
* 新增用户
*
* @param userDTO 保存对象
* @return 新增结果
*/
@Override
public UserDTO insert(final UserDTO userDTO) {
User user = userConverter.toModel(userDTO);
Date now = new Date();
user.setCreateUser(UserContext.getUserName());
user.setCreateTime(now);
user.setLastUpdateTime(now);
user.setLastUpdateUser(UserContext.getUserName());
user = userRepository.save(user);
return userConverter.toDto(user);
}
/**
* 删除用户
*
* @param id 删除对象id
*/
@Override
public void deleteById(final Long id) {
userRepository.deleteById(id);
}
/**
* 删除用户
*
* @param userDTO 删除对象
*/
@Override
public void delete(final UserDTO userDTO) {
User user = userConverter.toModel(userDTO);
userRepository.delete(user);
}
/**
* 更新用户
*
* @param userDTO 更新对象
* @return 更新结果
*/
@Override
public UserDTO update(final UserDTO userDTO) {
User user = userConverter.toModel(userDTO);
Date now = new Date();
user.setLastUpdateTime(now);
user.setLastUpdateUser(UserContext.getUserName());
user = userRepository.save(user);
return userConverter.toDto(user);
}
/**
* 批量新增用户
*
* @param userDTOList 新增对象
* @return 新增结果
*/
@Override
public List<UserDTO> batchInsert(final List<UserDTO> userDTOList) {
List<User> users = userConverter.toModelList(userDTOList);
Date now = new Date();
users.forEach(user -> {
user.setCreateUser(UserContext.getUserName());
user.setCreateTime(now);
user.setLastUpdateTime(now);
user.setLastUpdateUser(UserContext.getUserName());
});
users = userRepository.saveAll(users);
return userConverter.toDtoList(users);
}
/**
* 批量删除用户
*
* @param userDTOList 删除对象
*/
@Override
public void batchDelete(final List<UserDTO> userDTOList) {
List<User> users = userConverter.toModelList(userDTOList);
userRepository.deleteInBatch(users);
}
/**
* 批量更新用户
*
* @param userDTOList 更新对象
* @return 更新结果
*/
@Override
public List<UserDTO> batchUpdate(final List<UserDTO> userDTOList) {
List<User> users = userConverter.toModelList(userDTOList);
Date now = new Date();
users.forEach(user -> {
user.setLastUpdateTime(now);
user.setLastUpdateUser(UserContext.getUserName());
});
users = userRepository.saveAll(users);
return userConverter.toDtoList(users);
}
/**
* 根据姓名查询用户
*
* @param name 用户姓名
* @return 查询结果
*/
@Override
public List<UserDTO> getUserWithName(final String name) {
List<User> users = this.userRepository.findByNameContaining(name);
return userConverter.toDtoList(users);
}
/**
* 根据登录名查询用户信息
*
* @param loginNo 登录名
* @return 查询结果
*/
@Override
public UserDTO getUserByLoginNo(final String loginNo) {
User user = userRepository.findByEmailOrPhoneOrLoginNo(loginNo, loginNo, loginNo);
if (user == null) {
return null;
}
return userConverter.toDto(user);
}
}
我们完全是不需要使用SQL!!!