整合JdbcTemplate

持久层是JavaEE中访问数据库的核心操作,SpringBoot中对常见的持久层框架都提供了自动化配置,例如JdbcTemplate、JPA 等,MyBatis 的自动化配置则是MyBatis官方提供的。接下来分别向读者介绍Spring Boot整合这持久层技术中的整合JdbcTemplate。

JdbcTemplate是Spring提供的一套JDBC模板框架,利用AOP技术来解决直接使用JDBC时大量重复代码的问题。JdbcTemplate虽然没有MyBatis那么灵活,但是比直接使用JDBC要方便很多。Spring Boot中对JdbcTemplate的使用提供了自动化配置JdbcTemplateAutoConfiguration, 源码如下:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, JdbcTemplate.class })
@ConditionalOnSingleCandidate(DataSource.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
@EnableConfigurationProperties(JdbcProperties.class)
@Import({ DatabaseInitializationDependencyConfigurer.class, JdbcTemplateConfiguration.class,
		NamedParameterJdbcTemplateConfiguration.class })
public class JdbcTemplateAutoConfiguration {

}

1. 创建数据库和表

在数据库中创建表,代码如下:

DROP TABLE IF EXISTS `book`;
CREATE TABLE `book`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `author` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

INSERT INTO `book` VALUES (1, '三国演义', '罗贯中');
INSERT INTO `book` VALUES (2, '水浒传', '施耐庵');

创建数据库,在库中创建book表,同时添加两条测试语句。

2. 创建项目

创建Spring Boot项目,添加如下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.8</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

spring-boot-starter-jdbc中提供了spring-jdbc, 另外还加入了数据库驱动依赖和数据库连接池依赖。

3. 数据库配置

在application.yml中配置数据库基本连接信息:

spring:
  datasource:
    username: admin
    password: 123456
    #?serverTimezone=UTC解决时区的报错
    url: jdbc:mysql://120.55.61.170:3306/suohechuan?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    
    #Spring Boot 默认是不注入这些属性值的,需要自己绑定
    #druid 数据源专有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

4. 创建实体类

创建Book实体类,代码如下:

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Book {
    private Integer id;
    private String name;
    private String author;
}

5. 创建数据库访问层

创建BookDao,代码如下:

@Repository
public class BookDao {
    @Autowired
    JdbcTemplate jdbcTemplate;

    public int addBook(Book book) {
        return jdbcTemplate.update("INSERT INTO book (name , author) VALUES (?, ?)",
                book.getName(), book.getAuthor());
    }

    public int updateBook(Book book) {
        return jdbcTemplate.update("UPDATE book SET name=?, author=? WHERE id=?",
                book.getName(), book.getAuthor(), book.getId());
    }

    public int deleteBookById(Integer id) {
        return jdbcTemplate.update("DELETE FROM book WHERE id=?", id);
    }

    public Book getBookById(Integer id) {
        return jdbcTemplate.queryForObject("select * from book where id=?",
                new BeanPropertyRowMapper<>(Book.class), id);
    }

    public List<Book> getAllBooks() {
        return jdbcTemplate.query("select * from book",
                new BeanPropertyRowMapper<>(Book.class));
    }
}

代码解释:

  • 创建 BookDao,注入JdbcTemplate。由于已经添加了spring-jdbc 相关的依赖,JdbcTemplate会被自动注册到Spring容器中,因此这里可以直接注入JdbcTemplate使用。
  • 在JdbcTemplate中,增删改三种类型的操作主要使用update和batchUpdate方法来完成。query和queryForObject方法主要用来完成查询功能。另外,还有execute方法可以用来执行任意的SQL、call方法用来调用存储过程等。
  • 在执行查询操作时, 需要有一个RowMapper 将查询出来的列和实体类中的属性一一对应起来。如果列名和属性名都是相同的,那么可以直接使用BeanPropertyRowMapper; 如果列名和属性名不同,就需要开发者自己实现RowMapper接口,将列和实体类属性一一对应起来。

6. 创建Service和Controller

创建BookService和BookController,代码如下:

@Service
public class BookService {
    @Autowired
    BookDao bookDao;

    public int addBook(Book book) {
        return bookDao.addBook(book);
    }

    public int updateBook(Book book) {
        return bookDao.updateBook(book);
    }

    public int deleteBookById(Integer id) {
        return bookDao.deleteBookById(id);
    }

    public Book getBookById(Integer id) {
        return bookDao.getBookById(id);
    }

    public List<Book> getAllBooks() {
        return bookDao.getAllBooks();
    }
}
@RestController
public class BookController {
    @Autowired
    BookService bookService;

    @GetMapping("/bookOps")
    public void bookOps() {
        Book b1 = new Book();
        b1.setName("西厢记");
        b1.setAuthor("王实甫");
        int i = bookService.addBook(b1);
        System.out.println("addBook>>>" + i);
        Book b2 = new Book();
        b2.setId(1);
        b2.setName("朝花夕拾");
        b2.setAuthor("鲁迅");
        int updateBook = bookService.updateBook(b2);
        System.out.println("updateBook>>" + updateBook);
        Book b3 = bookService.getBookById(1);
        System.out.println("getBookById>>>" + b3);
        int delete = bookService.deleteBookById(2);
        System.out.println("deleteBookById>>>" + delete);
        List<Book> allBooks = bookService.getAllBooks();
        System.out.println("getAllBooks>>>" + allBooks);
    }
}

最后,在浏览器中访问http://localhost:8080/bookOps地址,控制台打印日志如图所示。

addBook>>>1
updateBook>>1
getBookById>>>Book(id=1, name=朝花夕拾, author=鲁迅)
deleteBookById>>>1
getAllBooks>>>[Book(id=1, name=朝花夕拾, author=鲁迅), Book(id=3, name=西厢记, author=王实甫), Book(id=4, name=西厢记, author=王实甫)]

数据库中的数据如图所示。

在这里插入图片描述

JdbcTemplate多数据源

所谓多数据源,就是一个Java EE项目中采用了不同数据库实例中的多个库,或者同一个数据库实例中多个不同的库。一般来说,采用MyCat等分布式数据库中间件是比较好的解决方案,这样可以把数据库读写分离、分库分表、备份等操作交给中间件去做,Java 代码只需要专注于业务即可。不过,这并不意味着无法使用Java代码解决类似的问题,在Spring Framework 中就可以配
置多数据源,Spring Boot继承其衣钵,只不过配置方式有所变化。

JdbcTemplate多数据源的配置是比较简单的,因为一个JdbcTemplate对应一个DataSource,开发者只需要手动提供多个DataSource,再手动配置JdbcTemplate即可。具体步骤如下。

1. 创建数据库

创建两个数据库。两个库中都创建book表,再各预设1条数据,创建脚本如下:

第一个数据库中

DROP TABLE IF EXISTS `book`;
CREATE TABLE `book`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `author` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

INSERT INTO `book` VALUES (1, '三国演义', '罗贯中');

第二个数据库中

DROP TABLE IF EXISTS `book`;
CREATE TABLE `book`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `author` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

INSERT INTO `book` VALUES (1, '水浒传', '施耐庵');

执行完数据库脚本后,数据库中的数据如图所示。

在这里插入图片描述

在这里插入图片描述

2. 创建项目

创建Spring Boot Web项目,添加如下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.8</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

注意这里添加的数据库连接池依赖是druid-spring-boot-starter。 druid-spring-boot-starter 可以帮助开发者在SpringBoot项目中轻松集成Druid数据库连接池和监控。

3. 配置数据库连接

在application.properties中配置数据库连接信息,代码如下:

spring:
  datasource:
  	#数据源1
    one:
      username: admin
      password: 123456
      #?serverTimezone=UTC解决时区的报错
      url: jdbc:mysql://120.55.61.170:3306/suohechuan?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
      driver-class-name: com.mysql.cj.jdbc.Driver
      type: com.alibaba.druid.pool.DruidDataSource
    #数据源2
    two:
      username: fristweb
      password: dTNFJW4B5MrwT4KS
      #?serverTimezone=UTC解决时区的报错
      url: jdbc:mysql://120.55.61.170:3306/fristweb?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
      driver-class-name: com.mysql.cj.jdbc.Driver
      type: com.alibaba.druid.pool.DruidDataSource
    
    #Spring Boot 默认是不注入这些属性值的,需要自己绑定
    #druid 数据源专有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

配置两个数据源,区别主要是数据库不同,其他都是一样的。

4. 配置数据源

创建DataSourceConfig配置数据源,根据application.properties中的配置生成两个数据源:

@Configuration
public class DataSourceConfig {
    @Bean
    @ConfigurationProperties("spring.datasource.one")
    DataSource dsOne(){
        return DruidDataSourceBuilder.create().build();
    }
    @Bean
    @ConfigurationProperties("spring.datasource.two")
    DataSource dsTwo(){
        return DruidDataSourceBuilder.create().build();
    }
}

代码解释:

  • DataSourceConfig中提供了两个数据源: dsOne 和dsTwo,默认方法名即实例名。
  • @ConfigurationProperties 注解表示使用不同前缀的配置文件来创建不同的DataSource实例。

5. 配置JdbcTemplate

我们已经了解到只要引入了spring-jdbc 依赖,那么开发者没有提供JdbcTemplate实例时,Spring Boot 默认会提供一个JdbcTemplate实例。现在配置多数据源时,由开发者自己提供JdbcTemplate实例,代码如下:

@Configuration
public class JdbcTemplateConfig {
    @Bean
    JdbcTemplate jdbcTemplateOne(@Qualifier("dsOne")DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }
    @Bean
    JdbcTemplate jdbcTemplateTwo(@Qualifier("dsTwo")DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }
}

代码解释:

  • JdbcTemplateConfig 中提供两个JdbcTemplate 实例。每个JdbcTemplate 实例都需要提供DataSource,由于Spring容器中有两个DataSource实例,因此需要通过方法名查找。@Qualifer注解表示查找不同名称的DataSource实例注入进来。

6. 创建BookController

创建实体类Book和BookController进行测试:

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Book {
    private Integer id;
    private String name;
    private String author;
}
@RestController
public class BookController {
    @Resource(name = "jdbcTemplateOne")
    JdbcTemplate jdbcTemplateOne;
    @Autowired
    @Qualifier("jdbcTemplateTwo")
    JdbcTemplate jdbcTemplateTwo;

    @GetMapping("/test1")
    public void test1() {
        List<Book> books1 = jdbcTemplateOne.query("select * from book", new BeanPropertyRowMapper<>(Book.class));
        List<Book> books2 = jdbcTemplateTwo.query("select * from book", new BeanPropertyRowMapper<>(Book.class));
        System.out.println("books1:" + books1);
        System.out.println("books2: " + books2);
    }
}

简单起见,这里没有添加Service 层,而是直接将JdbcTemplate注入到了Controller 中。在Controller中注入两个不同的JdbcTemplate有两种方式:一种 是使用@Resource注解,并指明name属性,即按name进行装配,此时会根据实例名查找相应的实例注入;另一-种是使用@Autowired注解结合@Qualifer注解,效果等同于使用@Resource注解。

7. 测试

最后,在浏览器地址栏输入“http://localhost:8080/test1",控制台打印日志。JdbcTemplate多数据源配置成功。

books1:[Book(id=1, name=三国演义, author=罗贯中)]
books2: [Book(id=2, name=水浒传, author=施耐庵)]

books2: " + books2);
}
}


简单起见,这里没有添加Service 层,而是直接将JdbcTemplate注入到了Controller 中。在Controller中注入两个不同的JdbcTemplate有两种方式:一种 是使用@Resource注解,并指明name属性,即按name进行装配,此时会根据实例名查找相应的实例注入;另一-种是使用@Autowired注解结合@Qualifer注解,效果等同于使用@Resource注解。

## 7. 测试
最后,在浏览器地址栏输入“http://localhost:8080/test1",控制台打印日志。JdbcTemplate多数据源配置成功。

```shell
books1:[Book(id=1, name=三国演义, author=罗贯中)]
books2: [Book(id=2, name=水浒传, author=施耐庵)]
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