+-
SpringBoot微服务使用JPA告别繁琐的XML和SQL

(点击上方公众号,可快速关注)


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!!!