Spring Boot 技术探索

Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run".

23、Spring Boot中JDBC的使用

平台环境:

名称

版本号

Mac OS X

10.14.6

JDK

1.8.0_201

Apache Maven

3.6.0

IntelliJ IDEA

2019.1 (Ultimate Edition)

Spring Boot

2.1.8.RELEASE

MySQL

8.0.13 库名test1,test2

 

  什么是JDBC?

  JDBC是Java DataBase Connectivity的缩写,字面意思是Java、数据库的连通、联结。有此可以看出,这是一套Java语言中通过执行SQL语句操作数据库的API(由一组用Java语言编写的类和接口组成)。

  它的优点是为多种关系数据库提供了统一的访问,并且也正是由于JDBC提供了统一的数据库访问机制,从而才在此基础之上发展出了更高级的数据库操作工具,其中最为流行的有Hibernate、MyBatis、Spring Data JPA、Spring JDBC。

  但是,JDBC的使用比较繁琐。一个完整的数据库操作需要7步,然而开发人员最需要关心的只有第4步。

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、关闭资源
}

 

  接下来就轮到今天的主角出场了,Spring JDBC。它是Spring Boot对JDBC的封装,Spring Boot在简化使用难度与维持原有的高性能之间做到了新的平衡。

 

准备好MySQL数据库,并建好库名叫test1,接下来是建表语句:

CREATE TABLE
    Employee
    (
        ID bigint NOT NULL AUTO_INCREMENT,
        user_name VARCHAR(100),
        pass_word VARCHAR(100),
        login_time DATETIME,
        remark VARCHAR(500),
        PRIMARY KEY (ID)
    )
    ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

pom.xml中添加依赖项

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

 

application.properties配置文件中添加数据库连接配置信息

spring.datasource.url=jdbc:mysql://localhost:3306/test1?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=true

spring.datasource.username=root

spring.datasource.password=12345678

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

 

新建model类Employee

package com.example.model;


import java.util.Date;


public class Employee
{
    private Long id;
    private String userName;
    private String password;
    private Date loginTime;
    private String remark;


    public Long getId()
    {
        return id;
    }


    public void setId(Long id)
    {
        this.id = id;
    }


    public String getUserName()
    {
        return userName;
    }


    public void setUserName(String userName)
    {
        this.userName = userName;
    }


    public String getPassword()
    {
        return password;
    }


    public void setPassword(String password)
    {
        this.password = password;
    }


    public Date getLoginTime()
    {
        return loginTime;
    }


    public void setLoginTime(Date loginTime)
    {
        this.loginTime = loginTime;
    }


    public String getRemark()
    {
        return remark;
    }


    public void setRemark(String remark)
    {
        this.remark = remark;
    }


    @Override
    public String toString()
    {
        return "User{" + "id=" + id + ", userName='" + userName + "', password='" + password + "', loginTime=" + loginTime + "', remark='" + remark + "'}";
    }
}

实体类的数据类型要和数据库字段一一对应,或者使用驼峰命名法。如果就是需要完全不一致,例如这里叫password,数据库里叫pass_word,则需要自定义映射关系,在后边代码中会写到。

 

新建Repository接口类EmployeeRepository

package com.example.demo;


import com.example.model.Employee;


import java.util.List;


public interface EmployeeRepository
{
    int save(Employee user);


    int update(Employee user);


    int delete(long id);


    List<Employee> findALL();


    Employee findById(long id);
}

定义增删改查方法。

 

新建实现EmployeeRepository接口类EmployeeRepositoryImpl

package com.example.demo.impl;


import com.example.demo.EmployeeRepository;
import com.example.model.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;


import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;


@Repository
public class EmployeeRepositoryImpl implements EmployeeRepository
{
    @Autowired
    private JdbcTemplate jdbcTemplate;


    @Override
    public int save(Employee employee)
    {
        return jdbcTemplate.update("INSERT INTO Employee (user_name, pass_word, login_time, remark) VALUES (?, ?, ?, ?);", employee.getUserName(), employee.getPassword(), employee.getLoginTime(), employee.getRemark());
    }


    @Override
    public int update(Employee employee)
    {
        return jdbcTemplate.update("UPDATE Employee SET user_name = ?, pass_word = ?, login_time = ?, remark = ? WHERE id=?", employee.getUserName(), employee.getPassword(), employee.getLoginTime(), employee.getRemark(), employee.getId());
    }


    @Override
    public int delete(long id)
    {
        return jdbcTemplate.update("DELETE FROM Employee where id = ? ", id);


    }


