一篇文章带你了解什么是 Spring JPA

什么是JDBC

JDBC(Java DataBase Connectivity),java数据库连接是用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由用Java语言编写的类和接口组成。

什么是JDBCTemplate

Template的字面意思是模板,Spring框架为我们提供了JDBCTemplate,它是访问数据库的类库,是Spring对JDBC的封装。

通俗点说就是Spring对jdbc的封装的模板

注意,JDBCTemplate不是ORM框架

JDBCTemplate如何使用

UserDao .java

@Repository
public class UserDao {
    
    

    @Autowired
    JdbcTemplate jdbcTemplate;


    public int addUser(User user){
    
    
    	return jdbcTemplate.update("INSERT INTO sys_user(username,jobs,phone) VALUE (?,?,?)",
    user.getName,user.getJobs,user.getPhone);
    }
    public int updateUser(User user){
    
    
    	return jdbcTemplate.update("UPDATE sys_user SET username=?,jobs=?,phone=? WHERE id=?",
    user.getName,user.getJobs,user.getPhone,user.getId);
    }
    public int deleteUser(Integer id){
    
    
    	return jdbcTemplate.update("DELETE FROM sys_user WHERE id=?",id);
    }
    public User getUserById(Integer id){
    
    
    	return jdbcTemplate.queryForObject("SELECT * FROM sys_user WHERE id =?",new BeanPropertyRowMapper<>(User.class),id);
    }
}

UserService

@Service
public class UserService {
    
    

    @Autowired
    UserDao userDao;
    
    public int addUser(User user){
    
    
    	return userDao.addUser(user);
    }
    public int updateUser(User user){
    
    
    	return userDao.updateUser(user);
    }
    public int deleteUser(Integer id){
    
    
    	return userDao.deleteUser(id);
    }
    public User getUserById(Integer id){
    
    
    	return userDao.getUserById(id);
    }
}

这是一个简单的JDBCTemplate,controller调用service,然后service调用userDao类中的方法,从而操作数据库。

可以看到,SQL语句和java代码高度耦合,不宜维护,而且可读性也不高。

Mybatis改造

我们将以上的方式切换到 Mybatis。

首先删掉UserDao,添加mapper映射文件UserMapper ,

@Mapper
public interface UserMapper{
    
    
    int addUser(User user);
    int deleteUser(int id);
    int updateUser(User user);
    User getUserById(Integer id);
}

然后在指定的目录下,生成UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.layman.mapper.UserMapper">

	<sql id="selectUserVo">
        select d.username, d.jobs, d.phone from sys_user d
    </sql>
  
    <select id="getUserById" parameterType="Long" resultType="User">
		<include refid="selectUserVo"/>
		WHERE id = = #{id}
	</select>
    
    <insert id="addUser" parameterType="User">
 		insert into sys_user (
 			<if test="username!= null and username!= 0">username,</if>
 			<if test="jobs!= null and jobs!= 0">jobs,</if>
 			<if test="phone!= null and phone!= ''">phone,</if>
 			create_time
 		)values(
 			<if test="username!= null and username!= 0">#{username},</if>
 			<if test="jobs!= null and jobs!= 0">#{jobs},</if>
 			<if test="phone!= null and phone!= ''">#{phone},</if>
 			sysdate()
 		)
	</insert>
	<delete id="deleteUser">
		delete from sys_user where id= #{id}
	</delete>
</mapper> 

最后改造Service

@Service
public class UserService {
    
    

    @Autowired
    UserMapper userMapper;
    
    public int addUser(User user){
    
    
    	return userMapper.addUser(user);
    }
    public int updateUser(User user){
    
    
   	 return userMapper.updateUser(user);
    }
    public int deleteUser(Integer id){
    
    
   	 return userMapper.deleteUser(id);
    }
    public User getUserById(Integer id){
    
    
    	return userMapper.getUserById(id);
    }
}

可以看到,这样改造之后,SQL语句和业务代码分离,解耦,代码可读性和扩展性也得到提升。

但是,这样的改造之后,随之而来,暴露了问题。

如果要实现多个表的CRUD,就需要定义不同的实体类,然后写对应的mapper文件,然后写雷同的CRUD方法。这显然是繁琐重复且没有技术含量的操作

