其他分享
首页 > 其他分享> > Spring-jdbcTempalate研究

Spring-jdbcTempalate研究

作者:互联网

   很多时候,需要使用jdbcTemplate,既有出于性能考虑的因素,也有出于个人偏好。

   关于jdbcTemplate的几个关键性的问题:

一、简介

    JdbcTemplate位于org.springframework包,组件标识为spring-jdbc。

    处于spring家族的核心区域。spring专注于应用开发,应用开发据大部分和数据库有关,数据库的操作主要由jdbc负责。

    用spring.io自己的话说,spring-jdbc就是默默地干了大家不愿意干,但又不得不干的事情。

    具体哪些是我们不愿意干的,看spring自己提供的图:

    x表示需要做的。

   要研究透JdbcTemplate,其实光JdbcTemplate自身是不够,还需要了解jdbc的其它一些内容,如果要彻底研究,请阅读spring.io有关的内容。

   限于篇幅,本文只讨论jdbcTempalte等几个template。

 

二、传递SQL参数

   从jdbc底层来说,只有一种传递参数的方式,下面来看参考代码:Lesson: JDBC Basics (The Java™ Tutorials > JDBC Database Access) (oracle.com)

Processing SQL Statements with JDBC (The Java™ Tutorials > JDBC Database Access > JDBC Basics) (oracle.com)
 public void updateCoffeeSales(HashMap<String, Integer> salesForWeek) throws SQLException {
    String updateString =
      "update COFFEES set SALES = ? where COF_NAME = ?";
    String updateStatement =
      "update COFFEES set TOTAL = TOTAL + ? where COF_NAME = ?";

    try (PreparedStatement updateSales = con.prepareStatement(updateString);
         PreparedStatement updateTotal = con.prepareStatement(updateStatement))
    
    {
      con.setAutoCommit(false);
      for (Map.Entry<String, Integer> e : salesForWeek.entrySet()) {
        updateSales.setInt(1, e.getValue().intValue());
        updateSales.setString(2, e.getKey());
        updateSales.executeUpdate();

        updateTotal.setInt(1, e.getValue().intValue());
        updateTotal.setString(2, e.getKey());
        updateTotal.executeUpdate();
        con.commit();
      }
    } catch (SQLException e) {
      JDBCTutorialUtilities.printSQLException(e);
      if (con != null) {
        try {
          System.err.print("Transaction is being rolled back");
          con.rollback();
        } catch (SQLException excep) {
          JDBCTutorialUtilities.printSQLException(excep);
        }
      }
    }
  }

 

    在原生jdbc中,使用?表示一个参数,?起到占位的作用。

    Spring jdbcTemplate为了传递参数方便,支持多种表示参数和设置参数的方式。

    表示参数的方式:

    a.占位,使用?表示

    b.命名,使用":参数名“表示

   

    传递参数的几种方式:

    a.不定大小的数组,集合。通常对应占位传参

    b.Map,Bean。通常对应命名参数

    来看看Spring JdbcTemplate的一些源码:

JdbcTemplate
org.springframework.jdbc.core.JdbcTemplate.batchUpdate(String, Collection<T>, int, ParameterizedPreparedStatementSetter<T>)
org.springframework.jdbc.core.JdbcTemplate.batchUpdate(String, List<Object[]>)
org.springframework.jdbc.core.JdbcTemplate.query(String, Object[], int[], ResultSetExtractor<T>)
org.springframework.jdbc.core.JdbcTemplate.update(String, Object...)

NamedParamterJdbcTempalte
org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.update(String, Map<String, ?>)
org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.queryForObject(String, SqlParameterSource, Class<T>)

 

    大部分传参都容易理解。命名参数传递总体比较优雅,比较好维护,除了写sql的时候会有那么一点点麻烦。

    但我们感兴趣的是SqlParameterSource

    我们来看下SqlParameterSource

 * @author Thomas Risberg
 * @author Juergen Hoeller
 * @since 2.0
 * @see NamedParameterJdbcOperations
 * @see NamedParameterJdbcTemplate
 * @see MapSqlParameterSource
 * @see BeanPropertySqlParameterSource
 */
public interface SqlParameterSource 


SqlParameterSource 接口有三个真正的实现:
org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource
org.springframework.jdbc.core.namedparam.MapSqlParameterSource
org.springframework.jdbc.core.namedparam.EmptySqlParameterSource

其中BeanPropertySqlParameterSource特别受一些人喜欢(有些人喜欢把任何东西包装成bean)

BeanPropertySqlParameterSource的源码注释:
SqlParameterSource implementation that obtains parameter valuesfrom bean properties of a given JavaBean object. The names of the beanproperties have to match the parameter names. 

Uses a Spring BeanWrapper for bean property access underneath.

 

    下面来看看一个例子:

@Override
    @Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT,rollbackFor=Exception.class)
    public int addFamilyWithNJT2(String name) {
        String sql="insert into family(name) values(:name)";
        //使用bean/pojo传递参数
        Family family=new Family(name);
        KeyHolder keyHolder=new GeneratedKeyHolder();        
        SqlParameterSource paramSource=new BeanPropertySqlParameterSource(family);
        int qty=njdbcTp.update(sql, paramSource, keyHolder);
        JSONObject.toJSONString(paramSource, true);
        return keyHolder.getKey().intValue();
    }

 

