0


【SpringBoot】多数据源N种实现详解

一、概述

1.1 业务场景

1,数据库的读写分类

2,SAAS服务多数据源

3,由于数据较大,要分库分表

1.2 主要的实现方式

(1)、使用分包方式,不同的数据源配置不同的MapperScan和mapper文件
(2)、使用AOP切片方式,实现动态数据源切换,AbstractRoutingDataSource
(3)、使用数据库代理中间件,如Mycat、shardingJDBC 等

1.3 不同方式之间的区别

  1. 分包方式可以集合JTA(JAVA Transactional API)实现分布式事务,但是整个流程的实现相对来说比较复杂。
  2. AOP动态配置数据源方式缺点在于无法实现全局分布式事务,所以如果只是对接第三方数据源,不涉及到需要保证分布式事务的话,是可以作为一种选择。
  3. 使用数据库代理中间件方式是现在比较流行的一种方式,很多大厂也是使用这种方式,开发者不需要关注太多与业务无关的问题,把它们都交给数据库代理中间件去处理,大量的通用的数据聚合,事务,数据源切换都由中间件来处理,中间件的性能与处理能力将直接决定应用的读写性能,比较常见的有Mycat、TDDL等。现在阿里出了100%自研的分布式数据库OceanBase,从最底层支持分布式,性能也非常强大,大家感兴趣的可以去了解下!

1.4 原理如下

一个spring和Mybatis的框架的项目中,我们在spring配置中往往是配置一个dataSource来连接数据库,然后绑定给sessionFactory,在dao层代码中再指定sessionFactory来进行数据库操作。

正如上图所示,每一块都是指定绑死的,如果是多个数据源,也只能是下图中那种方式。

可看出在Dao层代码中写死了两个SessionFactory,这样日后如果再多一个数据源,还要改代码添加一个SessionFactory,显然这并不符合开闭原则。

二、多种实现方式

2.1 【分包方式】实现简单的多数据源整合

主要是MyBatis框架分包方式实现

2.1.1 配置两个数据源配置

# 项目启动端口
server:
  port: 9090

# 项目 名称
spring:
  application:
    name: multi-datasource-instance
  datasource:
    # 主数据库
    master:
      # 注意,整合多数据源时如果使用springboot默认的数据库连接池Hikari,指定连接数据使用的是jdbc-url而不是url属性
      jdbc-url: jdbc:mysql://localhost:3306/test1?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver
    # 副数据库
    slave:
      # 注意,整合多数据源时如果使用springboot默认的数据库连接池Hikari,指定连接数据使用的是jdbc-url而不是url属性
      jdbc-url: jdbc:mysql://localhost:3306/test2?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver

2.1.2 编写主副数据库数据源配置

主数据源相关配置:主要是指定主数据源、扫描的mapper地址、事务管理器等信息。

@Configuration
// 指定主数据库扫描对应的Mapper文件,生成代理对象
@MapperScan(basePackages ="com.diary.it.multi.datasource.mapper" ,sqlSessionFactoryRef = "masterSqlSessionFactory")
public class MasterDataSourceConfig {

    // mapper.xml所在地址
    private static final String MAPPER_LOCATION = "classpath*:mapper/*.xml";

    /**
     * 主数据源,Primary注解必须增加,它表示该数据源为默认数据源
     * 项目中还可能存在其他的数据源,如获取时不指定名称,则默认获取这个数据源,如果不添加,则启动时候回报错
     */
    @Primary
    @Bean(name = "masterDataSource")
    // 读取spring.datasource.master前缀的配置文件映射成对应的配置对象
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource dataSource() {
        DataSource build = DataSourceBuilder.create().build();
        return build;
    }

    /**
     * 事务管理器,Primary注解作用同上
     */
    @Bean(name = "masterTransactionManager")
    @Primary
    public PlatformTransactionManager dataSourceTransactionManager(@Qualifier("masterDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    /**
     * session工厂,Primary注解作用同上
     */

    @Bean(name = "masterSqlSessionFactory")
    @Primary
    public SqlSessionFactory sqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource) throws Exception {
        final SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource);
        sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MasterDataSourceConfig.MAPPER_LOCATION));
        return sessionFactoryBean.getObject();
    }

}

