在传统的web开发过程中,进行JDBC编程是十分痛苦的,如下,是我自己写的一个从mysql数据库中取出数据的代码
package com.nenu.software;
import java.sql.*;
/**
* @author: software-liuwang
* @time: 2017/11/18 21:07
* @description : 传统jdbc连接mysql
*/
public class Tradition {
//jdbc驱动
private final static String DRIVER = "com.mysql.jdbc.Driver";
//连接地址
private final static String URL = "jdbc:mysql://localhost:3306/db_test";
//mysql用户名
private final static String USERNAME = "root";
//mysql密码
private final static String PASSWORD = "root";
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
//加载驱动
Class.forName(DRIVER);
//获取Connection
conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
//定义sql语句
String sql = "SELECT * FROM `t_news`";
//创建预处理对象
pstmt = conn.prepareStatement(sql);
//获取结果集
rs = pstmt.executeQuery();
//操作结果集
while(rs.next()) {
System.out.println("id = " + rs.getInt("id"));
System.out.println("name = " + rs.getString("name"));
}
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if(rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
上面的代码片段冗长,由于长时间不用,在写上面的代码段的时候,我发现很多地方我需要参考之前的项目,所以很容易出错。
有人在写项目的时候可能会写一套自己的DB类,简化开发代码,但由于是自己封装的,所以不够通用。而Spring的JDBC提供了一套JDBC框架,用于简化JDBC开发。
Spring通过抽象JDBC访问并提供一致的API来简化JDBC的工作量,我们只需要声明SQL、调用合适的Spring JDBC框架API、处理结果集即可,事务由Spring管理,并将JDBC受查异常转换为Spring一致的非受查异常,从而简化开发。
Spring JDBC抽象框由四部分组成:datasource、support、core、object
support包:提供将JDBC异常转换为DAO非检查异常转换类、一些工具类如JdbcUtils等。
datasource包:提供简化访问JDBC 数据源(javax.sql.DataSource实现)工具类,并提供了一些DataSource简单实现类从而能使从这些DataSource获取的连接能自动得到Spring管理事务支持。
core包:提供JDBC模板类实现及可变部分的回调接口,还提供SimpleJdbcInsert等简单辅助类。
object包:提供关系数据库的对象表示形式,如MappingSqlQuery、SqlUpdate、SqlCall、SqlFunction、StoredProcedure等类,该包是基于core包JDBC模板类实现。
这里说一下core包提供的JDBC模板类,其中JdbcTemplate是core包的核心类,所以其他模板类都是基于它完成的。
JdbcTemplate类帮助我们消除了冗长的代码,帮我们做哪些固定部分,如连接的创建和关闭,我们只做需要做的事情即可
JdbcTemplate类对可变部分采用回调接口方式实现,如ConnectionCallback通过回调接口返回给用户一个连接,从而可以使用该连接做任何事情、StatementCallback通过回调接口返回给用户一个Statement,从而可以使用该Statement做任何事情等等
前边我们已经使用过传统JDBC编程方式,接下来让我们看下Spring JDBC框架提供的更好的解决方案
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.25version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>3.2.18.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
<version>3.1.0.RELEASEversion>
dependency>
在使用JdbcTemplate模板类时必须通过DataSource获取数据库连接,Spring JDBC提供了DriverManagerDataSource实现,它通过包装”DriverManager.getConnection”获取数据库连接
package com.nenu.software.spring;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @author: software-liuwang
* @time: 2017/11/19 10:37
* @description : 在使用JdbcTemplate模板时必须通过DataSource获取数据库连接,Spring JDBC提供了DriverManagerDataSource实现,它
* 通过包装"DriverManager.getConnection"获取数据库连接。
*/
public class TestJdbcTemplate {
//jdbc驱动
private final static String DRIVER = "com.mysql.jdbc.Driver";
//连接地址
private final static String URL = "jdbc:mysql://localhost:3306/db_test";
//mysql用户名
private final static String USERNAME = "root";
//mysql密码
private final static String PASSWORD = "root";
private static JdbcTemplate jdbcTemplate;
/**
* 在此方法中定义了DataSource并使用DataSource对象创建了JdbcTemplate对象
*/
@BeforeClass //junit的注解,在测试方法之前执行,且只执行一次
public static void setUpClass() {
//构建DriverManagerDataSource对象
DriverManagerDataSource dataSource = new DriverManagerDataSource(URL, USERNAME, PASSWORD);
//加载驱动
dataSource.setDriverClassName(DRIVER);
//构建JdbcTemplate
jdbcTemplate = new JdbcTemplate(dataSource);
}
/**
* 先定义SQL,再调用JdbcTemplate的方法执行SQL,最后通过RowCallbackHandler回调处理ResultSet结果集
*/
@Test
public void test() {
//声明SQL
String sql = "SELECT * FROM `t_news`";
jdbcTemplate.query(sql, new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException {
//处理结果集
String name = rs.getString("name");
System.out.println("name = " + name);
}
});
}
}
JdbcTemplate执行流程:首先定义SQL,然后调用JdbcTemplate中的方法执行方法,最后通过RowCallbackHandler回调处理ResultSet结果集。
接下来让我们看一下如何使用JdbcTemplate来实现增删改查
package com.nenu.software.spring;
import org.junit.*;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @author: software-liuwang
* @time: 2017/11/19 11:11
* @description : 使用JdbcTemplate实现增删改查
*/
public class TestJdbcTemplate_1 {
//jdbc驱动
private final static String DRIVER = "com.mysql.jdbc.Driver";
//连接地址
private final static String URL = "jdbc:mysql://localhost:3306/db_test";
//mysql用户名
private final static String USERNAME = "root";
//mysql密码
private final static String PASSWORD = "root";
private static JdbcTemplate jdbcTemplate;
/**
* 在此方法中定义了DataSource并使用DataSource对象创建了JdbcTemplate对象
*/
@BeforeClass
public static void setUpClass() {
//构建DriverManagerDataSource对象
DriverManagerDataSource dataSource = new DriverManagerDataSource(URL, USERNAME, PASSWORD);
//加载驱动
dataSource.setDriverClassName(DRIVER);
//构建JdbcTemplate
jdbcTemplate = new JdbcTemplate(dataSource);
}
@Before
public void createTable() {
String createTableSql = "CREATE TABLE `t_test` (" +
" `id` int(11) NOT NULL AUTO_INCREMENT," +
" `test_name` varchar(50) DEFAULT NULL," +
" PRIMARY KEY (`id`)" +
")";
jdbcTemplate.update(createTableSql);
}
@After
public void dropTable() {
String dropTableSql = "DROP TABLE `t_test`";
jdbcTemplate.execute(dropTableSql);
}
private void insert() {
jdbcTemplate.update("INSERT INTO `t_test`(`test_name`) VALUES ('name1')");
jdbcTemplate.update("INSERT INTO `t_test`(`test_name`) VALUES ('name2')");
//spring3.2.2后,jdbcTemplate.qyeryForInt()方法过时,现在一般使用jdbc.queryForObject
// System.out.println("count after insert = " + jdbcTemplate.queryForInt("SELECT COUNT(*) FROM `t_test`"));
System.out.println("count after insert = " + jdbcTemplate.queryForObject("SELECT COUNT(*) FROM `t_test`", Integer.class));
}
private void delete() {
jdbcTemplate.update("DELETE FROM `t_test` WHERE `test_name` = 'name2'");
System.out.println("count after delete = " + jdbcTemplate.queryForObject("SELECT COUNT(*) FROM `t_test`", Integer.class));
}
private void update() {
jdbcTemplate.update("UPDATE `t_test` SET `test_name` = 'name3' WHERE `test_name` = 'name1'");
System.out.println("count name3 after update = " + jdbcTemplate.queryForObject("SELECT COUNT(*) FROM `t_test`", Integer.class));
}
private void select() {
jdbcTemplate.query("SELECT * FROM `t_test`", new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException {
System.out.print("-----id : " + rs.getInt("id"));
System.out.println(", name : " + rs.getString("test_name"));
}
});
}
@Test
public void test() {
insert();
delete();
update();
select();
}
}
JdbcTemplate主要提供以下五类方法:
execute方法:可以用于执行任何SQL语句,一般用于执行DDL语句;
(
补一波课:
SQL语言分为这么几种:
(1)数据定义语言(DDL):用来建立数据库、数据库对象和定义列的命令。包括:create、alter、drop
(2)数据操纵语言(DML):用来操纵数据库中数据的命令。包括:select、insert、update、delete。
(3)数据控制语言(DCL):用来控制数据库组件的存取许可、权限等的命令。包括:grant、deny、revoke
(4)其他语言元素:如流程控制语言、内嵌函数、批处理语句等。
)
**update方法及batchUpdate方法:**update方法用于执行新增、修改、删除等语句;batchUpdate方法用于执行批处理相关语句;
query方法及queryForXXX方法:用于执行查询相关语句;
需要提及的是queryForXXX方法有些已经过时了,已经改成queryForObject和queryForList方法了
call方法:用于执行存储过程、函数相关语句。
除此之外,JdbcTemplate类还支持很多回调类,比如ConnectionCallback,能够通过回调获取JdbcTemplate提供的Connection,类似的比如StatementCallback,PreparedStatementCallback,这里就不做过多介绍了
除此之外,Spring还提供了另外的两个模板类:
SimpleJdbcTemplate:也是基于JdbcTemplate的类,但利用java5+的可变参数列表和自动装箱和拆箱从而获取更简洁的代码,但是自从Spring3.1开始,JdbcTemplate和NamedParameterJdbcTemplate都提供了SimpleJdbcTemplate的功能,所以Spring3.1之后,SimpleJdbcTemplate就被标记为过时了。
NamedParameterJdbcTemplate:它能够在执行查询时把值绑定到SQL里的命名参数,而不是使用索引参数
举个例子:
package com.nenu.software.spring;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
/**
* @author: software-liuwang
* @time: 2017/11/20 14:10
* @description :
*/
public class TestNamedParameterJdbcTemplate {
//jdbc驱动
private final static String DRIVER = "com.mysql.jdbc.Driver";
//连接地址
private final static String URL = "jdbc:mysql://localhost:3306/db_test";
//mysql用户名
private final static String USERNAME = "root";
//mysql密码
private final static String PASSWORD = "root";
private static JdbcTemplate jdbcTemplate;
/**
* 在此方法中定义了DataSource并使用DataSource对象创建了JdbcTemplate对象
*/
@BeforeClass //junit的注解,在测试方法之前执行,且只执行一次
public static void setUpClass() {
//构建DriverManagerDataSource对象
DriverManagerDataSource dataSource = new DriverManagerDataSource(URL, USERNAME, PASSWORD);
//加载驱动
dataSource.setDriverClassName(DRIVER);
//构建JdbcTemplate
jdbcTemplate = new JdbcTemplate(dataSource);
}
@Test
public void testNamedParameterJdbcTemplate1() {
NamedParameterJdbcTemplate namedParameterJdbcTemplate = null;
namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
// String insertSql = "INSERT INTO `t_test`(`test_name`) VALUES (:name)";
// String selectSql = "SELECT * FROM `t_test` WHERE `test_name` = :name";
// String deleteSql = "DELETE FROM `t_test` WHERE `test_name` = :name";
Map paramMap = new HashMap();
paramMap.put("name", "name5");
// namedParameterJdbcTemplate.update(insertSql, paramMap);
// namedParameterJdbcTemplate.query(selectSql, paramMap, new RowCallbackHandler() {
// public void processRow(ResultSet resultSet) throws SQLException {
// System.out.println(resultSet.getString("test_name"));
// }
// });
// namedParameterJdbcTemplate.update(deleteSql, paramMap);
}
}
NamedParameterJdbcTemplate类为命名参数设值有两种方式:java.util.Map和SqlParameterSource:
(1)java.util.Map:使用Map键数据来对应命名参数,而Map值数据用于设值
(2)SqlParameterSource:默认有MapSqlParameterSource和BeanPropertySqlParameterSource实现;MapSqlParameterSource实现非常简单,只是封装了java.util.Map;而BeanPropertySqlParameterSource封装了一个JavaBean对象,通过JavaBean对象属性来决定命名参数的值
我们先定义一个实体类
package com.nenu.software.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* @author: software-liuwang
* @time: 2017/11/20 14:44
* @description :
*/
@Entity
@Table(name = "t_test")
public class TestEntity {
//id
@Id
private Integer id;
//name
@Column(name = "test_name")
private String testName;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTestName() {
return testName;
}
public void setTestName(String testName) {
this.testName = testName;
}
}
测试类
//BeanPropertySqlParameterSource为从命名参数设值
@Test
public void testNamedParamterJdbcTemplate2() {
NamedParameterJdbcTemplate namedParameterJdbcTemplate = null;
namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
TestEntity testEntity = new TestEntity();
testEntity.setTestName("name666");
String insertSql = "INSERT INTO `t_test`(`test_name`) VALUES(:testName)";
SqlParameterSource paramSource = new BeanPropertySqlParameterSource(testEntity);
namedParameterJdbcTemplate.update(insertSql, paramSource);
}