三、批处理执行

    批量执行,多用于数据导入,采集的业务场景。

    当然,如果是对付高速大量的数据导入,不建议使用目前这种方式,建议直接使用原生的jdbc或者是数据库产生的api来操作。

    只不过,只要咱的数据量不是太大,一般也够用。

   下面来个例子:

   

@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
    @Override
    public String batchExecute() {
        /**
         * 几种基本的batch操作
         */
        String sql = "insert into family(name,batch_no) values(?,?)";
        //1.0 ParameterizedPreparedStatementSetter
        List<Object[]> argList = new ArrayList<>();
        String batchNo = UUID.randomUUID().toString();
        for (int i = 0; i < 2; i++) {
            Object[] a = new Object[2];
            a[0] = UUID.randomUUID().toString();
            a[1] = batchNo;
            argList.add(a);
        }
        jdbcTp.batchUpdate(sql, argList, 4, (PreparedStatement ps, Object[] argument) -> {
            ps.setObject(1, argument[0]);
            ps.setObject(2, argument[1]);
        });

        //2.0 BatchPreparedStatementSetter
        BatchPreparedStatementSetter btss = new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                ps.setObject(1, argList.get(i)[0]);
                ps.setObject(2, argList.get(i)[1]);
            }

            @Override
            public int getBatchSize() {
                //这个大小不能超过参数集合大小,否则会报错。
                return argList.size();
            }
        };
        int[] qtys = jdbcTp.batchUpdate(sql, btss);
        int ttlQty = 0;
        for (int i = 0, len = qtys.length; i < len; i++) {
            ttlQty += qtys[i];
        }
        System.out.println(ttlQty);
        return batchNo;
    }

 

 

四、插入并返回key

 @Override
    public int addFamilyWithJT(String name) {
        KeyHolder keyHolder = new GeneratedKeyHolder();
        jdbcTp.update((Connection con) -> {
            String sql = "insert into family(name) values(?)";
            PreparedStatement ps =con.prepareStatement(sql, new String[]{"custom_id"});
            ps.setInt(1, Integer.valueOf(name));
            return ps;
        }, keyHolder);
        return keyHolder.getKey().intValue();
    }

 

 

五、查询并返回bean/pojo

@Override
    public HcsThirdsrv getByName(String serviceName) {
        String sql="SELECT\r\n"
                + "  service_name,\r\n"
                + "  service_name_cn,\r\n"
                + "  instance_service_name,\r\n"
                + "  service_desc,\r\n"
                + "  status_flag,\r\n"
                + "  add_time,\r\n"
                + "  last_optime\r\n"
                + "FROM\r\n"
                + "  hcs_thirdsrv \n"
                + "where  service_name=? or service_name_cn=? \n"
                + "limit 1";
        RowMapper<HcsThirdsrv> rowMapper =new BeanPropertyRowMapper<HcsThirdsrv>(HcsThirdsrv.class);
        HcsThirdsrv srv=this.jdbcTp.queryForObject(sql, rowMapper,serviceName);
        return srv;
    }

    spring对于返回bean的支持并不友好,希望以后的版本,能够直接出一个不用rowMapper的(现在我们自己都是对jdbcTemplate再封装一遍)。

六、性能

 

1.比较-mybatis、原生jdbc

   主要是和mybatis比较,网上有专门的测试,例如:

   https://blog.csdn.net/liulk20170518/article/details/119358143

   但不是很多,也比较老旧。此外考虑到mybatis的不断进化。

   但毫无疑问,mybatis总会比jdbcTemplate慢一些,因为它花了额外的一些时间做七七八八的处理。

   执行速度上,原生jdbc>jdbcTemplate>mybatis,这是没有异议的。

   我们很多项目还大量使用mybatis,主要是出于工程考虑:用cpu的速度来弥补工程师的思维能力欠缺和手动速度,以提高工程效率。

   灵活优雅,有时候就是慢的代名词。

   简单粗暴,有时候能够更快解决问题。

2.如何优化

   java的主要性能消耗在于数据转换、反射和解析,后者是先天不可调整,所以只能尽量减少反射操作和数据转换。

   所以,如果可能的话,执行sql的时候,尽量使用ListMap或者Map来返回结果。如果您看不习惯没有关系,只要快就可以了。

七、异常

   spring提供了几个常见的异常:

    实际执行的时候,更可能抛出的是org.springframework.dao下异常,这个包路径属于spring事务模块。

    在这个包里面,有更多更在明确的异常说明,例如下图:

 

 

八、和spring其它组件关系

         影响比较多,其中主要是事务。具体略。

九、适用场景

  1. 对性能要求较高的。
  2. 个性化要求高的。有些sql在mapper中写有点麻烦,尤其一些复杂的sql。
  3. 不想多一个依赖的,例如一些核心的东西。能够少依赖就尽量少依赖。这种情况下,可能直接上原生jdbc了,连jdbcTemplate都不用了

    因为这个缘故,所以spring提供了spring-jdbc组件。

    例如公用组件,核心工具,应该尽量少依赖外部的框架。当然实际做的时候,更可能取决于公司的规模和实例,项目和产品的性质。

    如果我们想的再长远一些,那么java是否真有存在的必要性?除了强大的生态,java并没有什么傲人的优势,而计算机最大优势就是人对于速度的最求,所以java要存活下去,则必须

修改jvm和编译器,一处编译处处执行,并没有那么大的必要性,尤其是做项目,虽然的确有的时候是不错。

   

 

标签:jdbc,String,jdbcTempalate,研究,Spring,spring,sql,org,name
来源: https://www.cnblogs.com/lzfhope/p/16071399.html