2.1.3 副数据源相关配置

主要是指定数据源、扫描的mapper地址、事务管理器等信息。

@Configuration
// 指定从数据库扫描对应的Mapper文件,生成代理对象
@MapperScan(basePackages = "com.diary.it.multi.datasource.mapper2", sqlSessionFactoryRef = "slaveSqlSessionFactory")
public class SlaveDataSourceConfig {
    // mapper.xml所在地址
    private static final String MAPPER_LOCATION = "classpath*:mapper2/*.xml";

    /**
     * 数据源
     */
    @Bean(name = "slaveDataSource")
    // 读取spring.datasource.slave前缀的配置文件映射成对应的配置对象
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource dataSource() {
        DataSource build = DataSourceBuilder.create().build();
        return build;
    }

    /**
     * 事务管理器
     */
    @Bean(name = "slaveTransactionManager")
    public PlatformTransactionManager dataSourceTransactionManager(@Qualifier("slaveDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    /**
     * session工厂
     */

    @Bean(name = "slaveSqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource) throws Exception {
        final SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource);
        sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(SlaveDataSourceConfig.MAPPER_LOCATION));
        return sessionFactoryBean.getObject();
    }

}

2.1.4 UserController层

/** *控制层 */ 
@RestController 
public class UserController { 
  @Autowired 
  private UserService userService; 
  /** 
    * 通过用户名获取用户信息,以及从库的地址信息 
    */ 
  @RequestMapping(value = "/user",method = RequestMethod.GET) 
  public User findUserByName(@RequestParam(value = "name",required = true) String name){ 
    return userService.findByName(name); 
  } 
}

2.1.5 Service层

照常注入主从两个Dao层

@Service 
public class UserService { 
  @Autowired 
  private UserDao userDao; 
  // 主数据源 

  @Autowired 
  private CityDao cityDao; 
  // 从数据源 

  public User findByName(String userName) { 
    User user = userDao.findByName(userName); 
    City city = cityDao.findByName("上海市"); 
    System.out.println(city); 
    user.setDescription("从数据库->"+city.getDescription()); 
    user.setCity(city); 
    return user; 
  }
}

2.2 AbstractRoutingDataSource AOP 方式实现

AbstrictRoutingDataSource的本质就是利用一个Map将数据源存储起来,然后通过Key来得到Value来修改数据源。

2.2.1 主要步骤

在 SpringBoot 项目中实现读写分离通常需要以下几步:

  1. 配置数据源:你需要为读操作和写操作分别配置一个数据源。
  2. 创建数据源路由逻辑:这通常通过扩展 Spring 的 AbstractRoutingDataSource 来实现。它允许你根据一定的逻辑来决定使用哪个数据源(读或写)。
  3. 配置事务管理器:这使得你能够在使用不同数据源时保持事务的一致性。
  4. 服务层或DAO层设计:确保在执行读操作时使用读数据源,在执行写操作时使用写数据源。
  5. 自定义切面,在切面中解析 @DataSource 注解。当一个方法或者类上面,有 @DataSource 注解的时候,将 @DataSource 注解所标记的数据源列出来存入到 ThreadLocal 中。

注意:这里使用ThreadLocal的原因是为了保证我们的线程安全。

2.2.2 配置文件类

spring:
  datasource:
    local:
      username: root
      password: 123456
      driver-class-name: com.mysql.jdbc.Driver
      jdbc-url: jdbc:mysql://localhost:3306/demo?serverTimezone=UTC&useUnicode=true@characterEncoding=utf-8
    remote:
      username: root
      password: 123456
      driver-class-name: com.mysql.jdbc.Driver
      jdbc-url: jdbc:mysql://192.168.85.111:3306/demo?serverTimezone=UTC&useUnicode=true@characterEncoding=utf-8

2.2.3 创建数据源枚举类

package com.mashibing.mult;

public enum DataSourceType {
    REMOTE,
    LOCAL
}

2.2.4 数据源切换处理

package com.mashibing.mult;

public class DynamicDataSourceContextHolder {

