前言

最近工作中经常使用Spring JDBC操作数据库,也断断续续的看了一些源码,便有了写一些总结的想法,希望在能帮助别人的同时,也加深一下自己对Spring JDBC的理解。

Spring JDBC 简介

Spring JDBC 是spring 官方提供的一个持久层框架,对jdbc进行了抽象和封装,消除了重复冗余的jdbc重复性的代码,使操作数据库变的更简单。

但Spring JDBC本身并不是一个orm框架,与hibernate相比,它需要自己操作sql,手动映射字段关系,在保持灵活性的同时,势必造成了开发效率的降低。如果需要使用完整的orm框架操作数据库,可以使用hibernate或者spring Data Jpa。

Spring JDBC不同版本的api会稍有变动,但总体的变化不大,以下测试代码均使用4.3.11.RELEASE。

模板类

Spring JDBC 提供了模板类对数据库简化对数据库的操作,其中JdbcTemplate是最常用的,如果需要使用命名参数可以使用NamedParameterJdbcTemplate,SimpleJdbcTemplate在3.1版本已经标记过时,在我使用的4.3版本中,已经被删除。

JdbcTemplate 入门示例

JdbcTemplate使用很简单,注入一个数据源就可以使用了

public class A001SpringJdbcJdbcTemplateTest {
private JdbcTemplate jdbcTemplate; @Before
public void init() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/blogsrc?useUnicode=true&characterEncoding=UTF-8");
dataSource.setUsername("root");
dataSource.setPassword("zhao");
jdbcTemplate = new JdbcTemplate(dataSource);
} @Test
public void queryTest() {
String sql = "select * from user";
List<Map<String, Object>> users = jdbcTemplate.queryForList(sql);
System.out.println(users);
} }

对于参数赋值,可以采用占位符的方式

  @Test
public void queryByParameterTest() {
String sql = "select * from user where id =?";
List<Map<String, Object>> users = jdbcTemplate.queryForList(sql, 1L);
List<Map<String, Object>> users1 = jdbcTemplate.queryForList(sql, new Object[] {1L});
}

mapper映射

Spring JDBC 通过mapper接口把resultSet对象中的数据映射为java对象,例如上述例子中返回 List<Map<String, Object>>,其实使用的是ColumnMapRowMapper的mapper实现。我们自己可以通过实现RowMapper接口的方式自定义从resultSet到java对象的映射关系。

先创建一个table

CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`password` varchar(255) NOT NULL,
`user_name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

User实体类

public class User implements Serializable {
private Long id;
private String userName;
private String password;
// 省略 getter setter
}

自定义mapper映射

  @Test
public void simpleMapperTest() {
String sql = "select * from user";
List<User> users = jdbcTemplate.query(sql, new RowMapper<User>() {
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getObject("id") == null ? null : rs.getLong("id"));
user.setUserName(rs.getString("user_name"));
user.setPassword(rs.getString("password"));
return user;
}
});
}

调用存储过程

对于数据库数据的更新操作,可以直接调用update接口,如果是存储过程,可以通过execute完成调用。

新建一个简单的存储过程test

DROP PROCEDURE IF EXISTS test;
CREATE PROCEDURE test (IN userId BIGINT ( 20 ),OUT userName VARCHAR ( 200 ) )
BEGIN
SET userName = ( SELECT user_name FROM USER WHERE id = userId );
END;
  @Test
public void proTest() {
String sql = "insert into user (user_name,password) VALUES (?, ?)";
jdbcTemplate.update(sql, "赵孤鸿", "123456"); // 插入数据
String userName = jdbcTemplate.execute(new CallableStatementCreator() {
public CallableStatement createCallableStatement(Connection con) throws SQLException {
String proc = "{call test(?,?)}";
CallableStatement cs = con.prepareCall(proc);
cs.setLong(1, 1L);// 设置输入参数的值 索引从1开始
cs.registerOutParameter(2, Types.VARCHAR);// 设置输出参数的类型
return cs;
}
}, new CallableStatementCallback<String>() {
public String doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException {
cs.execute();
return cs.getString(2);// 返回输出参数
}
});
System.out.println(userName);
}

NamedParameterJdbcTemplate

NamedParameterJdbcTemplate的使用基本上和JdbcTemplate类似,只不过参数的赋值方式由占位符变成了命名参数,命名参数优势在于,如果一个相同的参数出现了多次,只需要进行一次赋值即可。

创建NamedParameterJdbcTemplate对象的两种方式

    // 方式1
namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
// 方式2
namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
  @Test
public void namedParameterJdbcTemplateTest() {
String sql = "select * from user where id =:id";
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put("id", 1L);
List<Map<String, Object>> users = namedParameterJdbcTemplate.queryForList(sql, parameters);
System.out.println(users);
}

batchUpdate

对于大数据量的数据更新,可以采用batchUpdate接口

  @Test
public void batchUpdateTest() {
String sql = "insert into user (user_name,password) VALUES (?, ?)";
List<User> users = Lists.newArrayList();
for (int i = 0; i <= 10; i++) {
User user = new User();
user.setUserName("xiaoming");
user.setPassword("123456");
users.add(user);
}
jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
User user = users.get(i);
int count = 0;
ps.setString(++count, user.getUserName());// 索引从1开始
ps.setString(++count, user.getPassword());
} @Override
public int getBatchSize() {
return users.size();
}
});
}

与spring整合

只需要注入数据源即可

    <!-- 省略dataSource相关配置 -->

	<!-- 配置 Spirng 的 JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置 NamedParameterJdbcTemplate -->
<bean id="namedParameterJdbcTemplate"
class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="dataSource"></constructor-arg>
</bean>

如果是springboot项目,直接使用即可

  @Autowired
private JdbcTemplate jdbcTemplate;

注意事项

关于queryForObject,如果没有查询到,或者查询到多个,都会抛异常,看一下源码

  // 未查询到,或者查询到多个,都会抛异常
public static <T> T requiredSingleResult(Collection<T> results) throws IncorrectResultSizeDataAccessException {
int size = (results != null ? results.size() : 0);
if (size == 0) {
throw new EmptyResultDataAccessException(1);
}
if (results.size() > 1) {
throw new IncorrectResultSizeDataAccessException(1, size);
}
return results.iterator().next();
}

queryForMap最终走的也是queryForObject方法,因此,使用时也要注意

在自定义mapper获取ResultSet的值时,获取基本类型数据会有默认值,解决办法如下

  Long id =  rs.getLong("id");//如果id为null,会默认0
Long id1 = rs.getObject("id") == null ? null : rs.getLong("id");//先判断是否为null

完整的 源码 https://github.com/zhaoguhong/blogsrc

Spring JDBC(一)jdbcTemplate的更多相关文章

  1. 【Spring JDBC】JdbcTemplate(三)

    传统Jdbc API与Spring jdbcTemplate比较 //JDBC API Statement statement = conn.createStatement(); ResultSet ...

  2. JDBC(三)----Spring JDBC(JDBCTemplate)

    ##  Spring  JDBC *  Spring框架对JDBC的简单封装.提供了一个JDBCTemplate对象简化JDBC的开发 1.步骤 1.导入jar包 2.创建JDBCTemplate对象 ...

  3. Spring JDBC JdbcTemplate类示例

    org.springframework.jdbc.core.JdbcTemplate类是JDBC核心包中的中心类.它简化了JDBC的使用,并有助于避免常见的错误. 它执行核心JDBC工作流,留下应用程 ...

  4. Spring JDBC模板类—org.springframework.jdbc.core.JdbcTemplate(转)

    今天看了下Spring的源码——关于JDBC的"薄"封装,Spring 用一个Spring JDBC模板类来封装了繁琐的JDBC操作.下面仔细讲解一下Spring JDBC框架. ...

  5. Spring JDBC 框架使用JdbcTemplate 类的一个实例

    JDBC 框架概述 在使用普通的 JDBC 数据库时,就会很麻烦的写不必要的代码来处理异常,打开和关闭数据库连接等.但 Spring JDBC 框架负责所有的低层细节,从开始打开连接,准备和执行 SQ ...

  6. 【Spring】Spring的数据库开发 - 1、Spring JDBC的配置和Spring JdbcTemplate的解析

    Spring JDBC 文章目录 Spring JDBC Spring JdbcTemplate的解析 Spring JDBC的配置 简单记录-Java EE企业级应用开发教程(Spring+Spri ...

  7. spring jdbc查询 依赖JdbcTemplate这个类模版封装JDBC的操作

    package cn.itcast.spring.jdbc; import java.util.List; import org.springframework.jdbc.core.support.J ...

  8. 【JDBC】使用Spring提供的JDBCTemplate通过Statement向MySql数据库插入千万条数据,耗时4m55s,使用insert语句批量插入方式二

    这回依然是使用 insert批量插入这种方式 insert into emp(name,age,cdate) values ('A' , 20, '2019-10-13 00:00:00'), ('B ...

  9. 【JDBC】使用Spring提供的JDBCTemplate通过PrepareStatement向MySql数据库插入千万条数据,耗时32m47s,速度提升有限

    数据库环境还和原来一样,只是从Statement换成了PrepareStatement,都说PrepareStatement因为预编译比Statement快,但是实际运行真快不了多少. 代码如下: p ...

随机推荐

  1. LeetCode 560. Subarray Sum Equals K (子数组之和等于K)

    Given an array of integers and an integer k, you need to find the total number of continuous subarra ...

  2. Python对于CSV文件的读取与写入

    今天天气"刚刚好"(薛之谦么么哒),无聊的我翻到了一篇关于csv文件读取与写入的帖子,作为测试小白的我一直对python情有独钟,顿时心血来潮,决定小搞他一下,分享给那些需要的小白 ...

  3. 老生常谈:关于undo表空间的使用率

    就在前几天,又有一个客户向我咨询undo表空间使用率的问题. 这让我想起几年前曾经有个省份的案例,客户的实际运维人员是一位刚毕业不久的女孩,几乎不懂Oracle原理,项目经理交给她的任务也是基础运维工 ...

  4. unity客户端基本框架(转载)

    框架概述: 基础系统的框架搭建,其中包括: UI框架(NGUI + MVC) 消息管理(Advanced CSharp Messenger) 网络层框架(Socket + Protobuf ) 表格数 ...

  5. 基于Lua脚本解决实时数据处理流程中的关键问题

    摘要 在处理实时数据的过程中需要缓存的参与,由于在更新实时数据时并发处理的特点,因此在更新实时数据时经常产生新老数据相互覆盖的情况,针对这个情况调查了Redis事务和Lua脚本后,发现Redis事务并 ...

  6. C++获取基类指针所指子类对象的类名

    我们在程序中定义了一个基类,该基类有n个子类,为了方便,我们经常定义一个基类的指针数组,数组中的每一项指向都指向一个子类,那么在程序中我们如何判断这些基类指针是指向哪个子类呢? 关键字 typeid, ...

  7. Akka(35): Http:Server side streaming

    在前面几篇讨论里我们都提到过:Akka-http是一项系统集成工具库.它是以数据交换的形式进行系统集成的.所以,Akka-http的核心功能应该是数据交换的实现了:应该能通过某种公开的数据格式和传输标 ...

  8. 记录下 Markdown 语法

    github上常用markdown语法:Mastering Markdown 目录 0. 目录 1. 锚点 2.标题 3.超链接 3.1.行内式 3.2.自动链接 4.列表 4.1.有序列表 4.2. ...

  9. Mac上配置不同版本的JDK

    Mac上JDK的版本为1.8,编译AOSP时发现需要JDK 1.7.想找一种比较容易切换JDK版本的方式,经过一番Google发现Jenv比较合适. 安装Jenv至少有三种方式: - $ git cl ...

  10. mongoose返回值无法修改

    mongoose 查询方法 find 例:db.collections.find(query,function(err,doc) { 如果var res = doc[0]  是{name:'feife ...