JPA的概念

JPA (Java Persistence API) 是 Sun 官方提出的 Java 持久化规范。它为 Java 开发人员提供了一种对象关系映射工具来管理 Java 应用中的数据。

它的出现,主要是为了简化现有的持久化开发工作,以及整合 ORM 技术,结束现在 Hibernate,TopLink,Mybatis等很多ORM 框架各自为营的凌乱局面。

JPA 在充分吸收了现有Hibernate,TopLink,JDO 等ORM框架的基础上发展而来的,具有易于使用,伸缩性强等优点。

换言之,JPA 是规范,而 Hibernate,TopLink,Mybatis则实现了 JPA 规范。

如何配置

pom.xml

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

application.properties

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/layman?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.jpa.properties.hibernate.hbm2ddl.auto=create
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=true
  • spring.jpa.properties.hibernate.hbm2ddl.auto 表示是否自动建表,如果不配置,表示禁用自动建表功能。它有以下4种配置:

    1. create :每次加载Hibernate时都会删除上一次生成的表(包括数据),然后重新生成新表,即使两次没有任何修改也会这样执行。适用于每次执行单测前清空数据库的场景。

    2. create-drop:每次加载Hibernate时都会生成表,但当SessionFactory关闭时,所生成的表将自动删除

    3. update最常用的配置,第一次加载Hibernate时创建数据表(前提是需要先有数据库),以后加载Hibernate时不会删除上一次生成的表,会根据实体类进行更新,只新增字段,不会删除字段(即使实体中已经删除)。

    4. validate:每次加载Hibernate时都会验证数据表结构,只会和已经存在的数据表进行比较, 根据实体类修改表结构,但不会创建新表。

  • spring.jpa.show-sql=true : 在执行数据库操作,控制台打印 sql 语句,方便我们检查。

  • spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect :数据库的方言配置

新建用户实体类

import lombok.Data;

import javax.persistence.*;

/**
 * @author layman
 * @description: 用户实体类
 * @date 2021/10/29
 */
@Data
@Entity(name = "sys_user")
public class User {
    
    

    @Id
    @GeneratedValue
    private long id;

    @Column(name = "sys_user_name",nullable = false,unique = true,length = 35)
    private String userName;

    @Transient
    private Integer age;

    // 头发数量(没办法,老天嫉妒我英俊的容颜,所以不给我头发)
    @Column(name = "sys_hairnumber",is)
    private Integer hairNumber;

	// 是否有女朋友(别想了,肯定没有)
    @Column(name = "sys_girlfriend")
    private boolean hasGirlFriend;
}
  • @Entity注解 表示 该实体类会和数据库中的表建立关联关系,首次启动项目时,默认会在数据库中生成具有相同名称的的表,可以通过注解中的 name 属性来修改表名称, 如 @Entity(name=“sys_user”) , 这样数据库中表的名称则是 sys_user。该注解非常重要,如果没有该注解,首次启动项目时,数据库不会生成对应的表。
  • @Table 注解可以用来修改表的名字,该注解完全可以忽略不用,因为@Entity 注解已具备该注解的功能。
  • @Id 注解表明该属性字段是主键,该属性必须具备,否则报错。
  • @GeneratedValue 该注解通常和 @Id 主键注解一起使用,用来定义主键的生成策略,该注解有多种策略,先总结如下:
主键策略 说明
@GeneratedValue(strategy= GenerationType.IDENTITY) 主键自增,由数据库自动生成,在 MySQL中使用最频繁,Oracle不支持
@GeneratedValue(strategy= GenerationType.AUTO) 主键由程序控制,是 默认 的主键生成策略,oracle 默认是序列化,mysql 默认是主键自增
@GeneratedValue(strategy= GenerationType.AUTO) 根据底层数据库的序列来生成主键,Oracle支持,MySQL不支持
@GeneratedValue(strategy= GenerationType.TABLE) 使用一个特定的数据库表格来保存主键,较少使用

以 MySQL 数据库 为例, IDENTITYAUTO 用的较多,其中 IDENTITY用的更多一些。

补充

  • @Column 是属性注解,该注解可以定义一个字段映射到数据库属性的具体特征,比如字段长度,映射到数据库时属性的具体名字等。

  • @Transient 是属性注解,该注解标注的字段 不会被映射到数据库 中。