    /**
     * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
     *  所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
     */
    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    /**
     * 设置数据源变量
     * @param dataSourceType
     */
    public static void setDataSourceType(String dataSourceType){
        System.out.printf("切换到{%s}数据源", dataSourceType);
        CONTEXT_HOLDER.set(dataSourceType);
    }

    /**
     * 获取数据源变量
     * @return
     */
    public static String getDataSourceType(){
        return CONTEXT_HOLDER.get();
    }

    /**
     * 清空数据源变量
     */
    public static void clearDataSourceType(){
        CONTEXT_HOLDER.remove();
    }
}
package com.mashibing.mult;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.sql.DataSource;
import java.util.Map;

public class DynamicDataSource extends AbstractRoutingDataSource {

    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        // afterPropertiesSet()方法调用时用来将targetDataSources的属性写入resolvedDataSources中的
       //afterPropertiesSet 也可以重修此方法
        super.afterPropertiesSet();
    }

    /**
     * 根据Key获取数据源的信息
     *
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}

2.2.5 注入数据源

package com.mashibing.mult;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
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 javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class DataSourceConfig {
    @Bean
    @ConfigurationProperties("spring.datasource.remote")
    public DataSource remoteDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.local")
    public DataSource localDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    @Bean(name = "dynamicDataSource")
    @Primary
    public DynamicDataSource dataSource(DataSource remoteDataSource, DataSource localDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.REMOTE.name(), remoteDataSource);
        targetDataSources.put(DataSourceType.LOCAL.name(), localDataSource);
        return new DynamicDataSource(remoteDataSource, targetDataSources);
    }
}

2.2.6 自定义多数据源切换注解

package com.mashibing.mult;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    /**
     * 切换数据源名称
     */
    DataSourceType value() default DataSourceType.REMOTE;
}

2.2.7 AOP拦截类的实现

package com.mashibing.mult;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Aspect
@Order(1)
@Component
public class DataSourceAspect {

    @Pointcut("@annotation(com.mashibing.mult.DataSource)")
    public void dsPointCut() {

    }

    @Around("dsPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        DataSource dataSource = method.getAnnotation(DataSource.class);
        if (dataSource != null) {
            DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
        }
        try {
            return point.proceed();
        } finally {
            // 销毁数据源 在执行方法之后
            DynamicDataSourceContextHolder.clearDataSourceType();
        }
    }
}

2.2.8 使用切换数据源注解

package com.mashibing.mult;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Map;

@RestController
public class EmpController {

    @Autowired
    JdbcTemplate jdbcTemplate;

    @GetMapping("/local")
    @DataSource(value = DataSourceType.LOCAL)
    public List<Map<String, Object>> local(){
        List<Map<String, Object>> maps = jdbcTemplate.queryForList("select * from emp");
        return maps;
    }
    @GetMapping("/remote")
    @DataSource(value = DataSourceType.REMOTE)
    public List<Map<String, Object>> remote(){
        List<Map<String, Object>> maps = jdbcTemplate.queryForList("select * from emp");
        return maps;
    }

}

2.3 Dynamic-datasource

MyBatis-Plus(简称 MP)是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

使用MP提供的Dynamic-datasource多数据源框架,实现在不同数据源间切换,通过@DS注解实现对master和slave数据库的选择,展示了从配置、持久层到控制层的完整代码示例。

dynamic-datasource特性

  • 支持 数据源分组 ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。
  • 支持数据库敏感配置信息 加密 ENC()。
  • 支持每个数据库独立初始化表结构schema和数据库database。
  • 支持无数据源启动,支持懒加载数据源(需要的时候再创建连接)。
  • 支持 自定义注解 ,需继承DS(3.2.0+)。
  • 提供并简化对Druid,HikariCp,BeeCp,Dbcp2的快速集成。
  • 提供对Mybatis-Plus,Quartz,ShardingJdbc,P6sy,Jndi等组件的集成方案。
  • 提供 自定义数据源来源 方案(如全从数据库加载)。
  • 提供项目启动后 动态增加移除数据源 方案。
  • 提供Mybatis环境下的 纯读写分离 方案。
  • 提供使用 spel动态参数 解析数据源方案。内置spel,session,header,支持自定义。
  • 支持 多层数据源嵌套切换 。(ServiceA >>> ServiceB >>> ServiceC)。
  • 提供 基于seata的分布式事务方案。
  • 提供 本地多数据源事务方案。 附:不能和原生spring事务混用

