一、前言
Spring Boot JPA 是 Spring 在 ORM 框架的基础上封装的一套 JPA 应用框架,具体的数据访问和操作实现还是依赖于 ORM 框架来完成,Spring Boot JPA 只是完成了接口操作的标准封装,包括增删改查等在内的常用功能,可以帮助开发者降低学习成本,同时极大的提升开发效率。
以 Spring Boot 的2.0版本为例,Spring Boot JPA 的底层依赖于 Hibernate 框架来完成数据库的访问和操作,如果你熟悉 Hibernate 的框架使用,那么可以轻松的上手并使用它。
二、项目实战
1.引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
2.在application.properties增加相关配置
#数据库配置
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/my_db?useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
其中spring.jpa.hibernate.ddl-auto
参数是hibernate
的一个配置属性,主要作用有:自动创建、更新、验证数据库表结构。
相关的可选参数如下:
create
:每次加载 hibernate 相关实体表时会删除上一次生成的表,然后按照最新的 model 类生成新表,会造成数据库表数据丢失;create-drop
:每次加载 hibernate 时会根据 model 类生成新表,当服务关闭时,表自动删除,通常用于测试;update
:常用属性,第一次加载 hibernate 时会根据 model 类自动建表,以后加载 hibernate 时根据 model 类自动更新表结构,但是不会删除表中的数据;validate
:每次加载 hibernate 时会验证数据库表的结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值;
spring.jpa.show-sql
参数用于打印出自动生成的 SQL,方便调试。
3.创建实体
package com.example.dataproject.entity;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.*;
/**
* @author qx
* @date 2024/8/7
* @des 用户表
*/
@Entity
@Table(name = "t_user")
@Getter
@Setter
@ToString
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Integer age;
private String phone;
}
@Entity
注解用于标识User类是一个持久化的实体类;@Table
注解用于标识User类映射到数据库中的表名称;@Id
注解用于标识User映射到数据库的主键字段@GeneratedValue
注解用于标识User映射到数据库的主键为自增类型@Column
注解用于标识User映射到数据库的字段相关信息
4.创建数据访问接口
package com.example.dataproject.dao;
import com.example.dataproject.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* @author qx
* @date 2024/8/7
* @des 数据持久层
*/
public interface UserDao extends JpaRepository<User, Long> {
}
5.单元测试
package com.example.dataproject;
import com.example.dataproject.dao.UserDao;
import com.example.dataproject.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
class DataProjectApplicationTests {
@Autowired
private UserDao userDao;
@Test
void add() {
//新增数据
User user = new User();
user.setName("admin");
user.setAge(20);
user.setPhone("18899998888");
userDao.save(user);
}
@Test
void query() {
//查询数据
List<User> userList = userDao.findAll();
System.out.println(userList);
}
@Test
void update() {
//修改数据
User user = userDao.findById(1L).orElse(null);
if (user != null) {
user.setAge(22);
userDao.save(user);
}
}
@Test
void delete() {
//删除数据
userDao.deleteById(1L);
}
}
我们启动程序先执行add方法,并查看数据表。
测试query方法:
测试update方法:
测试delete方法:
三、使用进阶
1.简单查询
Spring Boot JPA 不仅为开发者封装了常用的模板方法,还支持根据方法名来动态生成 SQL 语句,比如findByXX,countByXX,getByXX后面跟属性名称,当调用方法的时候会自动生成响应的 SQL 语句,具体示例如下:
package com.example.dataproject.dao;
import com.example.dataproject.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* @author qx
* @date 2024/8/7
* @des 数据持久层
*/
public interface UserDao extends JpaRepository<User, Long> {
User findByPhone(String phone);
Integer countByName(String name);
}
编写单元测试验证内容的正确性。
@Test
public void queryTest(){
User user = userDao.findByPhone("18899998888");
System.out.println("user="+user);
Integer count = userDao.countByName("admin");
System.out.println("count="+count);
}
执行查询:
方法上支持 SQL 语句中的关键字,比如And
、Or
、Like
、OrderBy
等。
具体关键字上的使用和生成的 SQL 对应的关系如下:
2.复杂查询操作
在实际的开发过程中,由于业务的需要,我们经常需要编写复杂的 SQL 语句,比如链表查询,分页查询等,这个时候就需要用到自定义 SQL 语句的操作了。
其实大部分的 SQL 语句都可以通过方法来动态生成,如果想自定义 SQL 查询,Spring Boot JPA 也是支持的,操作上很简单。
在接口方法上,添加@Query
注解,即可实现自定义 SQL 语句;如果涉及到新增、修改和删除操作,需要再加上@Modifying
注解,同时也需要添加@Transactional
注解事务支持。
具体示例如下:
package com.example.dataproject.dao;
import com.example.dataproject.entity.User;
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;
/**
* @author qx
* @date 2024/8/7
* @des 数据持久层
*/
public interface UserDao extends JpaRepository<User, Long> {
User findByPhone(String phone);
Integer countByName(String name);
@Query("select u from User u where u.phone=?1")
User findByUserPhone(String phone);
@Modifying
@Transactional
@Query("delete from User u where u.name=?1")
int deleteByName(String name);
}
编写单元测试验证内容的正确性。
@Test
void sqlTest() {
User user = userDao.findByUserPhone("18899998888");
System.out.println("user=" + user);
}
@Test
void deleteTest() {
userDao.deleteByName("admin");
}
执行查询测试方法:
执行删除测试方法:
值得注意的是:这里自定义的 SQL 语句并非数据库中的 SQL 语句,而是 hibernate 所支持 SQL 语句,简称 hsql,例如表名要采用 Java 实体而非数据库中真实的表名,否则可能会报错。
3.分页查询
分页查询,在实际的业务开发中非常常见,其实 Spring Boot JPA 已经帮助开发者封装了分页查询的方法逻辑,在查询的时候传入Pageable
参数即可。
当前的数据表存在两条数据:
编写测试方法:
@Test
void pageTest() {
int page = 0, size = 10;
Pageable pageable = PageRequest.of(page, size);
Page<User> userPage = userDao.findAll(pageable);
System.out.println("总页数:" + userPage.getTotalPages());
System.out.println("总数量:" + userPage.getTotalElements());
System.out.println("数据:" + userPage.getContent());
}
执行测试方法:
4.多表查询
多表查询,也是实际开发中经常会碰到的场景,Spring Boot JPA 提供了两种实现方式,第一种是利用 Hibernate 的级联查询来实现,第二种是自定义 SQL 语句来实现。
第一种就不多说了,主要通过@OneToOne
、@OneToMany
、@ManyToOne
、@ManyToMany
和@JoinTable
注解来完成多表的级联查询,不过这种方式需要在数据库层面建立外键关联,通过外键来完成级联查询,不推荐采用。
下面我们来介绍一下自定义 SQL 语句来实现,实现起来也很简单,示例如下:
我们先创建一个实体类,关联用户ID
package com.example.dataproject.entity;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.math.BigDecimal;
/**
* @author qx
* @date 2024/9/3
* @des
*/
@Entity
@Table(name = "t_order")
@Getter
@Setter
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long userId;
private String goodsName;
private BigDecimal totalPrice;
}
下面我们来介绍一下自定义 SQL 语句来实现,实现起来也很简单,示例如下:
package com.example.dataproject.dao;
import com.example.dataproject.entity.Order;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
import java.util.Map;
/**
* @author qx
* @date 2024/9/3
* @des
*/
public interface OrderDao extends JpaRepository<Order, Long> {
@Query(nativeQuery = true, value = "select u.name,u.phone,o.* from t_order o INNER JOIN t_user u on u.id=o.user_id")
List<Map<String, Object>> findCustomer();
}
编写单元测试,验证代码的正确性。
package com.example.dataproject;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.example.dataproject.dao.OrderDao;
import java.util.List;
import java.util.Map;
@SpringBootTest
class DataProjectApplicationTests {
@Autowired
private OrderDao orderDao;
@Test
void testLink(){
List<Map<String, Object>> maps = orderDao.findCustomer();
System.out.println(JSON.toJSONString(maps));
}
}
输出结果如下:
5.属性映射屏蔽操作
如果某个实体类中的属性,不想被映射到数据库,可以添加@Transient
注解来实现,示例如下。
package com.example.dataproject.entity;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.math.BigDecimal;
/**
* @author qx
* @date 2024/9/3
* @des
*/
@Entity
@Table(name = "t_order")
@Getter
@Setter
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long userId;
private String goodsName;
private BigDecimal totalPrice;
@Transient
private String desc;
}