JDBC

  • 单个数据源
    1. 引入依赖
<!--
  spring-boot-starter-jdbc依赖与HikariCP和spring-jdbc
  HikariCP:spring boot 2.0默认使用的数据连接池(传说最快)
  spring-jdbc:针对JDBC操作spring封装的api
-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--示例使用mysql 8作数据库、这里引入mysql驱动-->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <scope>runtime</scope>
 </dependency>
  1. 数据源配置
    #mysql8 使用com.mysql.cj.jdbc.Driver驱动,PS:serverTimezone=GMT需要加上,不然服务器时间不能同步
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=GMT&autoReconnect=true&useUnicode=true&characterEncoding=UTF-8
    spring.datasource.username=root
    spring.datasource.password=
    spring.datasource.max-idle=10
    spring.datasource.max-wait=10000
    spring.datasource.min-idle=5
    spring.datasource.initial-size=5
  2. 使用spring封装的api执行sql
    dao接口:
public interface TestDao {
    /** 新增一条数据 */
    public int insert(EntityTest test)throws RuntimeException;
    /** 修改一条数据 */
    public int update(EntityTest test)throws RuntimeException;
    /** 根据主键删除一条数据 */
    public int delete(Integer id)throws RuntimeException;
    /** 查询全部数据 */
    public List<EntityTest> select()throws RuntimeException;
    /** 根据主键查询一条数据 */
    public EntityTest select(Integer id)throws RuntimeException;
}

dao实现类:

@Repository("testDao")
public class TestDaoImpl implements TestDao {
    /** spring boot 自带JDBC api */
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Override
    public int insert(EntityTest test) throws RuntimeException {
        // TODO Auto-generated method stub
        String sql ="insert into t_test (createDatetime,remark) values (?,?)";
        Date createDatetime = test.getCreateDatetime();
        String remark = test.getRemark();
        return jdbcTemplate.update(sql, createDatetime,remark);
    }

    @Override
    public int update(EntityTest test) throws RuntimeException {
        // TODO Auto-generated method stub
        String sql ="update t_test set createDatetime = ?,remark = ? where id = ?";
        Date createDatetime = test.getCreateDatetime();
        String remark = test.getRemark();
        Integer id = test.getId();
        return jdbcTemplate.update(sql, createDatetime,remark,id);
    }

    @Override
    public int delete(Integer id) throws RuntimeException {
        // TODO Auto-generated method stub
        String sql = "delete from t_test where id = ?";
        return jdbcTemplate.update(sql, id);
    }

    @Override
    public List<EntityTest> select() throws RuntimeException {
        // TODO Auto-generated method stub
        String sql = "select id,createDatetime,remark from t_test order by createDatetime desc";
        //     这种方式通过自己映射的字段与数学集合来反馈
        return jdbcTemplate.query(sql, new entityTestResult());
        //     这种方式通过框架自动映射来反馈
//      return jdbcTemplate.query(sql, new BeanPropertyRowMapper<EntityTest>(EntityTest.class));
    }

    @Override
    public EntityTest select(Integer id) throws RuntimeException {
        // TODO Auto-generated method stub
        String sql = "select id,createDatetime,remark from t_test where id=?";
        return jdbcTemplate.queryForObject(sql, new Object[] {id}, new BeanPropertyRowMapper<EntityTest>(EntityTest.class));
    }
    /**
     * 字段与属性封装
     * @author kyoxue
     * @date 2019年10月28日
     */
    class entityTestResult implements RowMapper<EntityTest>{

        @Override
        public EntityTest mapRow(ResultSet rs, int rowNum) throws SQLException {
            // TODO Auto-generated method stub
            EntityTest test = new EntityTest();
            test.setId(rs.getInt("id"));
            test.setCreateDatetime(rs.getDate("createDatetime"));
            test.setRemark(rs.getString("remark"));
            return test;
        }
        
    }
}

封装用的实体:

public class EntityTest {

