SpringJDBC 用法总结

大部分网上的 Spring 教程大多讲解的是 SSM 框架,其中的 M 现在指的是 MyBatis 这个第三方 ORM 框架,在我看来,MyBatis 有它的优越性,如 SQL 语句与业务代码分离,业务逻辑处理很灵活等。但是在小型业务系统开发时,由于 SSM 框架定义过于规范,开发具体功能时,会写很多接口,而真正的业务逻辑得不到较优的处理,呆板的框架应用反而适得其反。

本文主要介绍 SpringJDBC 的基础操作,方便研发人员快速的实现数据的增删改查,而不仅仅拘泥于繁重的、各种框架组合而成的项目之中。

SpringJDBC 封装了基础的 JDBC 操作,让我们不用去关心 获取驱动、建立连接、关闭连接等非业务操作,让我们更加专注于业务的实现。

本文将对 SpringJDBC 的用法进行介绍,文主要内容如下:

  • 基本的数据操作
  • 自增主键的获取

1. 基本的数据操作

1.1 更改

更改 主要使用的是 update 方法

  • 重载方法:主要包括三种,按需选择,下面展示的从简单到复杂。
sql = "insert into customer(name,age)values (?,?)";
int rows = jdbcTemplate.update(sql,"周杰伦",35);
int rows1 = jdbcTemplate.update(sql,new Object[]{"周杰伦",35});
int rows2 = jdbcTemplate.update(sql,new Object[]{"周杰伦",35},new int[]{Types.VARCHAR,Types.DECIMAL}); // 显式指定数据类型
  • 批量更改:一次性执行多条 update 语句,使用 batchUpdate 方法
    • jdbcTemplate.batchUpdate(String[] sql); 固定参数值
    • jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter()); 可变参数值
String sql1 = "insert into customer(NAME,AGE) values (1,1)";
String sql2 = "insert into customer(NAME,AGE) values (1,1)";
String sql3 = "insert into customer(NAME,AGE) values (1,1)";
String[] strings = new String[]{sql1,sql2,sql3};
int[] a = jdbcTemplate.batchUpdate(strings); // 固定参数的多条 SQL 语句组成一个数组传入
System.out.println(Arrays.toString(a));


sql = "insert into customer(NAME,AGE) values (?,?)";
final List<String> list = new ArrayList<String>(){};
list.add("1");list.add("2");list.add("3");
int[] b = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {  // 带可变参数的 SQL 语句传入
    public void setValues(PreparedStatement preparedStatement, int i) throws SQLException {
        preparedStatement.setString(1,list.get(i));
        preparedStatement.setString(2,list.get(i));
    }
    public int getBatchSize() {
        return list.size();
    }
});
System.out.println(Arrays.toString(b));
1.2 查询

查询 主要使用的是 query、queryForXX 等函数,而 queryForXX 系列底层其实调用的是 query 方法,故这里只介绍通过 query 方法查询数据。

query 有多个重载方法,例如传入固定参数的 SQL 语句,传入可变参数的 SQL 语句等等,语法类似上面介绍的 update 方法,这里介绍两种带回调方法的查询语句:

(1) void query(String sql, RowCallbackHandler rch)

使用 RowCallbackHandler 既可以返回单行结果,也可以返回多行结果。Spring 会对查询的结果集逐行调用 processRow 方法,用户不用关心怎么读取下一行数据的问题。

sql = "select CUST_ID,NAME,AGE from customer where CUST_ID = ?";
final Customer customer = new Customer();
// 查询单行数据
jdbcTemplate.query(sql, new Object[]{1}, new RowCallbackHandler() {
    public void processRow(ResultSet resultSet) throws SQLException {
        customer.setCUST_ID(Integer.parseInt(resultSet.getString("CUST_ID")));
        customer.setNAME(resultSet.getString("NAME"));
        customer.setAGE(Integer.parseInt(resultSet.getString("AGE")));
    }
});
System.out.println(customer.toString()); // Customer{CUST_ID=1, NAME='张三', AGE=12}

