第 3-1 课:Spring Boot 使⽤ JDBC 操作数据库

 
《精通 Spring Boot 42 讲》共分五⼤部分,第三部分主要讲解 Spring Boot 和数据库开发,共 8 课。 Spring Boot ⽀持了主流的 ORM 框架: MyBatis Hibernate Spring JDBC ,⼏种 ORM 在不同的场 景下各有优势,在 Spring Boot 体系内都有对应的 Starter 包以⽅便集成。⾸先将讲解 Spring JDBC 的 使⽤,然后介绍 MyBatis JPA 的各种使⽤场景以及多数据源的使⽤,接着演示如何集成流⾏的数据 库连接池 Druid ,最后结合本课程第⼆部分内容综合实践 JPA Thymeleaf 的使⽤。
 
JDBC Java Data Base Connectivity Java 数据库连接)是⼀种⽤于执⾏ SQL 语句的 Java API ,可以为多 种关系数据库提供统⼀访问,它由⼀组⽤ Java 语⾔编写的类和接⼝组成。 JDBC 提供了⼀种基准,据此可以 构建更⾼级的⼯具和接⼝,使数据库开发⼈员能够编写数据库应⽤程序。
 
说⽩了 JDBC 就是⼀套 Java 访问数据库的 API 规范,利⽤这套规范屏蔽了各种数据库 API 调⽤的差异性。当 Java 程序需要访问数据库时,直接调⽤ JDBC API 相关代码进⾏操作,JDBC 调⽤各类数据库的驱动包进 ⾏交互,最后数据库驱动包和对应的数据库通讯,完成 Java 程序操作数据库。
 
直接在 Java 程序中使⽤ JDBC ⽐较复杂,需要七步才能完成数据库的操作:
  • 加载数据库驱动
  • 建⽴数据库连接
  • 创建数据库操作对象
  • 定义操作的 SQL 语句
  • 执⾏数据库操作
  • 获取并操作结果集
  • 关闭对象,回收资源

关键代码如下:

try {
 // 1、加载数据库驱动
 Class.forName(driver);
 // 2、获取数据库连接
 conn = DriverManager.getConnection(url, username, password);
 // 3、获取数据库操作对象
 stmt = conn.createStatement();
 // 4、定义操作的 SQL 语句
 String sql = "select * from user where id = 6";
 // 5、执⾏数据库操作
 rs = stmt.executeQuery(sql);
 // 6、获取并操作结果集
 while (rs.next()) {
 // 解析结果集
 }
} catch (Exception e) {
 // ⽇志信息
} finally {
 // 7、关闭资源
}
通过上⾯的示例可以看出直接使⽤ JDBC 来操作数据库⽐较复杂,因此后期在 JDBC 的基础上⼜发展出了很 多著名的 ORM 框架,其中最为流⾏的是 Hibernate MyBatis Spring JDBC 。这三个流⾏的 ORM 框架在 后续的课程中都会讲到,这⾥主要了解⼀下 Spring JDBC Spring Boot 中的使⽤。
 
Spring Boot 针对 JDBC 的使⽤提供了对应的 Starter 包: spring-boot-starter-jdbc ,它其实就是在 Spring JDBC 上做了进⼀步的封装,⽅便在 Spring Boot ⽣态中更好的使⽤ JDBC ,下⾯进⾏示例演示。
 

快速上⼿