    private Integer id;
    private Date createDatetime;
    private String remark;
        //gettter...setter...
    @Override
    public String toString() {
        // TODO Auto-generated method stub
        StringBuffer buff = new StringBuffer();
        buff.append("id:").append(this.id).append(" ");
        buff.append("createDatetime:").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.getCreateDatetime())).append(" ");
        buff.append("remark:").append(this.remark).append(" ");
        return buff.toString();
    }
}
  1. 附上示例库表以及test
USE `test`;

DROP TABLE IF EXISTS `t_test`;
-- 注意DEFAULT CHARSET utf8 COLLATE utf8_general_ci 是之前老版sql的写法。
-- mysql8使用DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci
CREATE TABLE `t_test` (
  `id` INT AUTO_INCREMENT  NOT NULL PRIMARY KEY COMMENT 'primary key',
  `createDatetime` DATETIME NULL COMMENT 'create time',
  `remark` VARCHAR(500) NULL  COMMENT 'remark',
  KEY(createDatetime)
 ) ENGINE=INNODB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci COMMENT='test table';

使用junit测试:

/**
 * 集成springboot环境测试,主要测后台服务,dao等需要@autoware注入依赖的功能
 * 这里因为项目加入了websocket例子,所以将test引入容器环境并随机开端口,
 * 否则会报javax.websocket.server.ServerContainer not available
 * 如果没有websocket,(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)可以去除
 * @author kyoxue
 * @date 2019年10月15日
 */
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class applicationTest {
    @Autowired
    private TestDao testDao;
    @Test
    public void testJDBC()throws Exception{
        /** add */
//      EntityTest test = new EntityTest();
//      test.setCreateDatetime(new Date());
//      test.setRemark("test remark");
//      testDao.insert(test);
        
        /** find by primary key */
//      EntityTest test = testDao.select(1);
//      assertNotEquals(test, null);
//      System.out.println(test.toString());
        
        /** update */
//      test.setRemark("new remark");
//      testDao.update(test);
        
        /** find table data all */
//      List<EntityTest> tests = testDao.select();
//      assertNotEquals(tests, null);
//      for (EntityTest entityTest : tests) {
//          System.out.println(entityTest.toString());
//      }
        
        /** delete data by primary key*/
//      testDao.delete(1);
    }
}
  • 多数据源
  1. 依赖和单数据源一致,见上面
  2. 数据源配置(原先单数据源配置和单数据源的程序示例全部删掉,否则引起冲突不兼容
    一个项目最好要么多数据源配置,要么就单数据源。
    这里把上面单数据源配置也换成了多数据源配置方式。

spring.datasource.test.driver-class-name=com.mysql.cj.jdbc.Driver
#新版本mysql,必须带上时区说明serverTimezone=GMT
#GMT会少了8小时,所以这里用GMT+8,转义后:GMT%2b8
spring.datasource.test.jdbc-url=jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2b8&autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&useSSL=true
spring.datasource.test.username=root
spring.datasource.test.password=
spring.datasource.test.max-idle=10
spring.datasource.test.max-wait=10000
spring.datasource.test.min-idle=5
spring.datasource.test.initial-size=5

本地 订单数据源配置

spring.datasource.order.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.order.jdbc-url=jdbc:mysql://localhost:3306/order?serverTimezone=GMT%2b8&autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&useSSL=true
spring.datasource.order.username=root
spring.datasource.order.password=
spring.datasource.order.max-idle=10
spring.datasource.order.max-wait=10000
spring.datasource.order.min-idle=5
spring.datasource.order.initial-size=5

1.251 业务数据源配置

spring.datasource.report.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.report.jdbc-url=jdbc:mysql://xxx.xxx.xxx.xxx:3306/xx?serverTimezone=GMT%2b8&autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&useSSL=true
spring.datasource.report.username=root
spring.datasource.report.password=
spring.datasource.report.max-idle=10
spring.datasource.report.max-wait=10000
spring.datasource.report.min-idle=5
spring.datasource.report.initial-size=5

这里配置有个共同点,分别有相同的前缀:spring.datasource.test、spring.datasource.order、spring.datasource.report,以此区分数据源配置
注意:多数据源用的是HikariCP连接池,驱动key用driver-class-name,连接地址key用jdbc-url
因为多数据源,所以要定义不同的DataSource对像从而获取不同的JdbcTemplate对象。
新建个springboot Configuration(在SpringBootApplication即启动入口类同目录下)
主要针对不同数据源生成不同的DataSource,同时设置配置文件数据源对应前缀和返回DataSource对象的别名。
@Qualifier:当存在相同bean配置多个的时候,这时候需要取别名区分
@Primary: 如果系统认识不了用哪个的时候,会优先选择该注解对应的bean
@ConfigurationProperties:给对应的DataSource通过配置前缀找到参数信息

/**
 * jdbc多数据集源配置
 * @author kyoxue
 * @date 2019年10月28日
 */
@Configuration
public class DemoDBConfiguration {
    /**  定义数据源  */
    //默认数据源
    @Primary
    //bean名称
    @Bean(name="orderDB")
    //为数据源取别名区分
    @Qualifier("orderDB")
    //指定配置数据源前缀
    @ConfigurationProperties("spring.datasource.order")
    public DataSource orderDB() {
        return DataSourceBuilder.create().build();
    }
    //bean名称
    @Bean(name="reportDB")
    //为数据源取别名区分
    @Qualifier("reportDB")
    //指定配置数据源前缀
    @ConfigurationProperties("spring.datasource.report")
    public DataSource reportDB() {
        return DataSourceBuilder.create().build();
    }
    //bean名称
    @Bean(name="testDB")
    //为数据源取别名区分
    @Qualifier("testDB")
    //指定配置数据源前缀
    @ConfigurationProperties("spring.datasource.test")
    public DataSource testDB() {
        return DataSourceBuilder.create().build();
    }
    /**  定义API  */
    //生成api对象 -- 订单操作
    @Bean(name = "orderApi")
    public JdbcTemplate orderApi(@Qualifier("orderDB") DataSource db) {
        return new JdbcTemplate(db);
    }
    //生成api对象 -- 业务操作
    @Bean(name = "reportApi")
    public JdbcTemplate reportApi(@Qualifier("reportDB") DataSource db) {
        return new JdbcTemplate(db);
    }
    //生成api对象 -- test
    @Bean(name = "testApi")
    public JdbcTemplate testApi(@Qualifier("testDB") DataSource db) {
        return new JdbcTemplate(db);
    }
}

  1. 使用多数据源配置生成的对应api进行不同数据源的sql操作
    新建各个数据源对应的实体对象(test的实体上面已经列出)
/** reportDB下的一张配置表*/
public class EntityConfig {

    private String code;
    private String value;
    private String remark;
    private String creater;
    private String modifier;
    private Date createTime;
    private Date modifyTime;
    private String state;
    //setter...getter...
    @Override
    public String toString() {
        // TODO Auto-generated method stub
        StringBuffer buffer = new StringBuffer();
        buffer.append("code:").append(code).append(" ");
        buffer.append("value:").append(value).append(" ");
        buffer.append("remark:").append(remark).append(" ");
        buffer.append("creater:").append(creater).append(" ");
        buffer.append("createTime:").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(createTime)).append(" ");
        buffer.append("state:").append(state).append(" ");
        return buffer.toString();
    }
}
/** orderDB下的一张示例订单表*/
public class EntityOrder {
    private Long id;
    private Date createDatetime;
    private String orderNo;
    //setter...getter...
}