    @Override
    public Employee findById(long id)
    {
        // new BeanPropertyRowMapper<>是默认的数据库查询结果集与Model类之间的映射类,如果数据库字段名与Model属性名不一致(或者不符合驼峰命名法)则无法正常映射。
        // return jdbcTemplate.queryForObject("SELECT * FROM Employee WHERE id=?", new Object[]{id}, new BeanPropertyRowMapper<>(Employee.class));
        return jdbcTemplate.queryForObject("SELECT * FROM Employee WHERE id=?", new Object[]{id}, new EmployeeRowMapper());
    }


    @Override
    public List<Employee> findALL()
    {
        return jdbcTemplate.query("SELECT * FROM Employee", new EmployeeRowMapper());
    }


    // 自定义数据库查询结果集与Model类之间的映射关系类
    class EmployeeRowMapper implements RowMapper<Employee>
    {
        @Override
        public Employee mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            Employee user = new Employee();
            user.setId(rs.getLong("id"));
            user.setUserName(rs.getString("user_name"));
            user.setPassword(rs.getString("pass_word"));
            user.setLoginTime(rs.getTimestamp("login_time"));
            user.setRemark(rs.getString("remark"));
            return user;
        }
    }
}
  • @Repository注解表示此类是数据库操作类
  • JdbcTemplate是Spring封装好的JDBC操作类

 

新建测试类EmployeeRepositoryTests

package com.example.demo;
import com.example.model.Employee;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Date;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class EmployeeRepositoryTests
{
    @Autowired
    private EmployeeRepository employeeRepository;
    @Test
    public void testSave()
    {
        Employee user = new Employee();
        user.setUserName("A01");
        user.setPassword("123456");
        user.setLoginTime(new Date());
        user.setRemark("备注01");
        employeeRepository.save(user);
        Employee user2 = new Employee();
        user2.setUserName("A02");
        user2.setPassword("12345678");
        user2.setLoginTime(new Date());
        user2.setRemark("备注02");
        employeeRepository.save(user2);
    }
    @Test
    public void testFindALL()
    {
        List<Employee> users = employeeRepository.findALL();
        for (Employee user : users)
        {
            System.out.println("user == " + user.toString());
        }
    }
    @Test
    public void testUpdate()
    {
        Employee user = new Employee();
        user.setId(28L); // 这个ID要改成和数据库里的数据ID一致
        user.setUserName("A01");
        user.setPassword("123456");
        user.setLoginTime(new Date());
        user.setRemark("备注01_new");
        employeeRepository.update(user);
    }
    @Test
    public void testFindById()
    {
        Employee user = employeeRepository.findById(28L); // 这个ID要改成和数据库里的数据ID一致
        System.out.println("user == " + user.toString());
    }
    @Test
    public void testDelete()
    {
        employeeRepository.delete(28L); // 这个ID要改成和数据库里的数据ID一致
    }
}

 

测试之前:

如果手头没有好的MySQL客户端工具,推荐使用IDEA的Database工具。

找到View——Tool Windows——Database,打开工具窗口。

 

第一次打开需要新增数据源的连接。工具窗口内,右键选择New——Data Source——MySQL。

在打开的窗口中填写好数据库连接信息,接着点击Test Connection,如果显示Successful则表示连接成功。

点击OK保存并关闭窗口。

 

在新建好的数据源上右键选择SQL Scripts——Source Editor打开SQL语句编写窗口。

输入SQL语句,点击绿色三角执行。

 

可以看到IDEA自动为我们打开了Database Console窗口,并显示查询结果。

接下来的操作我们将用到此窗口来观察结果。

 

开始测试:

执行测试类第一个测试testSave(),成功之后找到我们之前准备好的Database Console窗口,右键选择Reload Page。观察测试结果,数据已经插入数据库。

 

接下来执行testFindALL()

在Run窗口中观察到测试结果。

user == User{id=28, userName='A01', password='123456', loginTime=2019-09-26 11:38:48.0', remark='备注01'}
user == User{id=29, userName='A02', password='12345678', loginTime=2019-09-26 11:38:48.0', remark='备注02'}

 

接下来执行testUpdate()

 

接下来执行testFindById()

在Run窗口中观察到测试结果。

user == User{id=28, userName='A01', password='123456', loginTime=2019-09-26 11:43:07.0', remark='备注01_new'}

 

接下来执行testDelete()

 


 

多数据源的配置与使用

如果一个项目中需要连接多个数据源,则需要做如下修改。

首先,需要准备好数据库test1和test2以及Employee表。

 

修改配置文件application.properties

# 多数据源1
spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/test1?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.primary.username=root
spring.datasource.primary.password=12345678
spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver


# 多数据源2
spring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/test2?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.secondary.username=root
spring.datasource.secondary.password=12345678
spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver

 

这里注意,默认的数据源配置是

spring.datasource.url

而这里手工指定多个数据源,则需要配置改为

spring.datasource.secondary.jdbc-url

因为默认连接池HikariCP读取的是 jdbc-url。

 

新建数据源配置类DataSourceConfig

package com.example.demo.config;


import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;


import javax.sql.DataSource;


@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);
    }
}

 

