JdbcTemplate彻底学习

概述

Spring JDBC抽象框架core包提供了JDBC模板类,其中JdbcTemplate是core包的核心类,因此其余模板类都是基于它封装完成的,JDBC模板类是第一种工做模式。java

 

       JdbcTemplate类经过模板设计模式帮助咱们消除了冗长的代码,只作须要作的事情(便可变部分),而且帮咱们作哪些固定部分,如链接的建立及关闭。sql

 

       JdbcTemplate类对可变部分采用回调接口方式实现,如ConnectionCallback经过回调接口返回给用户一个链接,从而可使用该链接作任何事情、StatementCallback经过回调接口返回给用户一个Statement,从而可使用该Statement作任何事情等等,还有其余一些回调接口数据库

 

Spring除了提供JdbcTemplate核心类,还提供了基于JdbcTemplate实现的NamedParameterJdbcTemplate类用于支持命名参数绑定、 SimpleJdbcTemplate类用于支持Java5+的可变参数及自动装箱拆箱等特性。设计模式

 

JdbcTemplate类支持的回调类:数组

  • 预编译语句及存储过程建立回调:用于根据JdbcTemplate提供的链接建立相应的语句;

PreparedStatementCreator:经过回调获取JdbcTemplate提供的Connection,由用户使用该Conncetion建立相关的PreparedStatement;app

CallableStatementCreator:经过回调获取JdbcTemplate提供的Connection,由用户使用该Conncetion建立相关的CallableStatement;框架

  • 预编译语句设值回调:用于给预编译语句相应参数设值;

         PreparedStatementSetter:经过回调获取JdbcTemplate提供的PreparedStatement,由用户来对相应的预编译语句相应参数设值;ide

         BatchPreparedStatementSetter:;相似于PreparedStatementSetter,但用于批处理,须要指定批处理大小;ui

  • 自定义功能回调:提供给用户一个扩展点,用户能够在指定类型的扩展点执行任何数量须要的操做;

         ConnectionCallback:经过回调获取JdbcTemplate提供的Connection,用户可在该Connection执行任何数量的操做;spa

         StatementCallback:经过回调获取JdbcTemplate提供的Statement,用户能够在该Statement执行任何数量的操做;

         PreparedStatementCallback:经过回调获取JdbcTemplate提供的PreparedStatement,用户能够在该PreparedStatement执行任何数量的操做;

         CallableStatementCallback:经过回调获取JdbcTemplate提供的CallableStatement,用户能够在该CallableStatement执行任何数量的操做;

  • 结果集处理回调:经过回调处理ResultSet或将ResultSet转换为须要的形式;

         RowMapper:用于将结果集每行数据转换为须要的类型,用户需实现方法mapRow(ResultSet rs, int rowNum)来完成将每行数据转换为相应的类型。

         RowCallbackHandler:用于处理ResultSet的每一行结果,用户需实现方法processRow(ResultSet rs)来完成处理,在该回调方法中无需执行rs.next(),该操做由JdbcTemplate来执行,用户只需按行获取数据而后处理便可。

         ResultSetExtractor:用于结果集数据提取,用户需实现方法extractData(ResultSet rs)来处理结果集,用户必须处理整个结果集;

下面详细讲解jdbcTmeplate的CRUD操做:

(一)增长、删除、修改操做:

1)增长、更新、删除(一条sql语句)(sql固定,不须要参数):

    (a) int update(final String sql)

      其中sql参数为须要传入的插入sql语句。

    (b)int update(PreparedStatementCreator psc)

 
    public void test() { jdbcTemplate.update(new PreparedStatementCreator() { @Override public PreparedStatement createPreparedStatement(Connection conn) throws SQLException { return conn.prepareStatement("insert into test(name) values('name1')"); } }); }
 

     (c)若是须要返回新插入数据的主键,采用以下方法(使用KeyHolder keyholder=new GeneratedKeyHolder();得到主键,jdbcTemplate和NamedParameterJdbcTemplate均可以经过此方法得到主键):

       int update(PreparedStatementCreator psc, final KeyHolder generatedKeyHolder)

 
public void test() { KeyHolder keyHolder = new GeneratedKeyHolder(); jdbcTemplate.update(new PreparedStatementCreator() { @Override public PreparedStatement createPreparedStatement(Connection conn) throws SQLException { return conn.prepareStatement("insert into test(name) values('name1')"); } },keyHolder); int i = keyHolder.getKey().intValue();//这就是刚插入的数据的主键 }
 

2)增长、更新、删除(一条sql语句)(sql须要注入参数填充‘?’):

  (a)int update(String sql, PreparedStatementSetter pss)

 