为了简单,这里新建一个公共的dao接口:

public interface CommDao<T> {
    /** 新增一条数据 */
    public int insert(T t)throws RuntimeException;
    /** 修改一条数据 */
    public int update(T  t)throws RuntimeException;
    /** 根据主键删除一条数据 */
    public int delete(Object id)throws RuntimeException;
    /** 查询全部数据 */
    public List<T> select()throws RuntimeException;
    /** 根据主键查询一条数据 */
    public T select(Object id)throws RuntimeException;
}

然后order和report分别新建dao实现类(这里只为了演示多数据源操作,所以方法没全部实现)

@Repository("testOrderDao")
public class TestOrderDaoImpl implements CommDao<EntityOrder> {

    @Autowired
    private JdbcTemplate orderApi;

    @Override
    public int insert(EntityOrder t) throws RuntimeException {
        // TODO Auto-generated method stub
        String sql = "insert into t_order(createDatetime,orderNo) values(?,?)";
        return orderApi.update(sql, t.getCreateDatetime(),t.getOrderNo());
    }

    @Override
    public int update(EntityOrder t) throws RuntimeException {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public int delete(Object id) throws RuntimeException {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public List<EntityOrder> select() throws RuntimeException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public EntityOrder select(Object id) throws RuntimeException {
        // TODO Auto-generated method stub
        return null;
    }

}
@Repository("testReportDao")
public class TestReportDaoImpl implements CommDao<EntityConfig> {

    @Autowired
    private JdbcTemplate reportApi;

    @Override
    public int insert(EntityConfig t) throws RuntimeException {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public int update(EntityConfig t) throws RuntimeException {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public int delete(Object id) throws RuntimeException {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public List<EntityConfig> select() throws RuntimeException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public EntityConfig select(Object id) throws RuntimeException {
        // TODO Auto-generated method stub
        String sql = "select * from t_gnzl_config where code = ?";
        return reportApi.queryForObject(sql, new Object[] {id}, new BeanPropertyRowMapper<EntityConfig>(EntityConfig.class));
    }
}
  1. 附上额外示例的表
-- 这是一张本机另外库order里的一张t_order表
DROP TABLE IF EXISTS `t_order`;
-- 注意DEFAULT CHARSET utf8 COLLATE utf8_general_ci 是之前老版sql的写法。
-- mysql8使用DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci
CREATE TABLE `t_order` (
  `id` BIGINT auto_increment  not null primary key comment 'primary key',
  `createDatetime` datetime null comment 'create time',
  `orderNo` varchar(25) not null  comment 'order no',
  key(`orderNo`)
 ) ENGINE=innodb DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci COMMENT='order table';
-- 这是一张不在本机的xxx库中的t_gnzl_config表
USE `xxx`;
DROP TABLE IF EXISTS `t_gnzl_config`;
CREATE TABLE `t_gnzl_config` (
  `code` char(6) NOT NULL COMMENT '配置KEY',
  `value` varchar(1024) DEFAULT NULL COMMENT '配置信息',
  `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  `creater` varchar(64) NOT NULL,
  `createTime` datetime NOT NULL,
  `modifier` varchar(64) DEFAULT NULL,
  `modifyTime` datetime DEFAULT NULL,
  `state` char(1) NOT NULL DEFAULT 'Y',
  PRIMARY KEY (`code`),
  UNIQUE KEY `_u_code` (`code`)
) ENGINE=innodb DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_0900_ai_ci COMMENT='xxx配置信息表';
  1. 使用junit测试(这里只执行了下reportDB对应的数据源,测试成功!~)
/**
 * 集成springboot环境测试,主要测后台服务,dao等需要@autoware注入依赖的功能
 * 这里因为项目加入了websocket例子,所以将test引入容器环境并随机开端口,
 * 否则会报javax.websocket.server.ServerContainer not available
 * 如果没有websocket,(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)可以去除
 * @author kyoxue
 * @date 2019年10月15日
 */
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class applicationTest {
    @Autowired
    private CommDao<EntityConfig> testReportDao;
    @Test
    public void testJDBC2()throws Exception{
        EntityConfig entityConfig = testReportDao.select("xxxx");
        assertNotEquals(entityConfig, null);
        System.out.println(entityConfig.toString());
    }
}
  • springboot jdbc over...
    ps:数据源配置很多时候涉及生产环境与测试环境的区分,springboot提供了默认选择环境配置文件的机制
    生成2份配置,前缀都是application-,而后缀就是当前项目欲使用的配置名称,然后将后缀名称告诉springboot,就会自动激活。


    image.png

    在application.properties主配置中,指定环境名称:offline即使用application-offline配置文件

#单个application配置文件无法满足不同环境的配置,这里使用该配置指定配置名来区分使用
#通过application-前缀识别springboot配置文件,通过后缀指定要使用的配置文件
spring.profiles.active=offline
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351