2.3.1 引入依赖

<!-- 多数据切换所需依赖 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.0.0</version>
</dependency>

2.3.2 配置文件

sever:
  # 端口
  port: 8080

# 配置数据源
spring:
  datasource:
    dynamic:
      primary: master #设置默认的数据源或者数据源组,默认值即为master
      strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        master:
          # 数据库路径jdbc:mysql://localhost:3306/mydb 的缩写,并配置时区
          url: jdbc:mysql:///mydb?serverTimezone=GMT%2B8
          username: root
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
        slave:
          url: jdbc:mysql:///mydb2?serverTimezone=GMT%2B8
          username: root
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver

# 打印MyBatis SQL 日志
logging:
  level:
    com.guqueyue.test.dao: debug # 写接口的包名

注意:

这里数据源的名字可以自定义,什么master、slave都可以随意命名。
这里可以根据格式自行添加数据源的个数,并且支持多种数据库,如oracle、达梦等。此处为了演示方便,都使用了mysql数据库。

2.3.3 dao层

package com.guqueyue.test.dao;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.guqueyue.test.entity.User;
import org.apache.ibatis.annotations.Select;

import java.util.List;

/**
 * @Author: guqueyue
 * @Description: 映射接口UserMapper
 * @Date: 2023/12/19
 **/
public interface UserMapper extends BaseMapper<User> {

    @DS("master") // 默认为主数据源,其实可以省略
    @Select("select * from users")
    List<User> selectUserList();

    @DS("slave") // 切换为slave数据源
    @Select("select * from users")
    List<User> selectUserListBySlave();

}

注意:

  1. @DS注解不止可以用在持久层,可以用在任意的类和方法上。
  2. @DS注解作用在方法上的优先级 > 类。

也就是说可以在类上加一个

@DS

注解默认一个该类的数据源,如

 @DS("master")


再在具体的方法上加一个

@DS

注解做个性化指定,如

 @DS("slave")

,效果等同:

/**
 * @Author: guqueyue
 * @Description: 映射接口UserMapper
 * @Date: 2023/12/19
 **/
@DS("master") // 默认为主数据源,其实可以省略
public interface UserMapper extends BaseMapper<User> {
    
    @Select("select * from users")
    List<User> selectUserList();

    @DS("slave") // 切换为slave数据源
    @Select("select * from users")
    List<User> selectUserListBySlave();

}

2.3.4 创建service

package com.guqueyue.test.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.guqueyue.test.entity.User;

import java.util.List;

/**
 * @Author: guqueyue
 * @Description: 用户service接口
 * @Date: 2023/12/19
 **/
public interface IUserService extends IService<User> {

    List<User> selectUserList(String type);
}

创建service实现类,并调用持久层接口,根据传入的参数切换不同的方法:

package com.guqueyue.test.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.guqueyue.test.dao.UserMapper;
import com.guqueyue.test.entity.User;
import com.guqueyue.test.service.IUserService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

/**
 * @Author: guqueyue
 * @Description: 用户实现类
 * @Date: 2023/12/19
 **/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Resource
    private UserMapper userMapper;

    @Override
    public List<User> selectUserList(String type) {

         // do something: 此处可根据实际情况进行业务选择,如根据当前登录的用户信息来选择不同的数据库
        // 此处为了方便演示,直接用前端传入的type来判断

        return "slave".equals(type) ? userMapper.selectUserListBySlave()
                                    : userMapper.selectUserList();
    }
}

2.3.5 .编写控制层代码

package com.guqueyue.test.controller;

import com.guqueyue.test.entity.User;
import com.guqueyue.test.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @Author: guqueyue
 * @Description: 用户控制层
 * @Date: 2023/12/19
 **/
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private IUserService userService;

    /**
     * 查询用户列表
     * @return
     */
    @RequestMapping("/list")
    public List<User> userList(String type) {
        System.out.println("接收到的数据源类型为:" + type);

        return userService.selectUserList(type);
    }
}

