ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Spring-jdbcTempalate研究

2022-05-02 12:32:58  阅读:129  来源: 互联网

标签:jdbc String jdbcTempalate 研究 Spring spring sql org name


   很多时候,需要使用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提供了几个常见的异常:

  • BadSqlGrammarException --语法错误
  • CannotGetJdbcConnectionException -- 获取连接异常
  • IncorrectResultSetColumnCountException -- 错误结果集合列数异常,例如本来只要一列的,现在有2列
  • InvalidResultSetAccessException  --  不可用的结果集合读取异常,通常发生在列位置或者名称设置错误的情况
  • JdbcUpdateAffectedIncorrectNumberOfRowsException -- 实际影响行数超出预计的异常。例如本来应该只影响1行,但现在2行
  • LobRetrievalFailureException -- 读取大字段数据失败
  • SQLWarningException  -- sql警告异常。没有特别说明
  • UncategorizedSQLException -- 无分类sql异常

    实际执行的时候,更可能抛出的是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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有