新建数据源切换枚举类DataSourceBox

package com.example.demo.config;


public enum DataSourceBox
{
    MySQL_TEST1,MySQL_TEST2;
}

 

修改EmployeeRepository,增加数据源切换方法。

package com.example.demo;


import com.example.demo.config.DataSourceBox;
import com.example.model.Employee;


import java.util.List;


public interface EmployeeRepository
{
    void changeJdbcTemplate(DataSourceBox dataSourceBox);


    int save(Employee user);


    int update(Employee user);


    int delete(long id);


    List<Employee> findALL();


    Employee findById(long id);
}

 

修改EmployeeRepositoryImpl类,实现数据源切换方法。

package com.example.demo.impl;


import com.example.demo.EmployeeRepository;
import com.example.demo.config.DataSourceBox;
import com.example.model.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;


import javax.annotation.Resource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;


@Repository
public class EmployeeRepositoryImpl implements EmployeeRepository
{
    @Autowired
    private JdbcTemplate primaryJdbcTemplate;


    @Autowired
    private JdbcTemplate secondaryJdbcTemplate;


    // 指定当前数据源默认为primaryJdbcTemplate
    @Resource(name = "primaryJdbcTemplate")
    private JdbcTemplate jdbcTemplateNow;


    @Override
    public void changeJdbcTemplate(DataSourceBox dataSourceBox)
    {
        switch (dataSourceBox)
        {
            case MySQL_TEST1:
            {
                this.jdbcTemplateNow = primaryJdbcTemplate;
                break;
            }
            case MySQL_TEST2:
            {
                this.jdbcTemplateNow = secondaryJdbcTemplate;
                break;
            }
        }
    }


    @Override
    public int save(Employee employee)
    {
        return jdbcTemplateNow.update("INSERT INTO Employee (user_name, pass_word, login_time, remark) VALUES (?, ?, ?, ?);", employee.getUserName(), employee.getPassword(), employee.getLoginTime(), employee.getRemark());
    }


    @Override
    public int update(Employee employee)
    {
        return jdbcTemplateNow.update("UPDATE Employee SET user_name = ?, pass_word = ?, login_time = ?, remark = ? WHERE id=?", employee.getUserName(), employee.getPassword(), employee.getLoginTime(), employee.getRemark(), employee.getId());
    }


    @Override
    public int delete(long id)
    {
        return jdbcTemplateNow.update("DELETE FROM Employee where id = ? ", id);


    }


    @Override
    public Employee findById(long id)
    {
        // new BeanPropertyRowMapper<>是默认的数据库查询结果集与Model类之间的映射类,如果数据库字段名与Model属性名不一致(或者不符合驼峰命名法)则无法正常映射。
        // return jdbcTemplate.queryForObject("SELECT * FROM Employee WHERE id=?", new Object[]{id}, new BeanPropertyRowMapper<>(Employee.class));
        return jdbcTemplateNow.queryForObject("SELECT * FROM Employee WHERE id=?", new Object[]{id}, new EmployeeRepositoryImpl.EmployeeRowMapper());
    }


    @Override
    public List<Employee> findALL()
    {
        return jdbcTemplateNow.query("SELECT * FROM Employee", new EmployeeRepositoryImpl.EmployeeRowMapper());
    }


    // 自定义数据库查询结果集与Model类之间的映射关系类
    class EmployeeRowMapper implements RowMapper<Employee>
    {
        @Override
        public Employee mapRow(ResultSet rs, int rowNum) throws SQLException
        {
            Employee user = new Employee();
            user.setId(rs.getLong("id"));
            user.setUserName(rs.getString("user_name"));
            user.setPassword(rs.getString("pass_word"));
            user.setLoginTime(rs.getTimestamp("login_time"));
            user.setRemark(rs.getString("remark"));
            return user;
        }
    }
}

 

修改测试方法

@Test
public void testSave()
{
    Employee user = new Employee();
    user.setUserName("A01");
    user.setPassword("123456");
    user.setLoginTime(new Date());
    user.setRemark("备注01");
    employeeRepository.changeJdbcTemplate(MySQL_TEST1);
    employeeRepository.save(user);


    Employee user2 = new Employee();
    user2.setUserName("A02");
    user2.setPassword("12345678");
    user2.setLoginTime(new Date());
    user2.setRemark("备注02");
    employeeRepository.changeJdbcTemplate(MySQL_TEST2);
    employeeRepository.save(user2);
}

 

 

Bootstrap Thumbnail Second
MySQL

MySQL is the world's most popular open source database.

GO

Bootstrap Thumbnail Third
算法基础

本书介绍了什么是计算机算法,如何描述它们,以及如何来评估它们。

GO