public void test() { String sql = "insert into test(name) values (?)"; //返回的是更新的行数 int count = jdbcTemplate.update(sql, new PreparedStatementSetter(){ @Override public void setValues(PreparedStatement pstmt) throws SQLException { pstmt.setObject(1, "name4"); } }); }
 

      (b)int update(String sql, Object[] args, int[] argTypes)

其中参数含义:   sql:预处理sql语句; args:sql须要注入的参数; argTypes:须要注入的sql参数的JDBC类型(java.sql.Types中来获取类型的常量);

 
    public void test() { String sql = "insert into test(name,age,create_date) values (?,?,?)"; Date now = new Date(System.currentTimeMillis()); //返回的是更新的行数 int count = jdbcTemplate.update(sql, new Object[]{"小明",14,now}, new int[]{Types.VARCHAR,Types.INTEGER,Types.DATE}); }
 

     (c)int update(String sql, Object... args)

其实内部仍是调用方法a实现的,JdbcTemplate提供这种更简单的方式“update(String sql, Object... args)”来实现设值,因此只要当使用该种方式不知足需求时才应使用PreparedStatementSetter(上面方法a)。

 
public void test() { String sql = "insert into test(name,age,create_date) values (?,?,?)"; Date now = new Date(System.currentTimeMillis()); //返回的是更新的行数 int count = jdbcTemplate.update(sql, "小明", 14, now); }
 
 
public void test() { String sql = "insert into test(name,age,create_date) values (?,?,?)"; Date now = new Date(System.currentTimeMillis()); //返回的是更新的行数 int count = jdbcTemplate.update(sql, new Object[]{"小明",14,now}); }
 

这两种实际上调用的都是该方法,因而可知Object...args实际上就是可变的数组,而数组长度是固定的,必须先定义一个数组,而Object...args在传递时参数能够任意,因此也能够传递一个固定的Object数组。

    (d)int update(PreparedStatementCreator psc)

使用该方法能够本身使用原始jdbc方式给预编译sql注入参数,来进行增长、删除、更新操做:

 
public void test(final Customer customer) {//参数也是局部变量,也必须用final修饰,内部类中才能访问(全局变量不用) //方法局部必须是final的,匿名内部类中才能引用 final String sql = "insert into test(name,age,create_date) values (?,?,?)"; Date now = new Date(System.currentTimeMillis()); //返回的是更新的行数 int count = jdbcTemplate.update(new PreparedStatementCreator() { @Override public PreparedStatement createPreparedStatement(Connection conn) throws SQLException { PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, customer.getName()); ps.setInt(2, customer.getAge()); ps.setDate(3, customer.getCreateDate()); return ps; } }); }
 

若是须要返回插入的主键,只能用此方法,增长KeyHolder参数:

 
public void test(final Customer customer) {//参数也是局部变量,也必须用final修饰,内部类中才能访问(全局变量不用) KeyHolder keyHolder = new GeneratedKeyHolder(); //方法局部必须是final的,匿名内部类中才能引用 final String sql = "insert into test(name,age,create_date) values (?,?,?)"; Date now = new Date(System.currentTimeMillis()); //返回的是更新的行数 int count = jdbcTemplate.update(new PreparedStatementCreator() { @Override public PreparedStatement createPreparedStatement(Connection conn) throws SQLException { PreparedStatement ps = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS); //有的数据库版本不同,须要添加第二个参数,否则会报错;
          ps.setString(1, customer.getName());
          ps.setInt(2, customer.getAge());
          ps.setDate(3, customer.getCreateDate());
          return ps;
        }
    },keyHolder);
    
    int i = keyHolder.getKey().intValue();//这就是刚插入的数据的主键 }
 

3)批量增长、删除、更新数据(多条sql语句)

  (a)批量执行多条sql(固定的sql,不须要注入参数,可是sql格式不固定)

      int[] batchUpdate(final String[] sql)

参数是一个String数组,存放多条sql语句;返回值是int数组,即每条sql更新影响的行数。

  (b)批量执行多条sql(预处理sql,须要注入参数)

    int[] batchUpdate(String sql, final BatchPreparedStatementSetter pss)

参数sql:一条预处理sql(若是是批量处理预处理sql,那么sql的格式就是固定的,只填充参数而已);第二个参数就是回调类,前面有统一介绍回调类。

举两个例子,一个更新,一个插入:

 
批量插入:
public void test(final List<Customer> customer) {//参数也是局部变量,也必须用final修饰,内部类中才能访问(全局变量不用) String sql = "insert into test(name,age,create_date) values (?,?,?)"; //返回的是更新的行数 int[] count = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { //注入参数值 ps.setString(1, customer.get(i).getName()); ps.setInt(2, customer.get(i).getAge()); ps.setDate(3, customer.get(i).getCreateDate()); } @Override public int getBatchSize() { //批量执行的数量 return customer.size(); } }); }
 
 
批量更新:
public void test(final List<Customer> customer) {//参数也是局部变量,也必须用final修饰,内部类中才能访问(全局变量不用) String sql = "update test set name = ?,age = ? where id = ?"; //返回的是更新的行数 int[] count = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { //注入参数值 ps.setString(1, customer.get(i).getName()); ps.setInt(2, customer.get(i).getAge()); ps.setInt(3, customer.get(i).getId()); } @Override public int getBatchSize() { //批量执行的数量 return customer.size(); } }); }
 

  (c)批量处理多条预处理sql语句还有下面几种简单方法(参数和前面相似,这里就不详解):

         int[] batchUpdate(String sql, List<Object[]> batchArgs);

         int[] batchUpdate(String sql, List<Object[]> batchArgs, int[] argTypes);

前面讲了增长、删除、更新操做,这节讲一下查询。

查询操做:

(一)查询一个值(不须要注入参数)

queryForObject(String sql, Class<T> requiredType);

注意:参数requiredType只能是String,Integer这种类型,不能是自定义的实体类型,只能返回一个值,不能映射对象(映射对象下面会说);

  sql:预处理sql;requiredType:查询单列结果的类型;

public void test() { String sql = "select count(*) from test"; int count = jdbcTemplate.queryForObject(sql, Integer.class); }

(二)查询一个值(使用预处理sql,须要注入参数)

queryForObject(String sql, Object[] args, Class<T> requiredType);

public void test(Integer id) { String sql = "select name from test where id = ?"; String name = jdbcTemplate.queryForObject(sql, new Object[]{id}, String.class); }

还有以下方式:queryForObject(String sql, Object[] args, int[] argTypes, Class<T> requiredType);

(三)查询单行记录,转换成一个对象(固定sql,不须要参数)

<T> T queryForObject(String sql, RowMapper<T> rowMapper)

public void test() { String sql = "select name,age from test where id = 10"; Customer customer = jdbcTemplate.queryForObject(sql, new RowMapper<Customer>() { @Override public Customer mapRow(ResultSet rs, int i) throws SQLException { Customer c = new Customer(); c.setName(rs.getString("name")); c.setAge(rs.getInt("age")); return c; } }); }

(四)查询单行记录,转换成一个对象(预处理sql,须要注入参数)

<T> T queryForObject(String sql, Object[] args, RowMapper<T> rowMapper)

public void test(Integer id) {//参数也是局部变量,也必须用final修饰,内部类中才能访问(全局变量不用) String sql = "select name,age from test where id = ?"; Customer customer = jdbcTemplate.queryForObject(sql, new Object[]{id}, new RowMapper<Customer>() { @Override public Customer mapRow(ResultSet rs, int paramInt) throws SQLException { Customer c = new Customer(); c.setName(rs.getString("name")); c.setAge(rs.getInt("age")); return c; } }); }

也可使用以下方式:(1)<T> T queryForObject(String sql, Object[] args, int[] argTypes, RowMapper<T> rowMapper);

          (2)<T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args);

(五)查询数据库中一列多行数据,即查询数据库中单列数据存入一个list中,方式以下:(固定sql,没参数)

  (a)List<Map<String, Object>> queryForList(String sql)

           这个方法封装成map放入list中,key:列名(Oracle数据库sql执行结果列名默认为大写,须要小写用as取别名,别名用双引号)  value:列的值

public void test() {//若是Oracle用这个sql查询,返回的列名就是NAME(大写的),对应Map里面的key就是NAME String sql = "select name from test where id > 0"; //若是用这个sql查询,返回的列名就是name(小写的),对应Map里面的key就是name String sql2 = "select name as \"name\" from test where id > 0"; List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);//这里用的是第一个sql }

  (b)<T> List<T> queryForList(String sql, Class<T> elementType)

           这个方法就是直接将单类型数据存入List中。

      注意:这个T虽然是泛型,可是只支持Integer.class String.class 这种单数据类型的,本身定义的Bean不支持。(因此用来查询单列数据)

public void test() {  String sql = "select name from test where id > 0"; List<String> list = jdbcTemplate.queryForList(sql, String.class); }

(六)查询数据库中一列多行数据,即查询数据库中单列数据存入一个list中,方式以下:(预处理sql,须要注入参数)

  (a)<T> List<T> queryForList(String sql, Object[] args, Class<T> elementType)

    注意:这个T虽然是泛型,可是只支持Integer.class String.class 这种单数据类型的,本身定义的Bean不支持。(因此用来查询单列数据,同前面一个意思,后面再也不解释)

public void test(Integer id) {  String sql = "select name from test where id > ?"; List<String> list = jdbcTemplate.queryForList(sql, new Object[]{id}, String.class); }

  还有以下方式实现:(1)<T> List<T> queryForList(String sql, Object[] args, int[] argTypes, Class<T> elementType);

              (2)<T> List<T> queryForList(String sql, Class<T> elementType, Object... args);

  (b)List<Map<String, Object>> queryForList(String sql, Object... args)

  封装成map存入List,和以前同样,要注意Oracle数据库返回的列名默认是大写的,若是须要,用别名变小写。

public void test(Integer id) {  String sql = "select name from test where id > ?"; List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, new Object[]{id}); }

  还有一种方式实现:List<Map<String, Object>> queryForList(String sql, Object[] args, int[] argTypes);

(七)查询多条数据(固定sql,没有参数)

  (a)<T> List<T> query(String sql, RowMapper<T> rowMapper) 

    每条数据映射为java对象,放入List中。

public void test() { String sql = "select name,age from test where id > 10"; List<Customer> list = jdbcTemplate.query(sql, new RowMapper<Customer>() { @Override public Customer mapRow(ResultSet rs, int rowNum) throws SQLException { //这里必须new对象,不能在方法外new,而后用同一个,由于是一个List,查询出来多个对象映射, //必须保证每一次调用都使用一个新的。 //若是不new,而是使用同一个对象,会致使存入到List中的都是同样的对象(都是最后一个对象)。 Customer customer = new Customer(); customer.setName(rs.getString("name")); customer.setAge(rs.getInt("age")); return customer; } }); }

  该方法也能够把每一行数据转换为自定义key-value的map对象放入list中,以下:(因此说使用回调类比简单方法更强大,里面逻辑本身按需求写)

public void test() { String sql = "select name,age from test where id > 10"; List<Map<Integer,Object>> list = jdbcTemplate.query(sql, new RowMapper<Map<Integer,Object>>() { @Override public Map<Integer,Object> mapRow(ResultSet rs, int rowNum) throws SQLException { Map<Integer, Object> map = new HashMap<Integer, Object>(); map.put(rowNum, rs.getString("name")); map.put(rowNum, rs.getInt("age")); return map; } }); }

map中的key,value类型根据须要自定义,很是方便。像这种实现RowMapper<T>接口的匿名类,T能够为Map,也能够为自定义的对象类型,如上两种,根据须要选择。

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

   注意:若是使用RowCallbackHandler 回调类,这个方法是没有返回值的,而是在回调类中将结果放入预先定义的List中,用法以下:

public void test() { String sql = "select name,age from test where id > 10"; //局部变量,必须用final修饰,内部类中才能访问(全局变量不用) final List<Customer> list = new ArrayList<Customer>(); jdbcTemplate.query(sql, new RowCallbackHandler() { @Override public void processRow(ResultSet rs) throws SQLException { Customer customer = new Customer(); customer.setName(rs.getString("name")); customer.setAge(rs.getInt("age")); list.add(customer); } }); }

固然,这种方式也能够转换为map,存入list中,和上面a方式同样,这里就不详解了。

  (c)<T> T query(final String sql, final ResultSetExtractor<T> rse)

  ResultSetExtractor使用回调方法extractData(ResultSet rs)提供给用户整个结果集,让用户决定如何处理该结果集。

public void test() { String sql = "select name,age from test where id > 10"; List<Customer> list = jdbcTemplate.query(sql, new ResultSetExtractor<List<Customer>>() { @Override public List<Customer> extractData(ResultSet rs) throws SQLException, DataAccessException { List<Customer> result = new ArrayList<Customer>(); while(rs.next()) { Customer customer = new Customer(); customer.setName(rs.getString("name")); customer.setAge(rs.getInt("age")); result.add(customer); } return result; } }); }

一样也能够转换为map对象放入list中,以下:

public void test() { String sql = "select name,age from test where id > 10"; List<Map<String, Integer>> list = jdbcTemplate.query(sql, new ResultSetExtractor<List<Map<String, Integer>>>() { @Override public List<Map<String, Integer>> extractData(ResultSet rs) throws SQLException, DataAccessException { List<Map<String, Integer>> result = new ArrayList<Map<String, Integer>>(); while(rs.next()) { Map<String, Integer> map = new HashMap<String, Integer>(); map.put(rs.getString("name"), rs.getInt("age")); result.add(map); } return result; } }); }

   (d)<T> List<T> query(PreparedStatementCreator psc, RowMapper<T> rowMapper)

public void test() {//局部变量,必须用final修饰,内部类中才能访问(全局变量不用) final String sql = "select name,age from test where id > 10"; List<Customer> list = jdbcTemplate.query(new PreparedStatementCreator() { @Override public PreparedStatement createPreparedStatement(Connection conn) throws SQLException { PreparedStatement ps = conn.prepareStatement(sql);
         //若是sql是预处理的,须要传入参数,能够在这里写jdbc代码传入,后面就不列举这种方法了 return ps; } }, new RowMapper<Customer>() { @Override public Customer mapRow(ResultSet rs, int rowNum) throws SQLException { Customer customer = new Customer(); customer.setAge(rs.getInt("age")); customer.setName(rs.getString("name")); return customer; } }); }

能够将RowMapper换成ResultSetExtractor或者RowCallbackHandler回调类,和前面同样,所以还有下面两种方法:

  (1)void query(PreparedStatementCreator psc, RowCallbackHandler rch);

  (2)<T> T query(PreparedStatementCreator psc, ResultSetExtractor<T> rse);

(八)查询多条数据(预处理sql,须要传入参数)

  (a)<T> List<T> query(String sql, PreparedStatementSetter pss, RowMapper<T> rowMapper)

public void test(final Integer id) {//参数也是局部变量,也必须用final修饰,内部类中才能访问(全局变量不用) String sql = "select name,age from test where id > ?"; List<Customer> list = jdbcTemplate.query(sql, new PreparedStatementSetter() { @Override public void setValues(PreparedStatement ps) throws SQLException { ps.setInt(1, id); } }, new RowMapper<Customer>() { @Override public Customer mapRow(ResultSet rs, int rowNum) throws SQLException { Customer customer = new Customer(); customer.setName(rs.getString("name")); customer.setAge(rs.getInt("age")); return customer; } }); }

用RowMapper回调类还有三种方法:

    (1)<T> List<T> query(String sql, Object[] args, int[] argTypes, RowMapper<T> rowMapper);

    (2)<T> List<T> query(String sql, Object[] args, RowMapper<T> rowMapper);

    (3)<T> List<T> query(String sql, RowMapper<T> rowMapper, Object... args);

而若是把回调类换成ResultSetExtractor或者RowCallbackHandler回调类,又有八种方法(像上面同样各有四种),这里就不举出来了,和前面用法一致。

使用Spring的JdbcTemplate或者NamedParameterJdbcTemplate查询数据库,获取结果,数据库表字段和实体类自动对应,可使用BeanPropertyRowMapper

 

注意:

  自动绑定,须要列名称和Java实体类名字一致,如:属性名 “userName” 能够匹配数据库中的列字段(这里说的列字段是sql执行结果的列名,也就是若是有别名就用别名,(Oracle默认列名大写)) "USERNAME" 或 “user_name”。这样,咱们就不须要一个个手动绑定了,大大提升了开发效率。

下面讲解BeanPropertyRowMapper用法:

BeanPropertyRowMapper<T>     implements RowMapper<T>这个类是实现了RowMapper接口的,因此以前讲的查询中,能用到RowMapper回调类的均可以用BeanPropertyRowMapper来将结果直接映射为实体类。

public List<UserEntity> findUser(UserEntity user) {  List<UserEntity> userList = jdbcTemplate.query(SEL_BY_USERNAME_PWD, new Object[] { user.getUserName(), user.getPwd() }, new BeanPropertyRowMapper<UserEntity>(UserEntity.class)); return userList; } 

正如上面,直接利用BeanPropertyRowMapper的构造方法传递一个须要映射的类的class对象进去便可实现,固然必须知足以前说的要求:

1.属性名“userName”要按以下规则匹配sql结果列:结果列要是"user_name"(大小写都行),由于BeanPropertyRowMapper的中会将结果列都转为小写去和对象中set属性对应;

2.属性名“user”这对应结果列为“USER”(大小写都行),理由同上;

相关文章
相关标签/搜索