概要
过度
我们前面基本介绍完了Spring的框架实现思路,现在我们以前面介绍的知识为基础,对常用的一些基于Spring的框架进行分离,目的是从java SE加特殊业务基本框架的角度对我们工作中使用的Spring定制框架进行解读。从而摘掉企业级框架的神秘面纱。
当然,虽然我一直对常用框架有一种读源码、即知其然也知其所以然的态度,但是要明白我们要用的本就是这些工具,我们需要的是这种探究的思想、设计模式的学习以及对疑难杂症的解决思路,而不是从头另搭一套东西出来。所以,我们在接下来的框架学习中力求用Java SE加基本框架的方法进行Spring框架的解读,但是不会再继续深入。
内容简介
本文回顾了jdbc的基本使用方法,并展示了 spring-jdbc
工具的使用方法。下文将以本文的代码为切入点,解析spring-jdbc
的实现逻辑。
前面对Spring框架的解析我们基本都是依照《Spring源码深度解析》一书,从这里开始,我们的阅读操作仍然以此书为一些指引,但是不在完全跟着他的思路走了。
所属环节
spring-jdbc
引入及概览。
上下环节
上文:Spring框架详细解析
下文:无【如果硬要安排一个的话,应该算是数据库相关框架的总结吧】
JDBC
引入
我们在大学针对数据库操作进学习了一种框架:jdbc,这个是Java操作数据库的最基本的框架了。我们在操作数据库都是依靠SQL语句的,JDBC(Java DataBase Connectivity)也是接受SQL入参,并执行链接库、查询操作。
使用示例
我们先看一下基本的jdbc使用方法:
public static void main(String args[]) throws ClassNotFoundException, SQLException {
// 在 com.mysql.cj.jdbc.Driver 类的静态代码块中进行了DriverManager的注册。
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://","","");
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select * from form");
while (resultSet.next()){
String x= resultSet.getString(1);
System.out.println("x="+x);
}
// statement.executeUpdate();
}
基本的jdbc的操作就是这样了,当然还有很多高级操作。比如:使用DataSource
数据库连接池,使用PreparedStatement
提高执行速度等等。
我们先看使用数据库连接池的操作:
public static void main(String args[]) throws ClassNotFoundException, SQLException, PropertyVetoException {
// 在 com.mysql.cj.jdbc.Driver 类的静态代码块中进行了DriverManager的注册。
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setUser("");
ds.setPassword("");
ds.setDriverClass("com.mysql.cj.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql://");
ds.setInitialPoolSize(3);
ds.setMaxPoolSize(10);
ds.setMaxStatements(100);
ds.setAcquireIncrement(2);
Statement statement = ds.getConnection().createStatement();
ResultSet resultSet = statement.executeQuery("select * from form");
while (resultSet.next()){
String x= resultSet.getString(1);
System.out.println("x="+x);
}
}
接下来是使用PreparedStatement
的操作:
public static void main(String args[]) throws ClassNotFoundException, SQLException {
// 在 com.mysql.cj.jdbc.Driver 类的静态代码块中进行了DriverManager的注册。
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://","","");
PreparedStatement statement = connection.prepareStatement("select * from form");
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()){
String x= resultSet.getString(1);
System.out.println("x="+x);
}
}
总结
上面的所有操作其实思路都是相似的:
- 加载JDBC驱动
- 设置数据库相关信息【url、用户名、密码等等】
- 获得
Connection
- 获得
Statement
- 传入sql,获得执行结果
Spring JDBC
引入
上面进行了一些总结,我们很容易发现整体思路都差不多:
- 其中1、2是一劳永逸的,这种设置在项目中不会变化
- 3、4可以根据线程及其他的一些信息(例如事务)进行托管,以高效、合理的完成实现
- 5和业务逻辑密切相关,涉及查询方式及数据转化,无法简化
所以,我们猜测Spring做的事应该分成两类:
- 对 JDBC驱动的加载、数据库的配置进行全局化设置:一次设置,多处复用。
- 接管
Connection
、Statement
的创建工作
我们使用Spring JDBC后只需要关注要执行的sql和结果映射即可。
使用示例
我们用最基本的xml配置来看吧,这个显的清楚一些:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 1、声明数据源对象:C3P0连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 加载jdbc驱动 -->
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
<!-- jdbc连接地址 -->
<property name="jdbcUrl" value="jdbc:mysql://"/>
<!-- 连接数据库的用户名 -->
<property name="user" value=""/>
<!-- 连接数据库的密码 -->
<property name="password" value=""/>
<!-- 数据库的初始化连接数 -->
<property name="initialPoolSize" value="3"/>
<!-- 数据库的最大连接数 -->
<property name="maxPoolSize" value="10"/>
<!-- 数据库最多执行的事务 -->
<property name="maxStatements" value="100"/>
<!-- 连接数量不够时每次的增量 -->
<property name="acquireIncrement" value="2"/>
</bean>
<!-- 创建jdbcTemplate对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="userTemplate" class="UserTemplate" >
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
</beans>
以上是所有的xml配置,其中:
- 创建
DataSource
相关的配置实现了我们说的第一点——对 JDBC驱动的加载、数据库的配置进行全局化设置:一次设置,多处复用 - 在我们的数据库增删改查操作和
DataSource
之间多了一层JdbcTemplate
,猜测应该是这里进行了一些Connector
和Statement
的封装工作。
我们继续看UserTemplate
的Java代码:
public Long saveUser(User user){
String sql = "INSERT INTO learn.form (creator, is_deleted, modifier, business_id, form_name, template_id, creation_code) VALUES (?,?,?,?,?,?,?)";
KeyHolder keyHolder = new GeneratedKeyHolder();
int result = jdbcTemplate.update(
new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection con) throws SQLException
{
PreparedStatement ps = getJdbcTemplate().getDataSource()
.getConnection().prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
ps.setString(1,user.getCreator());
ps.setBoolean(2,user.isDeleted());
ps.setString(3,user.getModifier());
ps.setLong(4,user.getBusinessId());
ps.setString(5,user.getFormName());
ps.setLong(6,user.getTemplateId());
ps.setString(7,user.getCreationCode());
return ps;
}
}, keyHolder);
if (result != 0){
System.out.println("插入数据成功");
return keyHolder.getKey().longValue();
}
return null;
}
public List<User> getByCreator(String creator){
return jdbcTemplate.query("select * from form where creator = '"+creator+"'",new UserRowMapper());
我们很容易发现查询、修改(包括插入、删除)操作都依赖于JdbcTemplate
的对应函数。我们在自己的代码中主要关注:
- SQL语句的构建操作
- 修改操作返回结果的带出设置
- 查询结果到POJO的映射
其中2、3都是各自的特殊属性,1是共通的属性。
总结
我们上面记录了对JdbcTemplate
的使用方法,后面主要关注的是就是其中对Connection
和Statement
的管理。