• 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

  • 相关阅读:
    unix/linux中如何在vi编辑器中方便的跳转到首行和末行?
    如何在Ubuntu中用firefox浏览器查看chm文档?
    sybase数据库技术 :游标可更新与for read only/for update
    PropertyMetadata和UIPropertyMetadata的一点区别
    wpf,离线状态下部分功能不可用。
    C#操作注册服务卸载服务启动服务停止服务.. .
    ContentControl与ContentPresenter区别?
    wpf telerik中的book控件
    C#写入和读出文本文件
    WPF 点击Calendar后,需要点击两次按钮
  • 原文地址:https://www.cnblogs.com/zhaoguhong/p/7997873.html
Copyright © 2020-2023  润新知