声明UserRepository

该类的声明 必须继承 JpaRepository

package com.banana.springcloud.test;

import org.springframework.data.jpa.repository.JpaRepository;
/**
 * @author layman
 * @description: UserRepository
 * @date 2021/10/29
 */
public interface UserRepository extends JpaRepository<User, Long> {
    
    
}

UML类图

在这里插入图片描述

可以看到 JpaRepository 继承了 CrudRepository,后者定义了很多基本的Crud方法,可以拿来直接用。

实际工作的类是 SimpleJpaRepository

@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID>{
    
    
}

简单实例

package com.banana.springcloud.test;

import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
/**
 * @author layman
 * @description: 简单测试
 */
public class UserService {
    
    

    @Autowired
    private UserRepository userRepository;

    @Test
    public void addUser(){
    
    

        // 设置user对象,没头发没对象的苦逼码农
        User u = new User();
        u.setUserName("layman");
        u.setAge(25);
        // 地中海发型
        u.setHairNumber(25);
        u.setHasGirlFriend(false);

        userRepository.save(u);
    }
}

Spring JPA 的查询方法

基本查询

spring data 默认实现,只要继承JpaRepository,就可以使用
在这里插入图片描述

自定义查询

根据方法名来自动生成SQL,主要的语法是 findXXBy, readAXXBy, queryXXBy, countXXBy, getXXBy后面跟上实体类中的属性名称,

import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

/**
 * @author layman
 * @description: UserRepository
 * @date 2021/10/29
 */
public interface UserRepository extends JpaRepository<User, Long> {
    
    

    User findByUserName(String userName);

    List<User> findByUserNameLike(String userName);


    User findByUserNameIgnoreCase(String userName);
}

复杂查询

在实际开发中,会用到 分页、删选、连表 等查询,此时需要特殊的方法或者自定义 SQL。

以分页查询为例,分页查询非常普遍。

spring data jpa已经实现了分页功能,在查询方法中,需要传入参数Pageable,建议Pageable做为最后一个传入参数。

Pageable是 spring 封装的分页实现类,使用的时候需要传入页数每页条数排序规则

简单演示

UserRepository.java

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

/**
 * @author layman
 * @description: UserRepository
 * @date 2021/10/29
 */
public interface UserRepository extends JpaRepository<User, Long> {
    
    

    // 复杂查询(分页查询)
    Page<User> findALL(Pageable pageable);

    Page<User> findByUserName(String userName,Pageable pageable);
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.PostMapping;

/**
 * @author layman
 * @description: 简单测试
 */
public class UserService {
    
    

    @Autowired
    private UserRepository userRepository;

    @PostMapping("/list")
    public Page<User> testPageQuery(String userName, Integer pageNo, Integer pageSize) {
    
    
        /**
         * pageNo: 页数
         * pageSize: 每页条数
         * Sort.Direction.DESC: 降序
         * id: 根据id进行降序排列
         */
        Pageable pageable = new PageRequest(pageNo - 1, pageSize, Sort.Direction.DESC, "id");
        Page<User> userList = userRepository.findByUserName(userName, pageable);
        return userList;
    }
}

自定义查询

Spring data 大部分 SQL都可以根据方法名定义的方式来实现,但是由于某些原因,我们想使用自定义的 SQL 来查询,spring data 也是可以支持的,如下所示:

package com.banana.springcloud.test;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * @author layman
 * @description: UserRepository
 * @date 2021/10/29
 */
public interface UserRepository extends JpaRepository<User, Long> {
    
    

    /**
	 * SQL操作的是实体类的属性,最后会映射到数据库表的字段上
	 * ?1 :对应方法入参的第一个参数
	 * ?2 :对应方法入参的第二个参数
	 */
    @Modifying
    @Query("update User u set u.userName = ?1 where u.id = ?2")
    int modifyByIdAndUserId(String  userName, Long id);


    @Transactional(timeout = 10)
    @Query("select u from User u where u.userName = ?1")
    User findByEmailAddress(String userName);
}

猜你喜欢

转载自blog.csdn.net/single_0910/article/details/121036519