Spring Boot 集成 JDBC 很简单,需要引⼊依赖并做基础配置即可,在开发项⽬之前需要先创建表,作为项 ⽬演示使⽤。设计⼀个 User ⽤户表,有 id name password age 等字段,对应的 SQL 脚本如下:
 
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
 `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
 `name` varchar(32) DEFAULT NULL COMMENT '⽤户名',
 `password` varchar(32) DEFAULT NULL COMMENT '密码',
 `age` int DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

添加配置

 
添加依赖包:
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
 <groupId>mysql</groupId>
 <artifactId>mysql-connector-java</artifactId>
</dependency>
演示项⽬中使⽤ MySQL 作为数据库,因此项⽬中需要引⼊ MySQL 驱动包,同时引⼊ spring-boot-starterjdbc。打开 pom.xml ⽂件,按下快捷键: Ctrl + Alt + SHIFT + U ,或者单击右键,选择 Diagrams | Show Dependencies 选项,查看项⽬依赖类图。
 
弹出 类图 对话框后,滚动⿏标放⼤查看,发现 spring-boot-starter-jdbc 直接依赖于 HikariCP springjdbc。
  • HikariCP Spring Boot 2.0 默认使⽤的数据库连接池,也是传说中最快的数据库连接池。
  • spring-jdbc Spring 封装对 JDBC 操作的⼯具包。

数据源配置:

spring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnico
de=true&characterEncoding=utf-8&useSSL=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
值得注意的是,在 Spring Boot 2.1.0 中, com.mysql.jdbc.Driver 已经过期,推荐使⽤
com.mysql.cj.jdbc.Driver
 

实体类

 
创建表对应的实体类:
 
public class User {
 private Long id;
 private String name;
 private String password;
 private int age;
 public User(String name, String password, int age) {
 this.name = name;
 this.password = password;
 this.age = age;
 }
 // 省略 getter setter
}
实体类的数据类型要和数据库字段⼀⼀对应:
  • Long 对应 bigint
  • String 对应 varchar
  • int 对应 int

封装 Repository

 
 
创建 UserRepository 定义我们常⽤的增删改查接⼝:
public interface UserRepository {
 int save(User user);
 int update(User user);
 int delete(long id);
 List<User> findALL();
 User findById(long id);
}
创建 UserRepositoryImpl 类实现 UserRepository 类接⼝:
@Repository
public class UserRepositoryImpl implements UserRepository {
 @Autowired
 private JdbcTemplate jdbcTemplate;
}
类上使⽤ @Repository 注解⽤于标注数据访问组件,同时在类中注⼊ JdbcTemplate ,其是 Spring 操作 JDBC 提供的⼯具类。
接下来封装保存⽤户的⽅法:
@Override
public int save(User user) {
 return jdbcTemplate.update("INSERT INTO users(name, password, age) values(?, ?
, ?)",
 user.getName(), user.getPassword(), user.getAge());
}
通过以上代码可以看出,其实就是拼接数据库插⼊的 SQL ,作为参数传给 update ⽅法。
更新⽤户信息和上面类似:
@Override
public int update(User user) {
 return jdbcTemplate.update("UPDATE users SET name = ? , password = ? , age = ?
 WHERE id=?",
 user.getName(), user.getPassword(), user.getAge(), user.getId());
}
封装删除⽤户的⽅法:
@Override
public int delete(long id) {
 return jdbcTemplate.update("DELETE FROM users where id = ? ",id);
}
根据⽤户 id 查询⽤户:
@Override
public User findById(long id) {
 return jdbcTemplate.queryForObject("SELECT * FROM users WHERE id=?", new Objec
t[] { id }, new BeanPropertyRowMapper<User>(User.class));
}
这⾥使⽤了 new BeanPropertyRowMapper<User>(User.class) 对返回的数据进⾏封装,它可⾃动 将⼀⾏数据映射到指定类的实例中,⾸先将这个类实例化,然后通过名称匹配的⽅式,映射到属性中去。
最后封装获取⽤户列表:
@Override
public List<User> findALL() {
 return jdbcTemplate.query("SELECT * FROM users", new UserRowMapper());
 // return jdbcTemplate.query("SELECT * FROM users", new BeanPropertyRowMapper(
User.class));
}
fifindALL() 使⽤了⼀个新的⽅式来封装结果集的返回,创建⼀个内部类 UserRowMapper
class UserRowMapper implements RowMapper<User> {
 @Override
 public User mapRow(ResultSet rs, int rowNum) throws SQLException {
 User user = new User();
 user.setId(rs.getLong("id"));
 user.setName(rs.getString("name"));
 user.setPassword(rs.getString("password"));
 user.setAge(rs.getInt("age"));
 }
}
UserRowMapper 继承了 RowMapper RowMapper 可以将数据中的每⼀⾏数据封装成⽤户定义的类,实现 RowMapper 接⼝覆盖 mapRow ⽅法,在 mapRow ⽅法封装对数据的返回处理。通过上⾯代码可以看出 UserRowMapper 循环遍历了查询返回的结果集,遍历的同时按照属性进⾏赋值。这样在查询使⽤时只需要 传⼊ new UserRowMapper() 即可⾃动解析返回数据。
 

测试

 
接下⾥我们对封装好的 UserRepository 进⾏测试,测试 UserRepository 中的各个⽅法是否正确。创建 UserRepositoryTests 类,将 userRepository 注⼊到类中。
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserRepositoryTests {
 @Autowired
 private UserRepository userRepository;
}
测试插⼊数据,直接调⽤ userRepository 对应的 save ⽅法。
@Test
public void testSave() {
 User user =new User("neo","123456",30);
 userRepository.save(user);
}
执⾏成功后会在数据库中发现⼀条 name neo 的值,证明插⼊成功。

按照同样的⽅法测试更新:

@Test
public void testUpdate() {
 User user =new User("neo","123456",18);
 user.setId(1L);
 userRepository.update(user);
}

 测试删除:

@Test
public void testDetele() {
 userRepository.delete(1L);
}
测试查询:
 
@Test
public void testQueryOne() {
 User user=userRepository.findById(1L);
 System.out.println("user == "+user.toString());
}
测试查询⽤户列表:
@Test
public void testQueryAll() {
 List<User> users=userRepository.findALL();
 for (User user:users){
 System.out.println("user == "+user.toString());
 }
}
测试执⾏正常,则表明 userRepository 中⽅法正确。
 

多数据源的使⽤

在项⽬中使⽤多个数据源是很常⻅的情况, Spring Boot 中多数据源的使⽤需要⾃⾏封装。我们在上⾯示例项
⽬的基础上进⾏改造。
 
配置⽂件
spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/test1?serverTimezon
e=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.primary.username=root
spring.datasource.primary.password=root
spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/test2?serverTimez
one=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.secondary.username=root
spring.datasource.secondary.password=root
spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
添加了两个数据源,⼀个是 test1 库,⼀个是 test2 库。
注意,这⾥使⽤的是 spring.datasource.*.jdbc-url ,因为默认连接池 HikariCP 读取的是 jdbc-url
 

初始化 JDBC

 
在项⽬启动的时候读取配置⽂件中的信息,并对 JDBC 初始化。
@Configuration
public class DataSourceConfig {
 @Primary
 @Bean(name = "primaryDataSource")
 @Qualifier("primaryDataSource")
 @ConfigurationProperties(prefix="spring.datasource.primary")
 public DataSource primaryDataSource() {
 return DataSourceBuilder.create().build();
 }
 @Bean(name = "secondaryDataSource")
 @Qualifier("secondaryDataSource")
 @ConfigurationProperties(prefix="spring.datasource.secondary")
 public DataSource secondaryDataSource() {
 return DataSourceBuilder.create().build(); 
 }
 @Bean(name="primaryJdbcTemplate")
 public JdbcTemplate primaryJdbcTemplate (
 @Qualifier("primaryDataSource") DataSource dataSource ) {
 return new JdbcTemplate(dataSource);
 }
 @Bean(name="secondaryJdbcTemplate")
 public JdbcTemplate secondaryJdbcTemplate(
 @Qualifier("secondaryDataSource") DataSource dataSource) {
 return new JdbcTemplate(dataSource);
 }
}
这段代码表示在启动的时候根据特定的前缀加载不同的数据源,根据构建好的数据源再创建不同的 JDBC
 

UserRepository 改造

 
我们对 UserRepository 中的所有⽅法进⾏改造,增加⼀个参数为 JdbcTemplate ,如果⽅法中传输了 JdbcTemplate,⽅法内就会使⽤传递的 JdbcTemplate 进⾏操作,如果传递的 JdbcTemplate 为空,使⽤默认
JdbcTemplate 连接操作。
@Repository
public class UserRepositoryImpl implements UserRepository {
 @Autowired
 private JdbcTemplate primaryJdbcTemplate;
 @Override
 public int save(User user,JdbcTemplate jdbcTemplate) {
 if(jdbcTemplate == null){
 jdbcTemplate= primaryJdbcTemplate;
 }
 return jdbcTemplate.update("INSERT INTO users(name, password, age) values(
?, ?, ?)",
 user.getName(), user.getPassword(), user.getAge());
 }
 //其他⽅法省略,详细内容可以查看源码
}

多数据源测试

 
测试类中注⼊了两个不同数据源的 JdbcTemplate ,同时注⼊ UserRepository 。测试使⽤不同的 JdbcTemplate 插⼊两条数据,查看两个数据库中是否都保存成功。
 
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserRepositoryTests {
 @Autowired
 private UserRepository userRepository;
 @Autowired
 private JdbcTemplate primaryJdbcTemplate;
 @Autowired
 private JdbcTemplate secondaryJdbcTemplate;
 @Test
 public void testSave() {
 User user =new User("smile","123456",30);
 userRepository.save(user,primaryJdbcTemplate);
 userRepository.save(user,secondaryJdbcTemplate);
 }
}
测试前请先创建 test1 test2 数据库,以及两个数据库中的⽤户表。
执⾏ testSave() 成功后,登录 test1 test 2 数据库查看 user 表,都存在⼀条 name smile 的⽤户信息, 说明多数据源插⼊数据成功,其他⽅法的测试⼤体相同。这样在项⽬中,我们想使⽤哪个数据源操作数据库 时,只需要传⼊数据源对应的 JdbcTemplate 实例即可。
 

总结

 
通过本节课程的学习,了解到使⽤原⽣的 JDBC 操作数据库⾮常繁琐,需要开发者⾃⾏封装数据库连接,执 ⾏完成后⼿动关闭对应的资源,这样不利于统⼀规范,也容易出现问题。后期 Spring 针对 JDBC 的使⽤推出 了 Spring JDBC Spring Boot 在此基础上⼜进⾏了⼀步封装,如今在 Spring Boot 项⽬中 JDBC 操作数据库
⾮常简单。
 
发布了91 篇原创文章 · 获赞 20 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/qq_34227896/article/details/103819673