ref:https://blog.csdn.net/u011054333/article/details/54772491html
先来看看一个JDBC的例子。咱们能够看到为了执行一条SQL语句,咱们须要建立链接,建立语句对象,而后执行SQL,而后操纵结果集获取数据。java
try(Connection connection = DriverManager.getConnection(URL, USERNAME, PASSWORD)){ List<User> users = new ArrayList<>(); try (Statement statement = connection.createStatement()) { try (ResultSet rs = statement.executeQuery("SELECT *FROM user")) { while (rs.next()) { User user = new User(); user.setId(rs.getInt(1)); user.setUsername(rs.getString(2)); user.setPassword(rs.getString(3)); user.setNickname(rs.getString(4)); user.setBirthday(rs.getDate(5)); users.add(user); } } } }
其实这些步骤中有不少步骤都是固定的,Spring JDBC框架将这些操做封装起来, 咱们只须要关注业务逻辑点便可。在Spring JDBC框架中,咱们要作的事情以下:mysql
Spring会帮咱们完成如下事情:程序员
JdbcTemplate是Jdbc框架最重要的类,提供了较为底层的Jdbc操做。其它几个类都是在JdbcTemplate基础上封装了相关功能。spring
要在Gradle项目中使用Spring JDBC框架,添加以下一段。因为Spring JDBC的主要类JdbcTemlate须要一个数据源用来初始化,因此还须要一个数据源的实现。sql
compile group: 'org.springframework', name: 'spring-jdbc', version: '4.3.5.RELEASE' compile group: 'org.apache.commons', name: 'commons-dbcp2', version: '2.1.1'
若是要使用Spring框架的其余功能,可能还须要添加对应的依赖。数据库
首先须要建立一个数据源Bean。为了将配置分离,咱们先新建一个jdbc.properties
文件。apache
jdbc.url=jdbc:mysql://localhost:3306/test jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.username=root jdbc.password=12345678
而后建立一个Spring配置文件jdbc.xml
。这里用到了<context:property-placeholder>
节点来导入其它配置文件。而后用这些属性建立一个数据源Bean,而后再利用数据源Bean来建立一个JdbcTemplate。数组
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg ref="dataSource"/> </bean> <context:property-placeholder location="jdbc.properties"/> </beans>
注册了JdbcTemplate以后,就能够将它注入到任何地方来使用了。首先它可使用execute方法,执行任何SQL语句。这里建立了一个简单的MySQL用户表,只有主键和用户名。安全
jdbcTemplate.execute("CREATE TABLE user(id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(255) UNIQUE)");
它还可使用update方法执行增长、更新和删除操做。
jdbcTemplate.update("INSERT INTO user(name) VALUES(?)", "yitian"); jdbcTemplate.update("INSERT INTO user(name) VALUES(?)", "zhang2"); jdbcTemplate.update("UPDATE user SET name=? WHERE name=?", "zhang3", "zhang2"); jdbcTemplate.update("DELETE FROM user WHERE name=?", "zhang3");
查询操做也很简单,使用queryForObject方法,传入SQL字符串和结果类型便可。
int count = jdbcTemplate.queryForObject("SELECT count(*) FROM user", Integer.class); System.out.println("记录数目是:" + count); String name = jdbcTemplate.queryForObject("SELECT name FROM user WHERE id=1", String.class); System.out.println("姓名是:" + name);
若是要查询整条记录也能够。Spring提供了一个接口RowMapper,只须要实现该接口的mapRow方法,便可将结果集的一条记录转化为一个Java对象,该方法的第二个参数是当前行的行数。下面是一个RowMapper实现。
public class UserRowMapper implements RowMapper<User> { @Override public User mapRow(ResultSet rs, int rowNum) throws SQLException { return new User(rs.getInt(1), rs.getString(2)); } }
User实体类对应于上面建立表时简单的用户表(其余方法已省略)。
public class User { private int id; private String name; }
实现了RowMapper接口以后,咱们就能够查询一条记录并转化为Java对象了。
User user = jdbcTemplate.queryForObject("SELECT id,name FROM user WHERE id=?", new UserRowMapper(), 1); System.out.println(user);
查询多条记录也能够,这时候须要使用query方法。
List<User> users = jdbcTemplate.query("SELECT id,name FROM usr", new UserRowMapper()); System.out.println(users);
还有一个通用方法queryForList,返回一个List,每个元素都是一个Map,在Map中存放着列名和值组成的键值对。
List<Map<String, Object>> results = jdbcTemplate.queryForList("SELECT id,name FROM user"); System.out.println(results);
前面的JdbcTemplate提供了很是方便的JDBC操做封装,可是在绑定参数的时候只能采用通配符?
方式以顺序方式绑定参数。若是SQL语句比较复杂,参数比较多,那么这种方式显得不太方便。所以Spring提供了一个更加方便的类NamedParameterJdbcTemplate,它能够以命名方式绑定SQL语句参数。NamedParameterJdbcTemplate在内部使用一个JdbcTemplate,你也能够调用getJdbcOperations方法获取底层的JdbcTemplate对象,而后用前面的方法进行基本操做。
建立NamedParameterJdbcTemplate和JdbcTemplate相同,只须要传入一个数据源便可。
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"> <constructor-arg ref="dataSource"/> </bean>
NamedParameterJdbcTemplate和JdbcTemplate的大部分操做相同,这里仅介绍绑定命名参数的部分。首先,SQL语句必须使用:参数名称
的形式做为参数。而后,咱们建立一个MapSqlParameterSource对象,它的内部使用了一个Map保存的命名参数的名称和值。而后咱们使用它的addValue方法传递须要的命名参数的名称和值,这个方法还能够接受第三个参数指定参数类型,这个类型以java.sql.Types
的一些公共字段的形式给出。最后,将MapSqlParameterSource传递给相应的方法执行便可。
String sql = "SELECT id,name FROM user WHERE name=:name AND id<:user_id"; MapSqlParameterSource namedParameters = new MapSqlParameterSource(); namedParameters.addValue("name", "test"); namedParameters.addValue("user_id", 100, Types.INTEGER); User user = namedParameterJdbcTemplate.queryForObject(sql, namedParameters, new UserRowMapper()); System.out.println(user);
若是不想建立MapSqlParameterSource对象,还能够直接使用一个Map传递命名参数的名称和值。
Map<String, Object> map = new HashMap<>(); map.put("user_id", 100); map.put("name", "test"); List<User> users = namedParameterJdbcTemplate.query(sql, map, new UserRowMapper()); System.out.println(users);
上面讨论的MapSqlParameterSource
实际上实现了SqlParameterSource
接口,上面的几个方法签名也是接受SqlParameterSource
接口。这个接口表示用来传递命名参数和值的集合。除了MapSqlParameterSource
以外,还有另一个经常使用的实现,BeanPropertySqlParameterSource
,这个类接受一个Java Bean对象,而后使用Bean的属性名和值做为命名参数的名称和值。这一点须要注意。
User bean = new User(100, "test"); SqlParameterSource parameterSource = new BeanPropertySqlParameterSource(bean); users = namedParameterJdbcTemplate.query(sql, parameterSource, new UserRowMapper()); System.out.println(users);
前面所说的JdbcTemplate封装了一些功能,让咱们方便的使用JDBC。Spring还提供了几个更高级、功能更具体的SimpleJdbc类。这些类会读取JDBC的元数据Metadata,使用起来更加方便。
SimpleJdbcInsert类用来插入数据。简单的使用方法以下。SimpleJdbcInsert须要一个数据源来建立,withTableName方法指定要插入的表名,usingGeneratedKeyColumns指定设置了主键自增的列名。其余使用方法和前面所说的类相似。executeAndReturnKey这个方法很特别,它会将数据插入数据库并返回该条记录对应的自增键。有时候咱们可能但愿使用自增主键来插入一条数据,因为主键是数据库自动生成的,咱们必须再次查询数据库才能得到主键。这种状况下使用executeAndReturnKey很是方便。注意这个方法返回的是java.lang.Number
类型,能够调用其XXXvalue
方法转换成各类数值。
SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(dataSource) .withTableName("user") .usingGeneratedKeyColumns("id"); User user = new User(); user.setName("test"); Map<String, Object> params = new HashMap<>(); params.put("names", user.getName()); int id = simpleJdbcInsert.executeAndReturnKey(params).intValue(); System.out.println("simpleJdbcInsert" + user);
SimpleJdbcCall类用来调用存储过程的。使用方法相似。这里就直接给出Spring官方文档的示例代码了。
MySQL存储过程。 CREATE FUNCTION get_actor_name (in_id INTEGER) RETURNS VARCHAR(200) READS SQL DATA BEGIN DECLARE out_name VARCHAR(200); SELECT concat(first_name, ' ', last_name) INTO out_name FROM t_actor where id = in_id; RETURN out_name; END; SimpleJdbcCall调用存储过程。 public class JdbcActorDao implements ActorDao { private JdbcTemplate jdbcTemplate; private SimpleJdbcCall funcGetActorName; public void setDataSource(DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.setResultsMapCaseInsensitive(true); this.funcGetActorName = new SimpleJdbcCall(jdbcTemplate) .withFunctionName("get_actor_name"); } public String getActorName(Long id) { SqlParameterSource in = new MapSqlParameterSource() .addValue("in_id", id); String name = funcGetActorName.executeFunction(String.class, in); return name; } // ... additional methods } 若是要从存储过程获取记录的话,能够这样。如下是一个MySQL存储过程。 CREATE PROCEDURE read_all_actors() BEGIN SELECT a.id, a.first_name, a.last_name, a.birth_date FROM t_actor a; END; 相对应的Java代码。 public class JdbcActorDao implements ActorDao { private SimpleJdbcCall procReadAllActors; public void setDataSource(DataSource dataSource) { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.setResultsMapCaseInsensitive(true); this.procReadAllActors = new SimpleJdbcCall(jdbcTemplate) .withProcedureName("read_all_actors") .returningResultSet("actors", BeanPropertyRowMapper.newInstance(Actor.class)); } public List getActorsList() { Map m = procReadAllActors.execute(new HashMap<String, Object>(0)); return (List) m.get("actors"); } // ... additional methods }
org.springframework.jdbc.object
包下提供了一组类,让咱们用更加面向对象的方式来操做数据库。咱们能够将SQL查询转化为一组业务对象,也能够方便的进行查询、更新和执行存储过程的操做。
MappingSqlQuery是一个抽象类,继承自SQLQuery。咱们在使用这个类的时候须要建立一个自定义类,继承自MappingSqlQuery,而后在其构造方法中初始化一个查询字符串,并在这里设置查询参数;而后须要实现该类的mapRow方法,将结果集的行转化为实体类对象。下面是一个例子。构造方法中定义的查询字符串会被建立为PreparedStatement,所以能够在查询字符串中使用占位符?
。对于每一个出现的占位符,咱们都必须调用declareParameter方法声明参数,该方法接受一个SqlParameter对象,该对象须要参数名和类型两个参数。最后须要调用compile方法编译和准备查询。该类是线程安全的,所以能够安全的在多个线程之间共享对象。
public class UserMappingSqlQuery extends MappingSqlQuery<User> { public UserMappingSqlQuery(DataSource ds) { super(ds, "SELECT id,name FROM user WHERE id=:id"); super.declareParameter(new SqlParameter("id", Types.INTEGER)); compile(); } @Override protected User mapRow(ResultSet rs, int rowNum) throws SQLException { return new User(rs.getInt(1), rs.getString(2)); } }
而后咱们建立一个对象,调用findObject方法并传入查询参数,便可得到结果对象。
@Test public void testMappingSqlQuery() { MappingSqlQuery<User> mappingSqlQuery = new UserMappingSqlQuery(dataSource); User user = mappingSqlQuery.findObject(1); logger.debug(user); }
若是查询要返回一组记录并传递多个查询参数。须要调用相应的execute方法。一下是另外一个MappingSqlQuery,以及其测试代码。
public class UsersMappingSqlQuery extends MappingSqlQuery<User> { public UsersMappingSqlQuery(DataSource ds) { super(ds, "SELECT id,name FROM user WHERE id<? AND name LIKE ?"); super.declareParameter(new SqlParameter("id", Types.INTEGER)); super.declareParameter(new SqlParameter("name", Types.VARCHAR)); compile(); } @Override protected User mapRow(ResultSet rs, int rowNum) throws SQLException { return new User(rs.getInt(1), rs.getString(2)); } }
//获取多个对象 mappingSqlQuery = new UsersMappingSqlQuery(dataSource); List<User> users = mappingSqlQuery.execute(100, "test"); logger.debug(users);
这个类的使用方法和SqlQuery相似,可是因为它是一个具体类,所以不须要定义子类便可使用。下面是它的简单使用方法。为了更新具体的数据(例如一个Java Bean对象),你也能够继承该类,并提供本身的更新方法,就和上面同样。
@Test public void testSqlUpdate() { SqlUpdate sqlUpdate = new SqlUpdate(dataSource, "INSERT INTO user(name) VALUES(?)"); sqlUpdate.declareParameter(new SqlParameter("name", Types.VARCHAR)); sqlUpdate.compile(); sqlUpdate.update("wang5"); List<User> users = jdbcTemplate.query("SELECT id,name FROM user", new UserRowMapper()); logger.debug(users); }
StoredProcedure是关系数据库中存储过程概念的抽象类,提供了一组方便的受保护方法。所以在使用该类的时候须要咱们建立一个子类,继承该类。在使用这个类的时候咱们须要使用setSql方法设置数据库中存储过程的名称。在传递参数的时候,使用SqlParameter传递IN参数,使用SqlOutParameter传递OUT参数,使用SqlInOutParameter传递INOUT参数。
如下是Spring官方文档的一个例子。
class GetSysdateProcedure extends StoredProcedure {
private static final String SQL = "sysdate"; public GetSysdateProcedure(DataSource dataSource) { setDataSource(dataSource); setFunction(true); setSql(SQL); declareParameter(new SqlOutParameter("date", Types.DATE)); compile(); } public Date execute() { // the 'sysdate' sproc has no input parameters, so an empty Map is supplied... Map<String, Object> results = execute(new HashMap<String, Object>()); Date sysdate = (Date) results.get("date"); return sysdate; } }
通常状况下Spring能够自行决定SQL参数的类型,可是有时候或者说最好由咱们提供准确的SQL参数信息。
JdbcTemplate
的不少查询和更新方法包含一个额外的参数,一个int数组,该数组应该是java.sql.Types
指定的一些常量,代表SQL参数的类型。SqlParameterValue
来设置参数的值,在建立该对象的时候提供参数的值和类型。SqlParameterSource
类(BeanPropertySqlParameterSource
或MapSqlParameterSource
)来指定命名参数和其类型。咱们在学习JDBC的时候,基本上都是从DriverManager类建立一个数据库链接。在实际环境中,咱们应该使用数据源(DataSource)来建立数据库链接。数据源将建立数据库的职责和应用代码分离,数据源能够交给数据库管理员来设置,程序员只须要获取数据源对象,而后开发相关代码。
在上面的例子中咱们使用的是Apache的commons-dbcp2
数据源,Spring本身也实现了几个数据源方便咱们开发和测试。
DriverManagerDataSource
是一个简单的数据源,每次请求都会返回一个新的数据库链接。它使用数据库驱动来建立数据源,就像咱们使用DriverManager
那样。这是一个简单的测试类,能够帮助咱们在不借助任何Java EE容器的状况下获取数据源。可是因为使用commons-dbcp2
这样的成熟数据源也很容易,因此其实咱们只须要使用commons-dbcp2
便可。
SingleConnectionDataSource
也是一个数据源,它包装了一个单独的数据库链接,在每次请求都会返回同一个数据库链接对象。和DriverManagerDataSource
相比它更轻量,由于没有建立额外数据库链接的开销。
在建立数据源的时候咱们能够在Spring配置文件中设置数据源的初始化脚本。
<jdbc:initialize-database data-source="dataSource"> <jdbc:script location="classpath:com/foo/sql/db-schema.sql"/> <jdbc:script location="classpath:com/foo/sql/db-test-data.sql"/> </jdbc:initialize-database>
有时候咱们但愿在初始化数据的时候删除上一次的测试数据。可是若是数据库不支持相似DROP TABLE IF EXISTS
这样的语法,那么咱们就必须在初始化脚本中添加一些DROP
语句。这些删除语句可能会失败(若是没有测试数据的状况下执行删除),这时候就能够忽略删除失败。当初始化脚本出现错误的时候就会抛出异常,可是若是设置了忽略删除失败,Spring就会直接忽略这些失败而不抛出异常。ignore-failures属性还能够取另外两个值NONE
和ALL
,分别表示不忽略失败和忽略全部失败。
<jdbc:initialize-database data-source="dataSource" ignore-failures="DROPS"> <jdbc:script location="..."/> </jdbc:initialize-database>
咱们还能够设置初始化脚本的间隔符。
<jdbc:initialize-database data-source="dataSource" separator="@@"> <jdbc:script location="classpath:com/foo/sql/db-schema.sql" separator=";"/> <jdbc:script location="classpath:com/foo/sql/db-test-data-1.sql"/> <jdbc:script location="classpath:com/foo/sql/db-test-data-2.sql"/> </jdbc:initialize-database>
org.springframework.jdbc.datasource.DataSourceUtils
,这是一个方便的工具类,包含了一组和数据源相关的工具方法。
org.springframework.jdbc.support.JdbcUtils
类提供了一些方法来操做JDBC,在Spring内部使用,也能够用于本身的JDBC操做。
还有几个工具类主要由Spring内部使用,这里就不列举了。
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#jdbc
项目代码在Csdn代码库,有兴趣的同窗能够看看。