zoukankan      html  css  js  c++  java
  • Spring中的JdbaTemplate

    JdbcTemplate概述

    Spring对数据库的操作在jdbc上面做了基本的封装,让开发者在操作数据库时只需关注SQL语句和查询结果处理器,即可完成功能(当然,只使用JdbcTemplate,还不能摆脱持久层实现类的编写)。
    在配合spring的IoC功能,可以把DataSource注册到JdbcTemplate之中。同时利用spring基于aop的事务即可完成简单的数据库CRUD操作。
    JdbcTemplate的限定命名为org.springframework.jdbc.core.JdbcTemplate。要使用JdbcTemlate需要导入spring-jdbc和spring-tx两个坐标。
    

    源码

    /**
    * JdbcTemplate实现了JdbcOperations接口,操作方法都定义在此接口中
    */
    public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
    /**
    * 使用默认构造函数构建JdbcTemplate
    */
    public JdbcTemplate() {
    }
    /**
    * 通过数据源构建JdbcTemplate
    */
    public JdbcTemplate(DataSource dataSource) {
    setDataSource(dataSource);
    afterPropertiesSet();
    }
    /**
    * 当使用默认构造函数构建时,提供了设置数据源的方法
    */
    public void setDataSource(@Nullable DataSource dataSource) {
    this.dataSource = dataSource;
    }
    }
    

    使用 JdbcTemplate

    JdbcTemplate是 JDBC 核心软件包中的中心类。它处理资源的创建和释放,这有助于您避免常见的错误,例如忘记关闭连接。它执行核心 JDBC 工作流程的基本任务(例如,语句创建和执行),而使应用程序代码提供 SQL 并提取结果。 JdbcTemplate类:

    • 运行 SQL 查询
    • 更新语句和存储过程调用
    • ResultSet个实例执行迭代并提取返回的参数值。
    • 捕获 JDBC 异常,并将其转换为org.springframework.dao包中定义的通用,信息量更大的异常层次结构。 (请参阅一致的异常层次结构。)

    在代码中使用JdbcTemplate时,只需实现回调接口,即可为它们明确定义 Contract。给定JdbcTemplate类提供的ConnectionPreparedStatementCreator回调接口将创建一条准备好的语句,提供 SQL 和任何必要的参数。 CallableStatementCreator接口(创建可调用语句)也是如此。 RowCallbackHandler接口从ResultSet的每一行提取值。

    您可以通过直接实例化DataSource引用在 DAO 实现中使用JdbcTemplate,也可以在 Spring IoC 容器中对其进行配置,并将其作为 Bean 引用提供给 DAO。

    Note

    DataSource应该始终配置为 Spring IoC 容器中的 bean。在第一种情况下,将 Bean 直接提供给服务。在第二种情况下,将其提供给准备好的模板。

    此类发出的所有 SQL 都以DEBUG级别记录在与模板实例的标准类名相对应的类别下(通常为JdbcTemplate,但是如果使用JdbcTemplate类的自定义子类,则可能有所不同)。

    以下各节提供了JdbcTemplate用法的一些示例。这些示例不是JdbcTemplate公开的所有功能的详尽列表。见服务员javadoc

    Querying (SELECT)

    以下查询获取关系中的行数:

    int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class);
    

    以下查询使用绑定变量:

    int countOfActorsNamedJoe = this.jdbcTemplate.queryForObject(
            "select count(*) from t_actor where first_name = ?", Integer.class, "Joe");
    

    以下查询查找String

    String lastName = this.jdbcTemplate.queryForObject(
            "select last_name from t_actor where id = ?",
            new Object[]{1212L}, String.class);
    

    以下查询查找并填充单个域对象:

    Actor actor = this.jdbcTemplate.queryForObject(
            "select first_name, last_name from t_actor where id = ?",
            new Object[]{1212L},
            new RowMapper<Actor>() {
                public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
                    Actor actor = new Actor();
                    actor.setFirstName(rs.getString("first_name"));
                    actor.setLastName(rs.getString("last_name"));
                    return actor;
                }
            });
    

    以下查询查找并填充许多域对象:

    List<Actor> actors = this.jdbcTemplate.query(
            "select first_name, last_name from t_actor",
            new RowMapper<Actor>() {
                public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
                    Actor actor = new Actor();
                    actor.setFirstName(rs.getString("first_name"));
                    actor.setLastName(rs.getString("last_name"));
                    return actor;
                }
            });
    

    如果最后两个代码段确实存在于同一应用程序中,则删除两个RowMapper匿名内部类中存在的重复并将它们提取到单个类(通常是static嵌套类)中,然后可以引用该重复是有意义的。根据需要使用 DAO 方法。例如,最好编写以下代码片段,如下所示:

    public List<Actor> findAllActors() {
        return this.jdbcTemplate.query( "select first_name, last_name from t_actor", new ActorMapper());
    }
    
    private static final class ActorMapper implements RowMapper<Actor> {
    
        public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
            Actor actor = new Actor();
            actor.setFirstName(rs.getString("first_name"));
            actor.setLastName(rs.getString("last_name"));
            return actor;
        }
    }
    
    使用 JdbcTemplate 更新(INSERT,UPDATE 和 DELETE)

    您可以使用update(..)方法执行插入,更新和删除操作。参数值通常作为变量参数提供,或者作为对象数组提供。

    下面的示例插入一个新条目:

    this.jdbcTemplate.update(
            "insert into t_actor (first_name, last_name) values (?, ?)",
            "Leonor", "Watling");
    

    以下示例更新现有条目:

    this.jdbcTemplate.update(
            "update t_actor set last_name = ? where id = ?",
            "Banjo", 5276L);
    

    下面的示例删除一个条目:

    this.jdbcTemplate.update(
            "delete from actor where id = ?",
            Long.valueOf(actorId));
    
    其他 JdbcTemplate 操作

    您可以使用execute(..)方法来运行任意 SQL。因此,该方法通常用于 DDL 语句。带有回调接口,绑定变量数组等的变体极大地超载了它。以下示例创建一个表:

    this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
    

    下面的示例调用一个存储过程:

    this.jdbcTemplate.update(
            "call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
            Long.valueOf(unionId));
    
    JdbcTemplate 最佳做法

    JdbcTemplate类的实例一旦配置便是线程安全的。这很重要,因为这意味着您可以配置JdbcTemplate的单个实例,然后将该共享引用安全地注入到多个 DAO(或存储库)中。 JdbcTemplate是有状态的,因为它维护对DataSource的引用,但是此状态不是会话状态。

    使用JdbcTemplate类(和关联的NamedParameterJdbcTemplate类)的常见做法是在 Spring 配置文件中配置DataSource,然后将共享的DataSource bean 依赖注入到 DAO 类中。在DataSource的设置器中创建JdbcTemplate。这将导致类似于以下内容的 DAO:

    public class JdbcCorporateEventDao implements CorporateEventDao {
    
        private JdbcTemplate jdbcTemplate;
    
        public void setDataSource(DataSource dataSource) {
            this.jdbcTemplate = new JdbcTemplate(dataSource);
        }
    
        // JDBC-backed implementations of the methods on the CorporateEventDao follow...
    }
    

    以下示例显示了相应的 XML 配置:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
    
        <bean id="corporateEventDao" class="com.example.JdbcCorporateEventDao">
            <property name="dataSource" ref="dataSource"/>
        </bean>
    
        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
            <property name="driverClassName" value="${jdbc.driverClassName}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    
        <context:property-placeholder location="jdbc.properties"/>
    
    </beans>
    

    显式配置的替代方法是使用组件扫描和 Comments 支持进行依赖项注入。在这种情况下,可以用@RepositoryComments 该类(这使其成为组件扫描的候选对象),并用@AutowiredCommentsDataSource setter 方法。以下示例显示了如何执行此操作:

    @Repository (1)
    public class JdbcCorporateEventDao implements CorporateEventDao {
    
        private JdbcTemplate jdbcTemplate;
    
        @Autowired (2)
        public void setDataSource(DataSource dataSource) {
            this.jdbcTemplate = new JdbcTemplate(dataSource); (3)
        }
    
        // JDBC-backed implementations of the methods on the CorporateEventDao follow...
    }
    
    • (1)@RepositoryComments 类。
    • (2)@AutowiredCommentsDataSource setter 方法。
    • (3)DataSource创建一个新的JdbcTemplate

    以下示例显示了相应的 XML 配置:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
    
        <!-- Scans within the base package of the application for @Component classes to configure as beans -->
        <context:component-scan base-package="org.springframework.docs.test" />
    
        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
            <property name="driverClassName" value="${jdbc.driverClassName}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </bean>
    
        <context:property-placeholder location="jdbc.properties"/>
    
    </beans>
    

    如果使用 Spring 的JdbcDaoSupport类,并且各种 JDBC 支持的 DAO 类都从该类扩展,则您的子类将从JdbcDaoSupport类继承setDataSource(..)方法。您可以选择是否从此类继承。提供JdbcDaoSupport类只是为了方便。

    无论您选择使用(或不使用)以上哪种模板初始化样式,都无需在每次运行 SQL 时都创建一个新的JdbcTemplate类实例。配置完成后,JdbcTemplate实例是线程安全的。如果您的应用程序访问多个数据库,则可能需要多个JdbcTemplate实例,这需要多个DataSources实例,然后需要多个不同配置的JdbcTemplate实例。

    示例代码

    public class JdbcConfig {
    
        @Value("${jdbc.driver}")
        private String driver;
        @Value("${jdbc.url}")
        private String url;
        @Value("${jdbc.username}")
        private String username;
        @Value("${jdbc.password}")
        private String password;
    
        /**
         * 创建数据源并存入Ioc容器
         * @return
         */
        @Bean
        public DataSource createDataSource(){
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setDriverClassName(driver);
            dataSource.setUrl(url);
            dataSource.setUsername(username);
            dataSource.setPassword(password);
            return dataSource;
        }
    
        /**
         * 创建JdbcTemplate对象
         * @param dataSource
         * @return
         */
        @Bean
        public JdbcTemplate createJdbcTemplate(DataSource dataSource){
            return new JdbcTemplate(dataSource);
        }
    
        @Bean
        public LobHandler createLobHandler(){
            return new DefaultLobHandler();
        }
    
        @Bean
        public NamedParameterJdbcTemplate createNamedParameterJdbcTemplate(JdbcTemplate jdbcTemplate){
            return new NamedParameterJdbcTemplate(jdbcTemplate);
        }
    
    }
    
    @Configuration
    @Import(JdbcConfig.class)
    @PropertySource("classpath:jdbc.properties")
    public class SpringConfiguration {
    }
    
    

    测试代码:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = SpringConfiguration.class)
    public class SpringJdbcTemplateTest {
    
        @Autowired
        private JdbcTemplate jdbcTemplate;
    
        @Test
        public void testSave(){
            jdbcTemplate.update("insert into account(money,name)values(?,?)",6789d,"userTest");
        }
    
        @Test
        public void testUpdate(){
            jdbcTemplate.update("update account set name=?,money=? where id=?","testZZZ",23456d,3);
        }
    
        @Test
        public void testDelete(){
            jdbcTemplate.update("delete from account where id = ? ",4);
        }
    
        @Test
        public void testFindOne(){
    //        List<Account> accounts =  jdbcTemplate.query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),1);
    //        System.out.println(accounts.isEmpty()?"empty":accounts.get(0));
    
    //        Account account = jdbcTemplate.queryForObject("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),1);
    //        System.out.println(account);
    
    
            Account account =   jdbcTemplate.query("select * from account where id = ?", (ResultSet rs) ->{
                Account account1 = null;
                //1.判断结果集能往下走
                if(rs.next()){
                    account1 = new Account();
                    account1.setId(rs.getInt("id"));
                    account1.setName(rs.getString("name"));
                    account1.setMoney(rs.getDouble("money"));
                }
                return account1;
            }, 1);
            System.out.println(account);
        }
    
        @Test
        public void testFindAll(){
            List<Account> accountList = jdbcTemplate.query("select * from account where money > ?",new BeanPropertyRowMapper<Account>(Account.class),999d);
            for(Account account : accountList){
                System.out.println(account);
            }
        }
    
        @Test
        public void testFindCount(){
           Integer count =  jdbcTemplate.queryForObject("select count(*) from account where money > ?",Integer.class,999d);
            System.out.println(count);
        }
    
    
        @Test
        public void testQueryForList(){
            /**
             * 得到某个特定类型的集合。类型是方法的第二个参数指定的
             */
            List<Double> list = jdbcTemplate.queryForList("select money from account where money > ?",Double.class,999d);
            for(Double money : list){
                System.out.println(money);
            }
    
            List<Map<String,Object>> list2 = jdbcTemplate.queryForList("select * from account where money > ? ",999d);
            list2.forEach(m ->{
                m.forEach( (k,v) ->{
                    System.out.println(k+","+v);
                });
            });
        }
    
        @Test
        public void testQueryForMap(){
            Map<String,Object> map = jdbcTemplate.queryForMap("select * from account where id = ?",1);
            for(Map.Entry<String,Object> me : map.entrySet()) {
                System.out.println(me.getKey()+","+me.getValue());
            }
        }
    
    
        @Test
        public void testQueryForRowSet(){
            SqlRowSet rowSet = jdbcTemplate.queryForRowSet("select * from account where money > ?",999d);
            System.out.println(rowSet);
            while(rowSet.next()){
                String name = rowSet.getString("name");
                System.out.println(name);
            }
        }
        
    }
    
    
  • 相关阅读:
    java获取程序执行时间
    自己不去努力 还有谁能帮你
    错误: 找不到或无法加载主类 的解决办法
    不要迷信红黑树 哈希是一切
    nancy的诊断2
    nancy中的诊断功能面板1
    ironpython 2.75 在c#中的使用
    sqlserver2008创建数据库 报 Cannot read property is filestream 此属性不可用于sql server 7.0 解决
    结巴net 分词 配置文件路径,在网站中的出现问题的解决
    akka 练手 原来第一次是原封不动的返回传出去的参数
  • 原文地址:https://www.cnblogs.com/dalianpai/p/13723731.html
Copyright © 2011-2022 走看看