2.4 JdbcTemplate

jdbcTemplate连接数据库就是用jdbcTemplate对象去调用它的query、udate、insert等方法操作数据库。
jdbcTemplate必须有dao层实现类,因为jdbcTemplate是在dao层用jdbcTemplate对应的方法操作sql语句的

2.4.1 配置文件

application.properties

application.yml

中配置两个数据源的基本属性:

# 数据源1配置
spring.datasource1.url=jdbc:mysql://localhost:3306/db1
spring.datasource1.username=user1
spring.datasource1.password=pass1
spring.datasource1.driver-class-name=com.mysql.cj.jdbc.Driver
 
# 数据源2配置
spring.datasource2.url=jdbc:mysql://localhost:3306/db2
spring.datasource2.username=user2
spring.datasource2.password=pass2
spring.datasource2.driver-class-name=com.mysql.cj.jdbc.Driver

创建配置类来定义两个数据源的Bean和对应的

JdbcTemplate

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")
    @ConfigurationProperties(prefix = "spring.datasource1")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }
 
    @Bean(name = "secondaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource2")
    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);
    }
}

2.4.2 定义数据库表对应的实体类

package com.qf.entity;
import java.util.Date;
public class Users {
    private Integer userId;
    private String username;
    private Date birthday;
    private Integer age;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Integer getUserId() {
        return   userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Users{" +
                "userId=" + userId +
                ", username='" + username + '\'' +
                ", birthday=" + birthday +
                ", age=" + age +
                '}';
    }
}

2.4.3 dao层

package com.qf.dao;

import com.qf.entity.Users;

import java.util.List;

//mybatis框架操作数据库有两种方式,一种是配置文件方式,一种是注解方式
public interface UsersDao {
    public List<Users> getall();
}
package com.qf.dao.impl;

import com.qf.dao.UsersDao;
import com.qf.entity.Users;
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//Repository给当前类创建对象
public class UsersDaoImpl implements UsersDao {
    @Resource(name = "onetemplate")
private JdbcTemplate jdbcTemplate;

//@Autowired
//@Qualifier("db2JdbcTemplate")
//private JdbcTemplate jdbcTemplate;

    @Override
    public List<Users> getall() {
        List<Users> userList = jdbcTemplate.query("SELECT * FROM users", new RowMapper<Users>() {
            @Override
            public Users mapRow(ResultSet resultSet, int i) throws SQLException {
                Users users = new Users();
                users.setAge(resultSet.getInt("age"));//第二个age是实体类Users的age属性对应的表的字段名。反正第二个age是字段名,第一个age是实体类属性
                users.setBirthday(resultSet.getDate("birthday"));
                users.setUsername(resultSet.getString("username"));
                users.setUserId(resultSet.getInt("userId"));
                return users;
            }
        });
        return userList;
    }
}

2.4.4 service层

package com.qf.service;

import com.qf.entity.Users;

import java.util.List;

public interface UsersService {
    public List<Users> getall();
}
package com.qf.service.impl;

import com.qf.dao.UsersDao;
import com.qf.entity.Users;
import com.qf.service.UsersService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

@Service
public class UsersServiceImpl implements UsersService{
@Resource
 private  UsersDao usersDao;
    @Override
    public List<Users> getall() {
        return usersDao.getall();
    }
}

2.4.5 控制曾代码

package com.qf.controller;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.qf.entity.Users;
import com.qf.service.UsersService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.annotation.Resource;
import java.util.List;

@Controller
public class TestController {
    @Resource
    private UsersService usersService;
    @RequestMapping("/test")//访问此请求的地址是localhost:8080/test
    public String test( ModelMap map){

        System.out.println("testjsp-----------");
        List<Users> usersList = usersService.getall();
        System.out.println("userList:"+usersList);
         map.addAttribute("usersList",usersList);
        return "show";//此时实际跳去的页面时/show.jsp.
    }

}

本文转载自: https://blog.csdn.net/zhyooo123/article/details/143417345
版权归原作者 常生果 所有, 如有侵权,请联系我们删除。

“【SpringBoot】多数据源N种实现详解”的评论:

还没有评论