sql = "select CUST_ID,NAME,AGE from customer";
final List<Customer> customerList = new ArrayList<Customer>();
final Customer customer1 = new Customer();
// 查询到多行数据,代码与上面没有区别,唯一的不同就是将结果集存入 List
jdbcTemplate.query(sql, new RowCallbackHandler() {
    public void processRow(ResultSet resultSet) throws SQLException {
        customer1.setCUST_ID(Integer.parseInt(resultSet.getString("CUST_ID")));
        customer1.setNAME(resultSet.getString("NAME"));
        customer1.setAGE(Integer.parseInt(resultSet.getString("AGE")));
        customerList.add(customer1);
    }
});
System.out.println(customerList);// [Customer{CUST_ID=38, NAME='3', AGE=3}, Customer{CUST_ID=38, NAME='3', AGE=3}]

(2) T queryForObject(String sql, RowMapper rowMapper)

使用 RowMapper 处理结果集,它直接返回的是 List,相比于上面,我们不用手动将对象添加到 List 中

sql = "select CUST_ID,NAME,AGE from customer";

List<Customer> customerList = jdbcTemplate.query(sql, new RowMapper<Customer>() {
    public Customer mapRow(ResultSet resultSet, int i) throws SQLException {
        Customer customer = new Customer();
        customer.setCUST_ID(Integer.parseInt(resultSet.getString("CUST_ID")));
        customer.setNAME(resultSet.getString("NAME"));
        customer.setAGE(Integer.parseInt(resultSet.getString("AGE")));
        return customer;
    }
});
System.out.println(customerList); // [Customer{CUST_ID=38, NAME='3', AGE=3}, Customer{CUST_ID=38, NAME='3', AGE=3}]

(3) 方法 a 与方法 b 的使用场景与注意事项

  • RowCallbackHandler 接口的实现类是可以有状态,即在多线程环境下,可能会有线程安全的问题。
    下面代码就展示了其实现类 RowCountCallbackHandler 打印结果集行数的示例,在多线程环境中,若行数发生变化,下面的结果可能不会一致。而 RowMapper 实现类没有这种情况。

    RowCountCallbackHandler countCallbackHandler = new RowCountCallbackHandler();
    jdbcTemplate.query("select * from customer",countCallbackHandler);
    System.out.println(countCallbackHandler.getRowCount());
  • 当处理大结果集时, RowMapper 是将结果集所有数据放到 List 中,这会占用大量 JVM 内存。RowCallbackHandler 接口内的 processRow() 方法则是一边获取数据一边处理。RowMapper 是先获取再处理,RowCallbackHandler 边获取边处理。

2. 自增主键的获取

在数据库层面,一个表往往会有一个主键来唯一标识这一行数据。

新增记录时,返回新增记录对应的自增主键值,即返回的该列属性包含两个,一个是自增,另一个是主键。

SpringJDBC 提供了对自增主键获取的方法,插入数据时,返回插入数据的主键。

定义 KeyHolder 对象即可完成上述功能,步骤如下:

1.执行 statement 语句时,指定绑定主键

connection.prepareStatement(sql1,PreparedStatement.RETURN_GENERATED_KEYS) 

final String sql1 = "insert into customer(name,age)values(?,?)";
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() {
    public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
        PreparedStatement preparedStatement = connection.prepareStatement(sql1,PreparedStatement.RETURN_GENERATED_KEYS);
        preparedStatement.setString(1,"suiyia");
        preparedStatement.setString(2,"30");
        return preparedStatement;
    }
},keyHolder);
System.out.println(keyHolder.getKey().toString());

2.keyHolder 的返回形式

Number getKey() 主键是数值类型,单列、一行
Map<String,Object> getKeys复合类型,多列、一行
List<Map<String,Object>> getKeyList 复合类型、多列、多行

3. 小结

本文只是对 SpringJDBC 的基本常用操作做了简单介绍,之所以介绍 SpringJDBC,是因为在快速开发的后台系统中,没有足够的时间去写接口等,这种方式在小型简单的系统开发时效率更高。但在大型复杂的系统研发中,踏实写接口,遵守框架的开发规范是很有必要的。

