目录
SpringBoot 整合 Spring Data JDBC
总结:用起来跟 Spring Data JPA 差不多
什么是 JdbcTemplate?(Template译为模板)
Spring 框架对 JDBC 进行封装,使用 JdbcTemplate 方便实现对数据库操作
Spring Data JDBC
既不需要JPA、Hibernate这种ORM框架,但Spring Data还是提供了面向对象的封装。
——它相当于是一种轻量化的持久化技术,用起来比Spring JDBC更方便,但又不像Spring Data JPA那么需要大量注解、复杂
它相当于是一个折中。
Spring Data JDBC的功能
大致包括如下几方面功能:
- DAO接口只需继承CrudRepository或PagingAndSortingRepository,
Spring Data JDBC能为DAO组件生成实现类、类似于Spring Data JPA。
- Spring Data JDBC支持方法名关键字查询、类似于Spring Data JPA
-
- Spring Data JDBC支持用@Query定义查询语句。
-
- Spring Data JDBC同样支持DAO组件添加自定义的查询方法
——————通过添加额外的父接口,并为额外的该接口提供实现类,
Spring Data JDBC就能该实现类中的方法“移植”到DAO组件中。
- 一般不支持样本查询;也不支持Specification查询。
Spring Data JDBC VS Spring Data JPA
Spring Data JDBC相当于“轻量化”的Spring Data JPA。
Spring Data JDBC的功能不如Spring Data JPA强大(毕竟它底层没有ORM框架的加持)。
Spring Data JDBC也不需要处理复杂的ORM映射、实体对象的生命周期管理等,因此Spring Data JDBC用起来更简单。
——Spring Data JDBC有点类似MyBatis
Spring Data JDBC映射规则
Spring Data JDBC默认的处理方式是“约定优于配置”的同名映射:
程序操作User对象,Spring Data JDBC对应于操作user表。
对于id数据列,自动被映射到对象的id属性。
Spring Data JDBC的注解:
@Table:映射自定义的表名。非JPA注解
@Column:映射自定义的列名,非JPA注解
@Id:修饰标识属性,非JPA注解
@PersistenceConstructor:修饰主构造器。当你的映射类中有多个构造器时,
你希望Spring Data JDBC用哪个构造器来创建对象,
就用该注解来修饰该构造器。
Spring Data JDBC操作数据库方法:
1、全自动:方法名关键字查询。
2、半自动:@Query指定查询语句。
3、全手动:自己定义查询方法,即可用DataSource,也用JdbcTemplate。
Spring Data JDBC的编程步骤:
1、定义映射类,为Java类添加@Table、@Column、@Id和 @PersistenceConstructor
2、让DAO接口继承CrudRepository或PagingAndSortingRepository。
3、在DAO接口中定义方法名关键字查询、@Query查询、完全自定义查询(需要额外的接口和实现类)
代码演示
其实跟 JPA 差不多
User 类
UserDao接口,
根据方法名关键字查询---------全自动,不用自己写sql的
也有通过注解 @Query 进行查询的 —>自己定义查询语句-----半自动,可以自己写sql语句
也可以自定义Dao接口,用来自己写sql和封装数据
自定义接口和实现类,来实现数据的查询,
一个基于 DataSource , 一个基于 jdbcTemplate
UserDaoTest 测试
测试类
application.properties
pom.xml
一个是 spring data jdbc 的依赖 , 一个是 mysql 的依赖,
创建项目的时候这个 mysql 的依赖老是不完整,所以要记录下
完整代码
User
package cn.ljh.app.domain;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table;
/**
* author JH
*/
@Data
//此处不能添加JPA注解,因为此项目没有用到 JPA
@Table("user_inf")
public class User
{
@Column(value = "user_id")
@Id
private Integer id;
private String name;
private String password;
private int age;
/**
* @PersistenceConstructor
* 修饰主构造器。当你的映射类中有多个构造器时,
* 你希望Spring Data JDBC用哪个构造器来创建对象,就用该注解来修饰该构造器
*/
@PersistenceConstructor
public User()
{
}
public User(Integer id, String name, String password, int age)
{
this.id = id;
this.name = name;
this.password = password;
this.age = age;
}
@Override
public String toString()
{
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
", age=" + age +
'}';
}
}
UserDao
package cn.ljh.app.dao;
import cn.ljh.app.domain.User;
import org.springframework.data.jdbc.repository.query.Modifying;
import org.springframework.data.jdbc.repository.query.Query;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface UserDao extends CrudRepository<User,Integer>,CustomUserDao
{
// 继承 CrudRepository 接口后,就已经有通用的 CRUD 操作,无需自己来书写这些方法
//方法名关键字查询---------全自动
//根据名字模糊查询
List<User> findByNameLike(String namePattern);
//根据年龄大小进行范围查询
List<User> findByAgeGreaterThan(int startAge);
List<User> findByAgeLessThan(int age);
//根据年龄区间进行范围查询
List<User> findByAgeBetween(int startAge , int endAge);
//@Query 查询 --->自己定义查询语句-----半自动
//rowMapperClass 或 rowMapperRef 是用来做自定义映射,查询出来的User对象的数据,映射到Student对象的属性上面去都可以,因为是自定义的。
//根据密码模糊查询
@Query("select * from user_inf where password like :passwordPattern")
List<User> findBySql(String passwordPattern);
//根据年龄范围修改名字
@Query("update user_inf set name = :name where age between :startAge and :endAge")
@Modifying //更改数据库数据需要用到这个注解
int updateNameByAge(String name , int startAge , int endAge);
}
CustomUserDao
package cn.ljh.app.dao;
import cn.ljh.app.domain.User;
import java.util.List;
/**
* author JH
*/
//自己定义的接口,用来实现全手动的查询
public interface CustomUserDao
{
//通过名字进行模糊查询,使用 dataSource
List<User> customQueryUsingConnection(String namePattern);
//通过名字进行模糊查询,使用 jdbcTemplate
List<User> customQueryUsingTemplate(String namePattern);
}
CustomUserDaoImpl
package cn.ljh.app.dao.impl;
import cn.ljh.app.dao.CustomUserDao;
import cn.ljh.app.domain.User;
import lombok.SneakyThrows;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
import java.sql.Array;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
/**
* author JH
*/
public class CustomUserDaoImpl implements CustomUserDao
{
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
//通过有参构造器进行依赖注入
public CustomUserDaoImpl(DataSource dataSource, JdbcTemplate jdbcTemplate)
{
this.dataSource = dataSource;
this.jdbcTemplate = jdbcTemplate;
}
@SneakyThrows
@Override
public List<User> customQueryUsingConnection(String namePattern)
{
//创建数据库连接
Connection connection = this.dataSource.getConnection();
//创建 PreparedStatement 预处理语句
PreparedStatement pstmt = connection.prepareStatement("select * from user_inf where name like ?");
pstmt.setString(1, namePattern);
//执行查询
ResultSet rs = pstmt.executeQuery();
List<User> userList = new ArrayList<>();
//遍历结果集,封装对象
while (rs.next())
{
userList.add(new User(
rs.getInt("user_id"),
rs.getString("name"),
rs.getString("password"),
rs.getInt("age")
));
}
return userList;
}
@Override
public List<User> customQueryUsingTemplate(String namePattern)
{
//直接执行查询
List<User> userList = this.jdbcTemplate.query(
"select user_id as id,name ,password,age from user_inf where name like ?",
//把查询的结果封装起来
new BeanPropertyRowMapper<>(User.class),
namePattern
);
return userList;
}
}
UserDaoTest
package cn.ljh.app;
import cn.ljh.app.dao.UserDao;
import cn.ljh.app.domain.User;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.jdbc.repository.query.Modifying;
import org.springframework.data.jdbc.repository.query.Query;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
/**
* author JH
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class UserDaoTest
{
@Autowired
private UserDao userDao;
// 继承 CrudRepository 接口后,就已经有通用的 CRUD 操作,无需自己来书写这些方法==============
//添加user对象
@ParameterizedTest
@CsvSource({"aa,xxx,2", "bb,xxx,3"})
public void testSave(String name, String password, int age)
{
//没有id,save就是添加
User user = userDao.save(new User(null, name, password, age));
System.err.println(user);
}
//根据id修改对象
@ParameterizedTest
@CsvSource({"13,aaa,xxxx,22"})
public void testUpdate(Integer id, String name, String password, int age)
{
//有id,save就是修改
User user = userDao.save(new User(id, name, password, age));
System.err.println(user);
}
//根据id删除用户对象
@ParameterizedTest
@ValueSource(ints = {14})
public void testDelete(Integer id)
{
userDao.deleteById(id);
}
//根据id查询对象
@ParameterizedTest
@ValueSource(ints = {1})
public void testFindById(Integer id)
{
Optional<User> user = userDao.findById(id);
}
//方法名关键字查询---------全自动=====================================================
//根据名字模糊查询
@ParameterizedTest
@ValueSource(strings = {"孙%", "%精"})
public void testFindByNameLike(String namePattern)
{
List<User> users = userDao.findByNameLike(namePattern);
users.forEach(System.err::println);
}
//根据年龄大小进行范围查询
@ParameterizedTest
@ValueSource(ints = {500, 10})
public void testFindByAgeGreaterThan(int startAge)
{
List<User> users = userDao.findByAgeGreaterThan(startAge);
users.forEach(System.err::println);
}
//根据年龄大小进行范围查询
@ParameterizedTest
@ValueSource(ints = {20})
public void testFindByAgeLessThan(int age)
{
List<User> users = userDao.findByAgeLessThan(age);
users.forEach(System.err::println);
}
//根据年龄区间进行范围查询
@ParameterizedTest
@CsvSource({"15,20", "500,1000"})
public void testFindByAgeBetween(int startAge, int endAge)
{
List<User> users = userDao.findByAgeBetween(startAge, endAge);
users.forEach(System.err::println);
}
//@Query 查询 --->自己定义查询语句-----半自动=====================================================================
//rowMapperClass 或 rowMapperRef 是用来做自定义映射,查询出来的User对象的数据,映射到Student对象的属性上面去都可以,因为是自定义的。
//根据密码模糊查询
@ParameterizedTest
@ValueSource(strings = {"niu%", "%3"})
public void testFindBySql(String passwordPattern)
{
List<User> users = userDao.findBySql(passwordPattern);
users.forEach(System.err::println);
}
//根据年龄范围修改名字
@ParameterizedTest
@CsvSource({"牛魔王aa,800,1000"})
@Transactional
@Rollback(false)
public void testUpdateNameByAge(String name, int startAge, int endAge)
{
int i = userDao.updateNameByAge(name, startAge, endAge);
}
//自己定义的接口,用来实现全手动的查询===================================================================================
//通过名字进行模糊查询,使用 dataSource
@ParameterizedTest
@ValueSource(strings = {"孙%"})
public void testCustomQueryUsingConnection(String namePattern)
{
List<User> users = userDao.customQueryUsingConnection(namePattern);
users.forEach(System.err::println);
}
//通过名字进行模糊查询,使用 jdbcTemplate
@ParameterizedTest
@ValueSource(strings = {"孙%"})
public void testCustomQueryUsingTemplate(String namePattern)
{
List<User> users = userDao.customQueryUsingTemplate(namePattern);
users.forEach(System.err::println);
}
}
application.properties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
</parent>
<groupId>cn.ljh</groupId>
<artifactId>Spring_Data_JDBC</artifactId>
<version>1.0.0</version>
<name>Spring_Data_JDBC</name>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<!-- 导入 spring data jdbc -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>