zoukankan      html  css  js  c++  java
  • Spring JDBC(一)jdbcTemplate

    前言

    最近工作中经常使用Spring JDBC操作数据库,也断断续续的看了一些源码,便有了写一些总结的想法,希望在能帮助别人的同时,也加深一下自己对Spring JDBC的理解。

    Spring JDBC 简介

    Spring JDBC 是spring 官方提供的一个持久层框架,对jdbc进行了抽象和封装,消除了重复冗余的jdbc重复性的代码,使操作数据库变的更简单。

    但Spring JDBC本身并不是一个orm框架,与hibernate相比,它需要自己操作sql,手动映射字段关系,在保持灵活性的同时,势必造成了开发效率的降低。如果需要使用完整的orm框架操作数据库,可以使用hibernate或者spring Data Jpa。

    Spring JDBC不同版本的api会稍有变动,但总体的变化不大,以下测试代码均使用4.3.11.RELEASE。

    模板类

    Spring JDBC 提供了模板类对数据库简化对数据库的操作,其中JdbcTemplate是最常用的,如果需要使用命名参数可以使用NamedParameterJdbcTemplate,SimpleJdbcTemplate在3.1版本已经标记过时,在我使用的4.3版本中,已经被删除。

    JdbcTemplate 入门示例

    JdbcTemplate使用很简单,注入一个数据源就可以使用了

    public class A001SpringJdbcJdbcTemplateTest {
      private JdbcTemplate jdbcTemplate;
    
      @Before
      public void init() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/blogsrc?useUnicode=true&characterEncoding=UTF-8");
        dataSource.setUsername("root");
        dataSource.setPassword("zhao");
        jdbcTemplate = new JdbcTemplate(dataSource);
      }
    
      @Test
      public void queryTest() {
        String sql = "select * from user";
        List<Map<String, Object>> users = jdbcTemplate.queryForList(sql);
        System.out.println(users);
      }
    
    }
    

    对于参数赋值,可以采用占位符的方式

      @Test
      public void queryByParameterTest() {
        String sql = "select * from user where id =?";
        List<Map<String, Object>> users = jdbcTemplate.queryForList(sql, 1L);
        List<Map<String, Object>> users1 = jdbcTemplate.queryForList(sql, new Object[] {1L});
      }
    

    mapper映射

    Spring JDBC 通过mapper接口把resultSet对象中的数据映射为java对象,例如上述例子中返回 List<Map<String, Object>>,其实使用的是ColumnMapRowMapper的mapper实现。我们自己可以通过实现RowMapper接口的方式自定义从resultSet到java对象的映射关系。

    先创建一个table

    CREATE TABLE `user` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `password` varchar(255) NOT NULL,
      `user_name` varchar(255) NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    

    User实体类

    public class User implements Serializable {
      private Long id;
      private String userName;
      private String password;
      // 省略 getter setter
    }
    

    自定义mapper映射

      @Test
      public void simpleMapperTest() {
        String sql = "select * from user";
        List<User> users = jdbcTemplate.query(sql, new RowMapper<User>() {
          public User mapRow(ResultSet rs, int rowNum) throws SQLException {
            User user = new User();
            user.setId(rs.getObject("id") == null ? null : rs.getLong("id"));
            user.setUserName(rs.getString("user_name"));
            user.setPassword(rs.getString("password"));
            return user;
          }
        });
      }
    

    调用存储过程

    对于数据库数据的更新操作,可以直接调用update接口,如果是存储过程,可以通过execute完成调用。

    新建一个简单的存储过程test

    DROP PROCEDURE IF EXISTS test;
    CREATE PROCEDURE test (IN userId BIGINT ( 20 ),OUT userName VARCHAR ( 200 ) ) 
    BEGIN
    SET userName = ( SELECT user_name FROM USER WHERE id = userId );
    END;
    
      @Test
      public void proTest() {
        String sql = "insert into user (user_name,password) VALUES (?, ?)";
        jdbcTemplate.update(sql, "赵孤鸿", "123456"); // 插入数据
        String userName = jdbcTemplate.execute(new CallableStatementCreator() {
          public CallableStatement createCallableStatement(Connection con) throws SQLException {
            String proc = "{call test(?,?)}";
            CallableStatement cs = con.prepareCall(proc);
            cs.setLong(1, 1L);// 设置输入参数的值 索引从1开始
            cs.registerOutParameter(2, Types.VARCHAR);// 设置输出参数的类型
            return cs;
          }
        }, new CallableStatementCallback<String>() {
          public String doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException {
            cs.execute();
            return cs.getString(2);// 返回输出参数
          }
        });
        System.out.println(userName);
      }
    

    NamedParameterJdbcTemplate

    NamedParameterJdbcTemplate的使用基本上和JdbcTemplate类似,只不过参数的赋值方式由占位符变成了命名参数,命名参数优势在于,如果一个相同的参数出现了多次,只需要进行一次赋值即可。
    创建NamedParameterJdbcTemplate对象的两种方式

        // 方式1
        namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
        // 方式2
        namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
    
      @Test
      public void namedParameterJdbcTemplateTest() {
        String sql = "select * from user where id =:id";
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("id", 1L);
        List<Map<String, Object>> users = namedParameterJdbcTemplate.queryForList(sql, parameters);
        System.out.println(users);
      }
    

    batchUpdate

    对于大数据量的数据更新,可以采用batchUpdate接口

      @Test
      public void batchUpdateTest() {
        String sql = "insert into user (user_name,password) VALUES (?, ?)";
        List<User> users = Lists.newArrayList();
        for (int i = 0; i <= 10; i++) {
          User user = new User();
          user.setUserName("xiaoming");
          user.setPassword("123456");
          users.add(user);
        }
        jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
          @Override
          public void setValues(PreparedStatement ps, int i) throws SQLException {
            User user = users.get(i);
            int count = 0;
            ps.setString(++count, user.getUserName());// 索引从1开始
            ps.setString(++count, user.getPassword());
          }
    
          @Override
          public int getBatchSize() {
            return users.size();
          }
        });
      }
    

    与spring整合

    只需要注入数据源即可

        <!-- 省略dataSource相关配置 -->
        
    	<!-- 配置 Spirng 的 JdbcTemplate -->
    	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    		<property name="dataSource" ref="dataSource"></property>
    	</bean>
    	<!-- 配置 NamedParameterJdbcTemplate -->
    	<bean id="namedParameterJdbcTemplate"
    		class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
    		<constructor-arg ref="dataSource"></constructor-arg>
    	</bean>
    

    如果是springboot项目,直接使用即可

      @Autowired
      private JdbcTemplate jdbcTemplate;
    

    注意事项

    关于queryForObject,如果没有查询到,或者查询到多个,都会抛异常,看一下源码

      // 未查询到,或者查询到多个,都会抛异常
      public static <T> T requiredSingleResult(Collection<T> results) throws IncorrectResultSizeDataAccessException {
        int size = (results != null ? results.size() : 0);
        if (size == 0) {
          throw new EmptyResultDataAccessException(1);
        }
        if (results.size() > 1) {
          throw new IncorrectResultSizeDataAccessException(1, size);
        }
        return results.iterator().next();
      }
    

    queryForMap最终走的也是queryForObject方法,因此,使用时也要注意

    在自定义mapper获取ResultSet的值时,获取基本类型数据会有默认值,解决办法如下

      Long id =  rs.getLong("id");//如果id为null,会默认0
      Long id1 =  rs.getObject("id") == null ? null : rs.getLong("id");//先判断是否为null
    

    完整的 源码 https://github.com/zhaoguhong/blogsrc

  • 相关阅读:
    防删没什么意思啊,直接写废你~
    绝大多数情况下,没有解决不了的问题,只有因为平时缺少练习而惧怕问题的复杂度,畏惧的心理让我们选择避让,采取并不那么好的方案去解决问题
    Java 模拟面试题
    Crossthread operation not valid: Control 'progressBar1' accessed from a thread other than the thread it was created on
    一步步从数据库备份恢复SharePoint Portal Server 2003
    【转】理解 JavaScript 闭包
    Just For Fun
    The database schema is too old to perform this operation in this SharePoint cluster. Please upgrade the database and...
    Hello World!
    使用filter筛选刚体碰撞
  • 原文地址:https://www.cnblogs.com/zhaoguhong/p/7997873.html
Copyright © 2011-2022 走看看