一、SBORM 介绍 1、目前只考虑支持 mysql; 2、基于spring jdbc的上层封装,底层jdbc操作基于JdbcTemplate,对于使用spring jdbc的人会有一点价值,比较简洁的封装可以节省很多重复劳动,具体节省多少可以看看example; 3、实现一套简单的ORM(直接使用spring rowmapper,insert自己实现),可以基于对象进行crud和相对复杂(感觉比hibernate强大一点)的sql操作; 4、基于对象指定查询的字段,大部分时候可以忘掉表结构进行业务开发; 5、支持简单的数据库路由,读写分离(半自动,需要指定取writer还是reader,默认规则reader采用随机的方式,当然也可以手动指定); 6、支持简单的分表,主要是针对一定规则的分表,比如百分表、千分表,也可以自己指定分表后缀; 7、简单的单表查询(比如所有条件是and或者or结构),基本实现0sql代码编写(类似HibernateTemplate selectByExample、findByCriteria、find等方法); 8、简单的单表排序支持,支持多个排序条件组合; 9、对于复杂的sql查询,提供获取jdbctemplate实例进行操作,类似spring jdbc的常规用法; 10、提供Entity代码生成接口,Entity并非简单的pojo(尽可能不要去修改此类),引入字段常量类,方便查询的时候指定选择字段,从而更好实现查询条件的封装; 二、为什么写SBORM? 1、hibernate:过于臃肿,使用不够灵活,优化难(其实主要是因为很少用),HQL感觉就是个渣,在 mysql几乎一统天下的背景下,跨数据库级别的兼容吃力不讨好。Hibernate的对象化关联处理确实挺强大,但是使用起来坑太多,有多少人敢在项目 中大范围使用真不知道,屠龙刀不是人人都提的起啊。 2、mybatis:轻量级,基于xml的模式感觉不利于封装,代码量不小,基于xml维护也麻烦(个人观点, 现在注解模式貌似也挺不错),感觉mybatis更适合存在dba角色的年代,可以远离代码进行sql调优,复杂的查询拼装起来也更加优雅(java基本 就是if else ...),但是对于查询业务简单但是数据库集群环境的场景有点憋屈(其实对mybatis使用也不多,瞎评论^_^)。 3、spring jdbc:小巧,灵活,足够优秀,个人比较喜欢使用,但是代码量偏大,原生的接口重复劳动量大,比如insert、mapper之类的; SBORM只是针对spring jdbc的一些不方便的地方,做了一些封装,更加简化日常的开发工作,基于spring jdbc的RowMapper自动实现对象映射,也勉强算的上叫ORM,只是大部分功能已经由spring jdbc实现了。 平时不太喜欢使用hibernate和mybatis,主要是使用spring jdbc,写这个东西的出发点主要是平时使用spring jdbc觉 得比较麻烦,重复性的代码偏多,一方面通过自动mapper降低返回结果处理工作量,另一方面参考hibernate对象化查询条件的模式,写了一个 QueryBudiler,使得更多简单的单表查询可以通过对象组织查询、更改逻辑,避免过多去写相似性的SQL语句,减少DAO接口量。 三、一些亮点 1、Entity的设计:很多人看了也许会说,这个不是POJO,不是纯粹的Java Bean,显得很另类。但是有多人在开发过程中(特别是在写sql的时候),经常要去看看表结构设计?还有多少次因为改了表某个字段,还得遍历去查找哪些 sql使用了这个字段?多少次看到在代码中直接传入字段名作为查询参数感到别扭?如果将表结构字段都用java对象去描述,能够解决这些问题,就不必要在 乎是不是POJO了,后面看example的时候应该能体会这么做的一些好处,至少我觉得是挺方便的,将大部分查询脱离表结构设计。 2、简单的数据库路由:如果分库结构不是太复杂(比如简单的读写分离、或者多个库集成),BaseDao可以自 动进行路由(比如读写分离,根据业务模式指定读、写库),如果非默认的路由规则,也可以通过手动设置的模式,进行数据库路由。数据库路由直接由 Entity指定,所有的路由都是根据Entity识别,也就是说查询也是围绕Entity展开的,避免类似使用spring jdbc的时候,各种 template实例跳来跳去,硬编码引入,写一个业务还得看看到底该用哪个template,尤其是多个数据库共用一个template实例的时候。 3、QueryBuilder:单表查询基本上都可以实现零Sql(除非查询条件特别复杂的),更新、删除等操作也可以通过QueryBuilder进行批量处理,不局限于根据主键来处理。 4、分表操作的支持:对于分表操作和常规的使用没有区别,只是指定分表规则,mybatis好像也可以通过制定参数实现分表处理,没搞清楚hibernate对这个是怎么处理的(hibernate好像是bean和表一对一绑定的)? 标签:sborm
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值