精华内容
参与话题
问答
  • Spring JDBC

    千次阅读 2013-03-15 09:52:24
    Spirng JDBC 一:采用Maven 引入 jar:... spring-jdbc ${spring.version} mysql mysql-connector-java 5.1.15 引入 spring jdbc 和 mysql 驱动; 二:数据库中建表;采用 mysql数据库;

    Spirng  JDBC

    一:采用Maven 引入 jar:

    <dependency>
    	<groupId>org.springframework</groupId>
    	<artifactId>spring-jdbc</artifactId>
    	<version>${spring.version}</version>
    </dependency>
    		
    
    		<!-- MySQL database driver -->
    		<dependency>
    			<groupId>mysql</groupId>
    			<artifactId>mysql-connector-java</artifactId>
    			<version>5.1.15</version>
    		</dependency>


    引入 spring jdbc  和  mysql 驱动;

    二:数据库中建表;采用 mysql数据库;

    DROP TABLE IF EXISTS `stock`;
    CREATE TABLE `stock` (
      `STOCK_ID` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
      `STOCK_CODE` VARCHAR(10) NOT NULL,
      `STOCK_NAME` VARCHAR(20) NOT NULL,
      PRIMARY KEY (`STOCK_ID`) USING BTREE,
      UNIQUE KEY `UNI_STOCK_NAME` (`STOCK_NAME`),
      UNIQUE KEY `UNI_STOCK_ID` (`STOCK_CODE`) USING BTREE
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    三:构建对应的类:

    package com.myapp.core.jdbc;
    
    public class Stock {
    	private int stock_id;
    	private String stock_name;
    	private String stock_code;
    
    	public int getStock_id() {
    		return stock_id;
    	}
    
    	public void setStock_id(int stock_id) {
    		this.stock_id = stock_id;
    	}
    
    	public String getStock_name() {
    		return stock_name;
    	}
    
    	public void setStock_name(String stock_name) {
    		this.stock_name = stock_name;
    	}
    
    	public String getStock_code() {
    		return stock_code;
    	}
    
    	public void setStock_code(String stock_code) {
    		this.stock_code = stock_code;
    	}
    
    }

    四:构建对应的 dao

    package com.myapp.core.jdbc;
    
    public interface StockDao {
    	 public  void  insert(Stock stock);
    	 
    	 public  Stock  findStockById(int id);
    }
    



    package com.myapp.core.jdbc;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    
    import javax.sql.DataSource;
    
    public class StockDaoImpl  implements StockDao{
     
    	private  DataSource  dataSource;
    	
    	public DataSource getDataSource() {
    		return dataSource;
    	}
    
    	public void setDataSource(DataSource dataSource) {
    		this.dataSource = dataSource;
    	}
    
    	
    	Connection  conn = null;
    	PreparedStatement  prep = null;
    	@Override
    	public void insert(Stock stock) {
    		// TODO Auto-generated method stub
    		
    	String  sql ="insert into  stock  values(?,?,?)";
    	try{
    		
    		conn = dataSource.getConnection();
    		
    		prep = conn.prepareStatement(sql);
    		prep.setInt(1, stock.getStock_id());
    		prep.setString(2, stock.getStock_code());
    		prep.setString(3, stock.getStock_name());
    		
    		boolean  flag = prep.execute();
    		
    		System.out.println(flag);
    		
    	}catch(Exception  e){
    		e.printStackTrace();
    	}finally{
    		try{
    			
    			if(prep != null){
    				prep.close();
    			}
    			if(conn != null){
    				conn.close();
    			}
    		}catch(Exception  ex){
    			ex.printStackTrace();
    		}
    	}
    }
       
    	@Override
    	public Stock findStockById(int id) {
    		// TODO Auto-generated method stub
    		String  sql = "select * from  stock  where  stock_id = ?";
    		ResultSet  rs = null;
    		Stock  stock = null;
    		try{
    			
    			conn = dataSource.getConnection();
    			
    			prep = conn.prepareStatement(sql);
    			prep.setInt(1, id);
    			rs = prep.executeQuery();
    			
    			stock = new  Stock();
    			while(rs.next()){
    				stock.setStock_id(id);
    				stock.setStock_code(rs.getString(2));
    				stock.setStock_name(rs.getString("stock_name"));
    				
    			}
    			
    		}catch(Exception  e){
    			e.printStackTrace();
    		}finally{
    			try{
    				
    				if(prep != null){
    					prep.close();
    				}
    				if(conn != null){
    					conn.close();
    				}
    			}catch(Exception  ex){
    				ex.printStackTrace();
    			}
    		}
    	  
    		return  stock;
    		
    		
    	}
    
    }
    

    五:对应的spring xml 配置注入

      <!-- setting  the  datasource -->
      
      <bean id="stockDao" class="com.myapp.core.jdbc.StockDaoImpl">
           <property name="dataSource" ref="dataSource" />
      </bean>
      
      
      
      
      <bean id="dataSource"
    		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
    		<property name="url" value="jdbc:mysql://localhost:3306/mybase" />
    		<property name="username" value="root" />
    		<property name="password" value="root" />
    	</bean>

    六:构建测试类:

    package com.myapp.core.jdbc;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class MainTest {
       public static void main(String[] args) {
    	   
    	ApplicationContext  context = new   ClassPathXmlApplicationContext("resource/datasources.xml");  
    	
    	 StockDao    stockDao =  (StockDao)context.getBean("stockDao");
    	
    	/* Stock  stock  = new  Stock();
    	        stock.setStock_id(61);
    	        stock.setStock_code("code1");
    	        stock.setStock_name("name1");*/
    	      
    	 
    	 // stockDao.insert(stock);
    	 
    	  Stock  stock =stockDao.findStockById(51);
    	  System.out.println("id:"+stock.getStock_id()+","+stock.getStock_code()+","+stock.getStock_name());
    	 
    }
    }
    

    七:maven 编译 引入jar:

    E:\workspace\Spring3Example>mvn  compile
    E:\workspace\Spring3Example>mvn  eclipse:eclipse
    以上是通过mvn编译,再转换为eclipse项目。

    八:运行结果

    id:51,code,name
    从数据库中取出了相应的数据。

    九:扩展:使用  PropertyPlaceholderConfigurer  通过占位符(placeholder)进行配置 jdbc连接

    1:定义database.properties文件

    jdbc.driverClassName=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/mybase
    jdbc.username=root
    jdbc.password=root


    2:通过配置文件定位database.properties文件的位置:

      <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location">
        <value>resource/database.properties</value>
        </property>
      </bean>

    3:通过占位符配置数据源bean

     
      <bean id="dataSource"
    		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    		<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>


    4:测试结果:

    id:51,code,name

    一样可以查询出结果,测试成功,over
    解决疑问:
    为什么用
    PropertyPlaceholderConfigurer类可以加载定位文件
    PropertyPlaceholderConfigurer实现了接口BeanFactoryPostProcessor
    所有实现了 BeanFactoryPostProcessor接口的实现类会在其他bean是创建之前完成加载和创建,如果一个对象实现了BeanFactoryPostProcessor接口,会在优先进行加载处理。
    Application contexts can auto-detect BeanFactoryPostProcessor beans in their bean definitions and apply them before any other beans get created.
    
    当spring 容器启动是时候,会自动检测那些bean实现了
    BeanFactoryPostProcessor接口,进行优先处理。
    
    
    
    
    关于 BeanFactoryPostProcessor的详细讲解 here
    展开全文
  • Spring JDBC-使用Spring JDBC访问数据库

    千次阅读 2017-09-28 06:02:56
    使用Spring JDBC 基本的数据操作 更改数据 返回数据库表的自增主键值 批量更改数据 查询数据 使用RowCallbackHandler处理结果集 使用RowMapperT处理结果集 RowCallbackHandler和RowMapperT的比较 查询单值数据 调用...

    概述

    Spring JDBC是Spring所提供的持久层技术,它的主要目的降低JDBC API的使用难度,以一种更直接、更简洁的方式使用JDBC API。

    Spring JDBC中,仅仅需要做那些和业务相关的DML操作的事儿而将获取资源、Statement创建、释放资源以及异常处理等繁杂乏味的工作交给Spring JDBC.


    使用Spring JDBC

    Spring JDBC通过模板回调机制大大降低了使用JDBC的复杂度。

    一般情况下,都是在DAO类中使用JdbcTemplate,JdbcTemplate在XML配置文件中后,在DAO中直接注入引用JdbcTemplate即可.

    我们看一个配置文件的例子

    <?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:p="http://www.springframework.org/schema/p"
        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">
    
        <!-- 扫描类包,将标注Spring注解的类自动转化Bean,同时完成Bean的注入 -->
        <context:component-scan base-package="com.xgj.dao.demo"/>
    
        <!-- 不使用context命名空间,则需要定义Bean
        <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
            <property name="locations" value="classpath:spring/jdbc.properties" />  
        </bean> -->
    
        <!-- 使用context命名空间,同上面的Bean等效.在xml文件中配置数据库的properties文件 -->
         <context:property-placeholder location="classpath:spring/jdbc.properties" />
    
         <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" 
            destroy-method="close"
            p:driverClassName="${jdbc.driverClassName}"
            p:url="${jdbc.url}"
            p:username="${jdbc.username}"
            p:password="${jdbc.password}" />
    
        <!-- 配置Jdbc模板  -->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
            p:dataSource-ref="dataSource" />
    
    </beans>
    

    在Spring配配置那文件中配置DAO一般分为4个步骤

    • 定义DataSource

    • 定义JdbcTemplate

    • 声明一个抽象的Bean,以便所有的DAO复用配置JdbcTemplate属性的配置(使用注解的方式更加方便)

    • 配置具体的DAO(使用注解的方式更加方便)


    其中JdbCTemplate有几个属性可以控制底层JDBC API的属性。

    1. queryTimeout 查询数据的最大超时时间,默认为0 ,表示使用底层JDBC驱动程序的默认设置

    2. fetchSize:设置底层的ResultSet每次从数据库返回的行数,该属性对程序的性能影响较大,如果设置过大,因为一次性载入的数据都会放到内存中,所以内存消耗会很大,反之设置的过小,从数据库读取的次数将增大,也会影响性能。 默认为0 ,表示使用底层JDCB驱动程序的默认设置。 Oracle驱动程序的fetchsize的默认值为10

    3. maxRows:设置底层的ResutlSet从数据库返回的最大行数,默认为0 ,表示使用底层JDBC驱动程序默认的设置

    4. ignoreWarnings :是否忽略SQL的告警信息。默认为true,即所有的告警信息都记录到日志中,如果设置为false,则JdbcTemplate将抛出SQLWarningException


    基本的数据操作

    数据库的增删改查(CRUD)及存储过程调用是最常见的数据库操作,JdbcTemplate提供了众多的方法,通过JdbcTemplate可以用简单的方法完成这些数据操作。


    下面我们以示例来实际演示下这些操作

    更改数据

    JdbcTemplate提供了若干个update方法,允许对数据表记录记录进行更改和删除操作。

    这里写图片描述

    首先我们定义一个抽象的DAO基类, BaseDao。 其中暂时我们只封装了注入JDBC的操作(扩展的话可以将分页等通用的功能抽取到BaseDao中等)

    package com.xgj.dao.basicOperation;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.JdbcTemplate;
    
    public abstract class BaseDao {
    
        public JdbcTemplate jdbcTemplate;
    
        // 注入JdbcTemplate实例
        @Autowired
        public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
            this.jdbcTemplate = jdbcTemplate;
        }
    }
    

    然后我们编写DAO层的代码,简单旗舰,直接将DAO定义成了类。一般来讲将DAO层编写成接口更合适。

    package com.xgj.dao.basicOperation.insertUpdateAndDelete;
    
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    import java.util.List;
    
    import org.springframework.jdbc.core.BatchPreparedStatementSetter;
    import org.springframework.stereotype.Repository;
    
    import com.xgj.dao.demo.BaseDao;
    
    /**
     * 
     * 
     * @ClassName: ArtisanDao
     * 
     * @Description: @Repository标注的DAO
     * 
     * @author: Mr.Yang
     * 
     * @date: 2017年9月18日 下午4:19:06
     */
    
    @Repository
    public class ArtisanDao extends BaseDao {
    
        private static final String sql = "insert into artisan_user(user_name,password) values(?,?)";
    
        /**
         * 
         * 
         * @Title: addSingleArtisan
         * 
         * @Description: 增加一个Artisan
         * 
         * @param artisan
         * 
         * @return: void
         */
        public void addSingleArtisan(Artisan artisan) {
            jdbcTemplate.update(sql, artisan.getUserName(), artisan.getPassword());
            System.out.println("insert successfully");
        }
    
    
    }
    

    由于JdbcTemplate在内部通过PreparedStatement执行SQL语句,所以可以绑定参数的SQL语句,每个“?”占位符可以接受一个参数。

    尽量使用可绑定参数的SQL语句,以便数据库可以复用SQL的执行计划,提高数据库的执行效率。 此外,应该在DAO使用类级别的静态常量(final static)定义SQL字符串,不应该在方法内部声明SQL字符串变量,以提高JVM的内存使用效率。

    在通过public int update(String sql, Object... args) throws DataAccessException 方法为SQL语句的占位符绑定参数时,并没有显示的指定对应字段的数据类型,此时,Spring直接让PreparedStatement根据参数的类型进行“猜测”。 有一种更好的做法是使用public int update(String sql, Object[] args, int[] argTypes) throws DataAccessException显示指定每个占位符所对英的字段数据类型,这样就可以保证类型安全,当参数值为null时,这种形式提供了更好的支持。

    以下代码仅为演示

    // 使用该类中的常量属性定义参数类型
    import java.sql.Type 
    
    
    ....
    
    jdbcTemplate,update(sql, new Object[]{..} ,new int[]{Types.VARCHAR2...});
    
    ....

    配置文件如下(以下的几个操作都加载这个配置文件)

    <?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:p="http://www.springframework.org/schema/p"
        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">
    
        <!-- 扫描类包,将标注Spring注解的类自动转化Bean,同时完成Bean的注入 -->
        <context:component-scan base-package="com.xgj.dao.basicOperation" />
    
        <!-- 不使用context命名空间,则需要定义Bean <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
            <property name="locations" value="classpath:spring/jdbc.properties" /> </bean> -->
    
        <!-- 使用context命名空间,同上面的Bean等效.在xml文件中配置数据库的properties文件 -->
        <context:property-placeholder location="classpath:spring/jdbc.properties" />
    
        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
            destroy-method="close" p:driverClassName="${jdbc.driverClassName}"
            p:url="${jdbc.url}" p:username="${jdbc.username}" p:password="${jdbc.password}" />
    
        <!-- 配置Jdbc模板 -->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
            p:dataSource-ref="dataSource" />
    
    </beans>
    

    测试类

    package com.xgj.dao.basicOperation.insertUpdateAndDelete;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class ArtisanDaoTest {
    
        public static void main(String[] args) {
            // 启动Spring 容器
            ApplicationContext ctx = new ClassPathXmlApplicationContext(
                    "classpath:com/xgj/dao/basicOperation/basicOperation.xml");
    
            Artisan artisan = ctx.getBean("artisan", Artisan.class);
            artisan.setUserName("Artisan");
            artisan.setPassword("987654");
    
            ArtisanDao artisanDao = ctx.getBean("artisanDao", ArtisanDao.class);
    
            // 调用目标方法
            artisanDao.addSingleArtisan(artisan);
    }
    

    除了上述两个update方法外,JdbcTemplate还提供了以下几个功能相似的重载方法


    • public int update(final String sql) 为不带占位符的SQL语句提供的便利方法

    • public int update(String sql, PreparedStatementSetter pss) PreparedStatementSetter 是一个回调接口,它定义了一个setValues(PreparedStatement ps)接口方法 ,如下所示

    public void addStudent(Student student){
        jdbcTemplate.update(sql,new PreparedStatementSetter (){
        public void setValues(PreparedStatement  ps) throws SQLException{
            ps.setString(1,student.getName());
            ps.setString(2,student,getSex());
        }
    });
    
    }

    PreparedStatement绑定参数时,参数索引从1开始,而非0开始。 第一个参数索引为1,第二个参数索引为2,依次类推。

    当然了,还有其他方法 ,需要指出的是,在实际用用中,应该优先考虑不带回调接口的JdbcTemplate方法。没有必要使用那些带有回调接口的方法,因为Spring会在内部自动创建这些回调实例。


    返回数据库表的自增主键值

    举个例子 ORACLE数据库

    com.xgj.dao.transaction.annotationTrans.dao.impl.StudentDaoImpl.java

    @Override
        public void addStudent(final Student student) {
            // 这里采用和addTeacher不同的方式,输出插入数据库的主键ID
            KeyHolder keyHolder = new GeneratedKeyHolder();
            PreparedStatementCreator preparedStatementCreator = new PreparedStatementCreator() {
    
                @Override
                public PreparedStatement createPreparedStatement(Connection con)
                        throws SQLException {
                    PreparedStatement ps = con.prepareStatement(addTeacherSQL,
                            new String[] { "id" });
                    ps.setString(1, student.getName());
                    ps.setInt(2, student.getAge());
                    ps.setString(3, student.getSex());
                    return ps;
                }
            };
    
            jdbcTemplate.update(preparedStatementCreator, keyHolder);
            System.out.println("获取到的插入数据库的ID:" + keyHolder.getKey().longValue());
        }
    

    在实际开发中,我们并不太建议使用表自增键,因为这种方式会让开发变得更加复杂且降低程序的移植性,在应用层中创建主键才是主流的方式,可以使用UUID或者通过一个编码引擎获取主键值。


    批量更改数据

    如果需要一次性插入或者更新多条记录,当然可以简单的通过多次调用update()方法完成任务,但是这不是最好的实现方案。 更好的选择是使用JDBCTemplate批量数据更改的方法。一般情况下,后者拥有更好的性能,因为更新的数据将被批量发送到数据库中,它减少了对数据库访问的次数。

    这里写图片描述

    我们解读下下面两个方法:

    • public int[] batchUpdate(String[] sql)
      多条SQL语句组成一个数组,注意此处的sql语句不能带参数,该方法以批量方式执行这些SQL语句。Spring在内部使用JDBC提供的批量更新API完成操作,如果底层的JDBC Driver不支持批量更新操作,Spring将采用逐条更新的方式模拟批量更新。

    • int[] batchUpdate(String sql,BatchPreparedStatementSetter pss)使用本方法对于同一结构的带参SQL语句多次进行数据更新操作。通过BatchPreparedStatementSetter回调接口进行批量参数的绑定工作。

    BatchPreparedStatementSetter定义了两个方法:

    • int getBatchSize():指定本批次的大小
    • void setValues(PreparedStatement ps,int i):为给定的PreparedStatement设置参数
    /**
         * 
         * 
         * @Title: addBatchArtisan
         * 
         * @Description: 批量更新
         * 
         * @param artisanList
         * 
         * @return: void
         */
        public void addBatchArtisan(final List<Artisan> artisanList) {
    
            jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
    
                @Override
                public void setValues(PreparedStatement ps, int index)
                        throws SQLException {
                    Artisan artisan = artisanList.get(index);
                    ps.setString(1, artisan.getUserName());
                    ps.setString(2, artisan.getPassword());
                }
    
                // 指定该批的记录数
                @Override
                public int getBatchSize() {
                    return artisanList.size();
                }
            });
    
            System.out.println("batch insert successfully");
        }

    需要注意的是BatchPreparedStatementSetter是一次性地批量提交数据,而不会分批提交,getBatchSize()是整批的大小。所以,如果希望将一个List中的数据通过BatchPreparedStatementSetter批量更新到数据库中,getBatchSize()就应该设置为List的大小。

    如果List非常大,希望分多次批量提交,则可分段读取这个大List并暂存到一个小的List中,再将这个小的List通过BatchPreparedStatemetSetter批量保存到数据库中。


    查询数据

    在Spring JDBC中,仅需要指定SQL查询语句并定义好如何从结果集中返回数据就可以了。

    使用RowCallbackHandler处理结果集

    Spring提供了org.springframework.jdbc.core.RowCallbackHandler回调接口,通过该接口可以定义如何从结果集中获取数据. RowCaIlbackHandler接口很简单,仅有一 个方法void processRow(ResultSet rs) throws SQLException
    Spring会遍历结果集, 对结果集中的每一行调用RowCallbackHandler回调接口处理数据。所以用户无 须 调用ResultSet的next()方法,而只需要定义好如何获取结果行数据的逻辑就可以了。

    我们来看个示例

    package com.xgj.dao.basicOperation.retrieve_select;
    
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;
    
    import org.springframework.jdbc.core.RowCallbackHandler;
    import org.springframework.jdbc.core.RowMapper;
    import org.springframework.stereotype.Repository;
    
    import com.xgj.dao.demo.BaseDao;
    
    /**
     * 
     * 
     * @ClassName: ArtisanRDao
     * 
     * @Description: @Repository 标注的DAO
     * 
     * @author: Mr.Yang
     * 
     * @date: 2017年9月18日 下午6:14:24
     */
    
    @Repository
    public class ArtisanRDao extends BaseDao {
    
        private static final String selectArtisanUserSql = "select user_name ,password from artisan_user where user_id = ? ";
        private static final String selectArtisanUsersSql = "select user_name ,password from artisan_user where user_id between ? and ? ";
    
        /**
         * 
         * 
         * @Title: selectArtisanById
         * 
         * @Description: 取一条数据
         * 
         * @param artisanId
         * @return
         * 
         * @return: Artisan
         */
        public Artisan selectArtisanById(int artisanId) {
    
            final Artisan artisan = new Artisan();
            // (1)将结果集中的数据抽取到artisan对象中
            jdbcTemplate.query(selectArtisanUserSql, new Object[] { artisanId },
                    new RowCallbackHandler() {
    
                        @Override
                        public void processRow(ResultSet rs) throws SQLException {
                            artisan.setUserName(rs.getString("user_name"));
                            artisan.setPassword(rs.getString("password"));
                        }
                    });
            return artisan;
        }
    }

    如果需要获取多条记录,依旧可以使用RowCallbackHandler完成任务,只需要稍微调整一下结果集的处理逻辑就可以了。 代码如下

    /**
         * 
         * 
         * @Title: selectArtisansByIds
         * 
         * @Description: 使用RowCallbackHandler获取多条记录
         * 
         * @param beginId
         * @param toId
         * @return
         * 
         * @return: List<Artisan>
         */
        public List<Artisan> selectArtisansByIds(int beginId, int toId) {
    
            final List<Artisan> artisanList = new ArrayList<Artisan>();
    
            jdbcTemplate.query(selectArtisanUsersSql,
                    new Object[] { beginId, toId }, new RowCallbackHandler() {
    
                        @Override
                        public void processRow(ResultSet rs) throws SQLException {
                            Artisan artisan = new Artisan();
                            artisan.setUserName(rs.getString("user_name"));
                            artisan.setPassword(rs.getString("password"));
                            artisanList.add(artisan);
                        }
                    });
    
            return artisanList;
        }

    当结果集中没有数据时,并不会抛出异常。只是此时RowCallbackHandle:回调接口 中定义的处理逻辑没有得到调用罢了。


    使用RowMapper<T>处理结果集

    Spring还提供了一个和RowCallbackHandler功能类似的RowMapper<T>接口,它也可以使用RowMapper定义结果集映射逻辑,在结果集为多行记录时,该接口更 容易使用。RowMapper<T>也只有一个接口方法:

    T mapRow(ResultSet rs, int rowNum)

    看下示例

        /**
         * 
         * 
         * @Title: selectArtisansByIdsWithRowMapper
         * 
         * @Description: 使用RowMapper获取多行结果集
         * 
         * @param beginId
         * @param toId
         * @return
         * 
         * @return: List<Artisan>
         */
        public List<Artisan> selectArtisansByIdsWithRowMapper(int beginId, int toId) {
    
            return jdbcTemplate.query(selectArtisanUsersSql, new Object[] {
                    beginId, toId }, new RowMapper<Artisan>() {
    
                @Override
                public Artisan mapRow(ResultSet rs, int rowNum) throws SQLException {
    
                    Artisan artisan = new Artisan();
                    artisan.setUserName(rs.getString("user_name"));
                    artisan.setPassword(rs.getString("password"));
                    return artisan;
                }
            });
    
        }

    RowCallbackHandler和RowMapper<T>的比较

    从功能上讲,RowCallbackHandler和RowMapper没有太大的区别,它们都是用于定义结果集行的读取逻辑,将ResultSet中的数据映射到对象或者List中 。

    RowCallbackHandler接口实现类可以是有状态的,而RowMapper的实现类应该是无状态的。如果RowCallbackHandler实现类是有状态的,用户就不能在多个地方复用,只有无状态的实例都能在不同的地方复用。

    比如,Spring有一个RowCallbackHandler的实现类是RowCountCallbackHandler,可以计算结果集行数:

    RowCountCallbackHandler countCallback = new RowCountCallbackHandler();
    jdbcTemplate.query("select * from user", countCallback);
    int rowCount = countCallback.getRowCount();

    可见RowCountCallbackHandler包含了一个记录结果集行数的状态,在多线程的环境中,如果没有进行特殊的处理,就不能在多个地方复用countCallback实例。

    Spring也提供了几个RowMapper实现类,如ColumnMapRowMapper和SingleColumnRowMapper。

    • ColumnMapRowMapper将结果集中的每一行映射为一个
    • MapSingleColumnRowMapper将结果集中的某一列映射为一个Object。它们都只是定义了映射逻辑,而没有保持状态。

    我们知道,通过JDBC查询返回一个ResultSet结果集时,JDBC并不会一次性将所有匹配的数据都加载到JVM中,而是只返回同一批次的数据(由JDBC驱动程序决定,如Oracle的JDBC驱动程序默认返回10行数据),当通过ResultSet#next()游标流动结果集超过数据范围时,JDBC再获取一批数据。这样以一种“批量化+串行化”的处理方式避免大结果集处理时JVM内存的过大开销。

    当处理大结果集数据时,如果使用RowMapper,则虽然获取数据的过程是串行化的,但是结果集中的所有数据最终都会映射并汇总成一个List对象,占用大量的JVM内存,甚至可以直接引发OutOfMemoryException异常。这里应该使用RowCallbackHandler接口,在processRow接口方法内部处理结果集数据。

    当使用RowCallbackHandler接口时,如果结果集中没有数据,并不会抛出异常,只是此时RowCallbackHandler回调接口中定义的处理逻辑没有得到调用罢了。


    查询单值数据

    Both queryForInt() and queryForLong() are deprecated since version 3.2.2 . To fix it, replace the code with queryForObject(String, Class).

    package com.xgj.dao.basicOperation.getSingleValue;
    
    import org.springframework.stereotype.Repository;
    
    import com.xgj.dao.basicOperation.BaseDao;
    
    /**
     * 
     * 
     * @ClassName: GetCountOfArtisanDao
     * 
     * @Description: @Repository 标注的DAO
     * 
     * @author: Mr.Yang
     * 
     * @date: 2017年9月19日 下午12:05:08
     */
    
    @Repository
    public class GetCountOfArtisanDao extends BaseDao {
    
        private final static String COUNTSQL = "select count(1) from artisan_user where user_name = ? ";
    
        /**
         * 
         * 
         * @Title: getCount
         * 
         * @Description: Both queryForInt() and queryForLong() are deprecated since
         *               version 3.2.2 (correct me if mistake). To fix it, replace
         *               the code with queryForObject(String, Class).
         * 
         *               https://www.mkyong.com/spring/jdbctemplate-queryforint-is-
         *               deprecated/
         * 
         * @return
         * 
         * @return: int
         */
        public boolean getCount(String userName) {
            boolean isExist = false;
            int count = jdbcTemplate.queryForObject(COUNTSQL,
                    new Object[] { userName }, Integer.class);
    
            if (count > 0) {
                isExist = true;
            } else {
                isExist = false;
            }
            return isExist;
        }
    }
    

    调用存储过程(3种方式)

    这里写图片描述

    CallProcDemo

    package com.xgj.dao.basicOperation.callProc;
    
    import java.sql.CallableStatement;
    import java.sql.SQLException;
    import java.sql.Types;
    
    import org.springframework.dao.DataAccessException;
    import org.springframework.jdbc.core.CallableStatementCallback;
    import org.springframework.stereotype.Repository;
    
    import com.xgj.dao.demo.BaseDao;
    
    @Repository
    public class CallProcDemo extends BaseDao {
        // (1) 调用存过的语句
        private static final String PROCSQL_STRING = "call PROC_artisan_oper(?,?)";
    
        public int getUserCount(final String userName) {
            int num = jdbcTemplate.execute(PROCSQL_STRING,
                    new CallableStatementCallback<Integer>() {
    
                        @Override
                        public Integer doInCallableStatement(CallableStatement cs)
                                throws SQLException, DataAccessException {
                            // (2)绑定入参
                            cs.setString(1, userName);
                            // (3)注册输出参数
                            cs.registerOutParameter(2, Types.INTEGER);
                            // 执行
                            cs.execute();
                            return cs.getInt(2);
                        }
                    });
            System.out.println("num:" + num);
            return num;
        }
    }
    

    CallProcDemoWithSimpleJdbcCall

    package com.xgj.dao.basicOperation.callProc;
    
    import java.util.Map;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
    import org.springframework.jdbc.core.namedparam.SqlParameterSource;
    import org.springframework.jdbc.core.simple.SimpleJdbcCall;
    import org.springframework.stereotype.Repository;
    
    @Repository
    public class CallProcDemoWithSimpleJdbcCall {
    
        private JdbcTemplate jdbcTemplate;
    
        @Autowired
        public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
            this.jdbcTemplate = jdbcTemplate;
        }
    
        public void getUserCount(int userId) {
    
            SimpleJdbcCall jdbcCall = new SimpleJdbcCall(jdbcTemplate)
                    .withProcedureName("PROC_artisan_selectInfo_withId");
            // 如果调用function 则为withFunctionName(functionName)
    
            // 注册入参 必须和存过的入参保持一致 不区分大小写
            SqlParameterSource in = new MapSqlParameterSource().addValue(
                    "p_user_id", userId);
    
            // 获取返回结果
            Map<String, Object> outMap = jdbcCall.execute(in);
    
            for (Map.Entry<String, Object> entry : outMap.entrySet()) {
                System.out.println("key=" + entry.getKey() + ",value="
                        + entry.getValue());
            }
    
            String userName = (String) outMap.get("O_USERNAME");
            String password = (String) outMap.get("O_PASSWORD");
    
            System.out.println("userName:" + userName + " ,password=" + password);
    
        }
    }
    

    CallProcDemoWithCallableStatementCreator

    package com.xgj.dao.basicOperation.callProc;
    
    import java.sql.CallableStatement;
    import java.sql.SQLException;
    import java.sql.Types;
    import java.util.HashMap;
    import java.util.Map;
    
    import org.springframework.dao.DataAccessException;
    import org.springframework.jdbc.core.CallableStatementCallback;
    import org.springframework.jdbc.core.CallableStatementCreator;
    import org.springframework.jdbc.core.CallableStatementCreatorFactory;
    import org.springframework.jdbc.core.SqlOutParameter;
    import org.springframework.jdbc.core.SqlParameter;
    import org.springframework.stereotype.Repository;
    
    import com.xgj.dao.demo.BaseDao;
    
    @Repository
    public class CallProcDemoWithCallableStatementCreator extends BaseDao {
    
        private static final String PROCSQL_STRING = "call PROC_artisan_selectInfo_withId(?,?,?)";
    
        @SuppressWarnings({ "rawtypes", "unchecked" })
        public void printUserInfo(int userId) {
    
            // 使用CallableStatementCreatorFactory 创建 CallableStatementCreator
            CallableStatementCreatorFactory factory = new CallableStatementCreatorFactory(
                    PROCSQL_STRING);
            // 设置入参
            factory.addParameter(new SqlParameter("p_user_id", Types.INTEGER));
            // 设置出参
            factory.addParameter(new SqlOutParameter("o_username", Types.VARCHAR));
            factory.addParameter(new SqlOutParameter("o_password", Types.VARCHAR));
    
            Map<String, Integer> paramMap = new HashMap<String, Integer>();
            paramMap.put("p_user_id", userId);
    
            CallableStatementCreator csc = factory
                    .newCallableStatementCreator(paramMap);
    
            String userInfo = jdbcTemplate.execute(csc,
                    new CallableStatementCallback() {
    
                        @Override
                        public Object doInCallableStatement(CallableStatement cs)
                                throws SQLException, DataAccessException {
                            // 执行
                            cs.execute();
                            // 获取返回结果
                            String userName = cs.getString(2);
                            String password = cs.getString(3);
                            String returnInfo = userName + "|" + password;
                            return returnInfo;
                        }
                    });
    
            System.out.println("UserInfo:" + userInfo);
        }
    }
    

    示例源码

    代码已托管到Github—> https://github.com/yangshangwei/SpringMaster

    展开全文
  • Spring JDBC

    千次阅读 2019-02-28 12:02:15
    Spring JDBC

    1 JdbcTemplate 概述

    它是 spring 框架中提供的一个对象,是对原始 Jdbc API 对象的简单封装。spring 框架为我们提供了很多的操作模板类。

    • 操作关系型数据的:
      JdbcTemplate
      HibernateTemplate
    • 操作 nosql 数据库的:
      RedisTemplate
    • 操作消息队列的:
      JmsTemplate

    我们今天的主角在 spring-jdbc-5.0.2.RELEASE.jar 中,我们在导包的时候,除了要导入这个 jar 包外,还需要导入一个 spring-tx-5.0.2.RELEASE.jar(它是和事务相关的)。

    2 JdbcTemplate 对象的创建

    我们可以参考它的源码,来一探究竟:

    public JdbcTemplate() {
    }
    
    public JdbcTemplate(DataSource dataSource) {
        setDataSource(dataSource);
        afterPropertiesSet();
    }
    
    public JdbcTemplate(DataSource dataSource, boolean lazyInit) {
        setDataSource(dataSource);
        setLazyInit(lazyInit);
        afterPropertiesSet();
    }
    

    除了默认构造函数之外,都需要提供一个数据源。既然有set方法,依据我们之前学过的依赖注入,我们可以在配置文件中配置这些对象。

    3 spring 中配置数据源

    3.1 环境搭建

    在这里插入图片描述

    3.2 编写 spring 的配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
        
    </beans>
    

    3.3 配置数据源

    我们之前已经接触过了两个数据源,C3P0 和 DBCP。要想使用这两数据源都需要导入对应的 jar 包。

    3.3.1 配置 C3P0 数据源

    导入 c3p0-0.9.2.1.jar到工程的 lib 目录。在 spring 的配置文件中配置:

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql:///spring"></property>
        <property name="user" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>
    

    3.3.2 配置 DBCP 数据源

    导入 commons-dbcp.jarcommons-pool.jar到工程的 lib 目录。在 spring 的配置文件中配置:

    <!-- 配置数据源 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql:// /spring"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>
    

    3.3.3 配置 spring 内置数据源

    spring 框架也提供了一个内置数据源,我们也可以使用 spring 的内置数据源,它就在spring-jdbc-5.0.2.REEASE.jar 包中:

    <bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql:///spring"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>
    

    3.4 将数据库连接的信息配置到属性文件中:

    【定义属性文件】

    jdbc.driverClass=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql:///spring
    jdbc.username=root
    jdbc.password=123456
    

    【引入外部的属性文件】
    一种方式:

    <!-- 引入外部属性文件: -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:jdbc.properties"/>
    </bean>
    

    另一种方式:

    <context:property-placeholder location="classpath:jdbc.properties"/>
    

    4 JdbcTemplate 的增删改查操作

    4.1 前期准备

    -- 创建数据库:
    create database spring_day02;
    use spring_day02;
    
    -- 创建表:
    create table account(
    id int primary key auto_increment,
    name varchar(40),
    money float
    )character set utf8 collate utf8_general_ci;
    

    4.2 在 spring 配置文件中配置 JdbcTemplate

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!-- 配置一个数据库的操作模板:JdbcTemplate -->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
        <!-- 配置数据源 -->
        <bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
            <property name="url" value="jdbc:mysql:///spring"></property>
            <property name="username" value="root"></property>
            <property name="password" value="123456"></property>
        </bean>
    </beans>
    

    4.3 最基本使用

    public class JdbcTemplateDemo2 {
        public static void main(String[] args) {
            //1.获取 Spring 容器
            ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
            //2.根据 id 获取 bean 对象
            JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate");
            //3.执行操作
            jt.execute("insert into account(name,money)values('eee',500)");
        }
    }
    

    4.4 保存操作

    public class JdbcTemplateDemo3 {
        public static void main(String[] args) {
            //1.获取 Spring 容器
            ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
            //2.根据 id 获取 bean 对象
            JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate");
            //3.执行操作
            //保存
            jt.update("insert into account(name,money)values(?,?)","fff",5000);
        }
    }
    

    4.5 更新操作

    public class JdbcTemplateDemo3 {
        public static void main(String[] args) {
            //1.获取 Spring 容器
            ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
            //2.根据 id 获取 bean 对象
            JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate");
            //3.执行操作
            //修改
            jt.update("update account set money = money-? where id = ?",300,6);
        }
    }
    

    4.6 删除操作

    public class JdbcTemplateDemo3 {
        public static void main(String[] args) {
            //1.获取 Spring 容器
            ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
            //2.根据 id 获取 bean 对象
            JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate");
            //3.执行操作
            //删除
            jt.update("delete from account where id = ?",6);
        }
    }
    

    4.7 查询所有操作

    public class JdbcTemplateDemo3 {
        public static void main(String[] args) {
            //1.获取 Spring 容器
            ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
            //2.根据 id 获取 bean 对象
            JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate");
            //3.执行操作
            //查询所有
            List<Account> accounts = jt.query("select * from account where money > ? ", new AccountRowMapper(), 500);
            for(Account o : accounts){
                System.out.println(o);
            }
        }
    }
    
    public class AccountRowMapper implements RowMapper<Account>{
        @Override
        public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
            Account account = new Account();
            account.setId(rs.getInt("id"));
            account.setName(rs.getString("name"));
            account.setMoney(rs.getFloat("money"));
            return account;
        }
    }
    

    4.8 查询一个操作

    使用 RowMapper 的方式:常用的方式

    public class JdbcTemplateDemo3 {
        public static void main(String[] args) {
            //1.获取 Spring 容器
            ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
            //2.根据 id 获取 bean 对象
            JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate");
            //3.执行操作
            //查询一个
            List<Account> as = jt.query("select * from account where id = ? ", new AccountRowMapper(), 55);
            System.out.println(as.isEmpty()?"没有结果":as.get(0));
        }
    }
    

    使用 ResultSetExtractor 的方式:不常用的方式

    public class JdbcTemplateDemo3 {
        public static void main(String[] args) {
            //1.获取 Spring 容器
            ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
            //2.根据 id 获取 bean 对象
            JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate");
            //3.执行操作
            //查询一个
            Account account = jt.query("select * from account where id = ?", new AccountResultSetExtractor(),3);
            System.out.println(account);
        }
    }
    

    4.9 查询返回一行一列操作

    public class JdbcTemplateDemo3 {
        public static void main(String[] args) {
            //1.获取 Spring 容器
            ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
            //2.根据 id 获取 bean 对象
            JdbcTemplate jt = (JdbcTemplate) ac.getBean("jdbcTemplate");
            //3.执行操作
            //查询返回一行一列:使用聚合函数,在不使用 group by 字句时,都是返回一行一列。最长用的就是分页中获取总记录条数
            Integer total = jt.queryForObject("select count(*) from account where money > ?", Integer.class,500);
            System.out.println(total);
        }
    }
    

    5 在 dao 中使用 JdbcTemplate

    5.1 准备实体类

    /**
    * 账户的实体
    */
    public class Account implements Serializable {
    
        private Integer id;
        private String name;
        private Float money;
    
        // get、set......
        @Override
        public String toString() {
            return "Account [id=" + id + ", name=" + name + ", money=" + money + "]";
        }
    }
    

    5.2 第一种方式:在 dao 中定义 JdbcTemplate

    /**
    * 账户的接口
    */
    public interface IAccountDao {
    
    /**
    * 根据 id 查询账户信息
    * @param id
    * @return
    */
    Account findAccountById(Integer id);
    
    /**
    * 根据名称查询账户信息
    * @return
    */
    Account findAccountByName(String name);
    
    /**
    * 更新账户信息
    * @param account
    */
    void updateAccount(Account account);
    }
    
    /**
    * 账户的持久层实现类
    * 此版本的 dao,需要给 dao 注入 JdbcTemplate
    */
    public class AccountDaoImpl implements IAccountDao {
        private JdbcTemplate jdbcTemplate;
        
        public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
            this.jdbcTemplate = jdbcTemplate;
        }
        @Override
        public Account findAccountById(Integer id) {
            List<Account> list = jdbcTemplate.query("select * from account where id = ?", new AccountRowMapper(), id);
            return list.isEmpty()?null:list.get(0);
        }
        @Override
        public Account findAccountByName(String name) {
            List<Account> list = jdbcTemplate.query("select * from account where name = ? ",new AccountRowMapper(),name);
            if(list.isEmpty()){
                return null;
            }
            if(list.size()>1){
                throw new RuntimeException("结果集不唯一,不是只有一个账户对象");
            }
            return list.get(0);
        }
        @Override
        public void updateAccount(Account account) {
        jdbcTemplate.update("update account set money = ? where id = ? ",account.getMoney(),account.getId());
        }
    }
    

    配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!-- 配置一个 dao -->
        <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl">
            <!-- 注入 jdbcTemplate -->
            <property name="jdbcTemplate" ref="jdbcTemplate"></property>
        </bean>
        
        <!-- 配置一个数据库的操作模板:JdbcTemplate -->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
        <!-- 配置数据源 -->
        <bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName"
    value="com.mysql.jdbc.Driver"></property>
            <property name="url" value="jdbc:mysql:///spring"></property>
            <property name="username" value="root"></property>
            <property name="password" value="123456"></property>
        </bean>
    </beans>
    

    思考:
    此种方式有什么问题吗?
    答案:
    有个小问题。就是我们的 dao 有很多时,每个 dao 都有一些重复性的代码。下面就是重复代码:

    private JdbcTemplate jdbcTemplate;
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    

    能不能把它抽取出来呢?

    5.3 第二种方式:让 dao 继承 JdbcDaoSupport

    JdbcDaoSupport 是 spring 框架为我们提供的一个类,该类中定义了一个 JdbcTemplate 对象,我们可以直接获取使用,但是要想创建该对象,需要为其提供一个数据源:具体源码如下:

    public abstract class JdbcDaoSupport extends DaoSupport {
        //定义对象
        private JdbcTemplate jdbcTemplate;
        //set 方法注入数据源,判断是否注入了,注入了就创建 JdbcTemplate
        public final void setDataSource(DataSource dataSource) {
            if (this.jdbcTemplate == null || dataSource != this.jdbcTemplate.getDataSource()){
                //如果提供了数据源就创建 JdbcTemplate
                this.jdbcTemplate = createJdbcTemplate(dataSource);
                initTemplateConfig();
            }
        }
    
        //使用数据源创建 JdcbTemplate
        protected JdbcTemplate createJdbcTemplate(DataSource dataSource) {
            return new JdbcTemplate(dataSource);
        }
        
        //当然,我们也可以通过注入 JdbcTemplate 对象
        public final void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
            this.jdbcTemplate = jdbcTemplate;
            initTemplateConfig();
        }
        //使用 getJdbcTmeplate 方法获取操作模板对象
        public final JdbcTemplate getJdbcTemplate() {
            return this.jdbcTemplate;
        }
    
    /**
    * 账户的接口
    */
    public interface IAccountDao {
    
        /**
        * 根据 id 查询账户信息
        * @param id
        * @return
        */
        Account findAccountById(Integer id);
    
        /**
        * 根据名称查询账户信息
        * @return
        */
        Account findAccountByName(String name);
    
        /**
        * 更新账户信息
        * @param account
        */
        void updateAccount(Account account);
    }
    
    /**
    * 账户的持久层实现类
    * 此版本 dao,只需要给它的父类注入一个数据源
    */
    public class AccountDaoImpl2 extends JdbcDaoSupport implements IAccountDao {
        @Override
        public Account findAccountById(Integer id) {
            //getJdbcTemplate()方法是从父类上继承下来的。
            List<Account> list = getJdbcTemplate().query("select * from account where id = ? ",new AccountRowMapper(),id);
            return list.isEmpty()?null:list.get(0);
        }
        @Override
        public Account findAccountByName(String name) {
            //getJdbcTemplate()方法是从父类上继承下来的。
            List<Account> list = getJdbcTemplate().query("select * from account where name = ? ",new AccountRowMapper(),name);
            if(list.isEmpty()){
                return null;
            }
            if(list.size()>1){
                throw new RuntimeException("结果集不唯一,不是只有一个账户对象");
            }
            return list.get(0);
        }
        @Override
        public void updateAccount(Account account) {
            //getJdbcTemplate()方法是从父类上继承下来的。
            getJdbcTemplate().update("update account set money = ? where id = ?",account.getMoney(),account.getId());
        }
    }
    

    配置文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
        
        <!-- 配置 dao2 -->
        <bean id="accountDao2" class="com.itheima.dao.impl.AccountDaoImpl2">
            <!-- 注入 dataSource -->
            <property name="dataSource" ref="dataSource"></property>
        </bean>
        <!-- 配置数据源 -->
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
            <property name="url" value="jdbc:mysql:///spring"></property>
            <property name="username" value="root"></property>
            <property name="password" value="123456"></property>
        </bean>
    </beans>
    

    思考:

    • 两版 Dao 有什么区别呢?

    答案:

    • 第一种在 Dao 类中定义 JdbcTemplate 的方式,适用于所有配置方式(xml 和注解都可以)。
    • 第二种让 Dao 继承 JdbcDaoSupport 的方式,只能用于基于 XML 的方式,注解用不了。
    展开全文
  • Spring JDBC开发

    2017-02-07 16:55:58
    Spring JDBC开发@(Spring)[spring jdbc]Spring JDBC开发 SpringJDBC模板的概述 什么是JDBC的模板 SpringJDBC模板入门 创建web项目引入jar包 创建表 编写测试 Spring管理连接池和模板 Spring管理内置连接池 ...
  • Spring JDBC详解

    万次阅读 多人点赞 2015-02-27 20:41:35
    Spring JDBC详解》本文旨在讲述Spring JDBC模块的用法。Spring JDBC模块是Spring框架的基础模块之一。作者:chszs,转载需注明。博客主页:http://blog.csdn.net/chszs一、概述在Spring JDBC模块中,所有的类可以...
  • 示例源码概述我们在Spring-使用Spring JDBC访问数据库使用JDBC进行了CRUD(Create Retrieve Update Delete增删改查)以及调用存过的操作,这里我们将进一步了解一些高级的数据库操作知识,包括获取本地数据连接进行...
  • Spring4.X系列之Spring JDBC

    千次阅读 2017-10-01 23:00:44
    源码专栏欢迎关注 :Spring-JDBC手札导读Spring JDBC-Spring对DAO的支持Apache-DBCP数据库连接池解读C3P0-数据库连接池解读Spring JDBC-Spring事务管理之数据库事务基础知识Spring JDBC-Spring事务管理之ThreadLocal...
  • 本文目录Spring JDBCSpring JDBC 示例第一步:创建maven工程和数据库表第二步:创建dao层第三步:添加Spring配置文件第四步:测试代码和运行结果 Spring JDBC Spring JDBC 框架负责所有的低层细节,从开始打开连接,...
  • 使用Spring JDBC和Lambda表达式简化DAO 如果你需要向数据库中插入一条Item记录,那么会有类似下面的代码: Item对应的实体类型为: public class Item { public int name; public BigDecimal price; } ...
  • Spring JDBC的详解

    2020-09-27 10:34:49
    一.Spring jdbc概述 1.概述: Spring JDBCSpring所提供的持久层技术,他主要目的降低JDBC API的使用难度,以一种更直接、更简洁的方式使用JDBC API。 2.作用: SpringJDBC模块负责数据库资源和错误处理,大大...
  • Spring JDBC配置与使用

    万次阅读 2019-07-05 13:26:45
    1.Spring JDBC模块有什么作用? 1.1JdbcTemplate的解析 1.2Spring Jdbc的配置 1.3Spring JdbcTemplate的常用方法 1.3.1创建数据库,并导入jar包 1.3.2创建配置文件 <?xml version="1.0" encoding="UTF-8"?&...
  • SpringJDBC 学习笔记

    2017-04-29 22:51:45
    SpringJDBC学习记录。
  • Spring JDBCSpringJDBC整合应用直接使用JDBC Spring JDBC的配置和例子。加上mvc 实现 资费列表 自己遇到的一些问题。做的一些记录和总结。
  • Spring学习笔记_5_Spring JDBC

    千次阅读 2020-01-20 19:24:07
    这里其实相当于是复习,之前在学数据库操作的时候顺便学了这个,也记了笔记,关于为什么要用spring JDBC之类的,可以看JDBC那里的先复习一下。 Spring JDBC Spring JdbcTemplate类 JdbcTemplate类的继承关系十分简单...
  • SpringJdbc访问数据库

    千次阅读 2017-06-18 14:30:43
    SpringJdbc (1)SpringJdbc是什么? 是spring框架中的一个模块,对jdbc api做了封装,简化了 jdbc的代码(比如,不用考虑获取连接与关闭连接了) (2)编程步骤 step1. 导包。 step2. 添加spring配置文件。 ...
  • Spring JDBC应用实例讲解 ,具体效果和过程看博文 http://blog.csdn.net/evankaka/article/details/45477185
  • 表示,并且受到位置的限制,定位参数的问题在于,一旦参数的位置发生变化,必须改变参数的绑定,在SpringJDBC中,绑定SQL参数的另一种选择是使用具名参数,SQL具名参数是按照名称绑定,而不是位置绑定。 什么是...
  • 最简单的用户登录与注册系统 spring mvc spring jdbc

    千次下载 热门讨论 2014-11-03 10:51:01
    spring4.0.5版本开发的,spring mvc,spring jdbc,最简单的用户登录,注册和修改的系统。适合初学者学习。
  • Spring JDBC-Spring对事务管理的支持

    千次阅读 2017-09-21 12:08:15
    Spring JDBC 和MybBatis的事务管理器的配置 JPA的事务管理器的配置 Hibernate的事务管理器的配置 JTA 的事务管理器的配置 事务同步管理器 事务的传播行为 示例 编程式的事务管理 示例概述 Spring为事务管理提供了...
  • Spring JDBCSpring JdbcTemplate 、

    千次阅读 2016-11-25 16:43:56
    Spring JDBC抽象框架所带来的价值将在以下几个方面得以体现:(注:使用了Spring JDBC抽象框架之后,应用开发人员只需要完成斜体字部分的编码工作。) 定义数据库连接参数 打开数据库连接 声明SQL语句 预...
  • spring JDBCspring所提供的持久层技术,它的主要目的是降低使用JDBC API 的门槛,以一种更直接更简洁的方式访问JDBC API。 spring JDBC 通过模版和回调机制大大降低了使用JDBC的复杂度,借助JDBCTemplate,用户...
  • Spring JDBC-Spring对DAO的支持

    千次阅读 2017-09-16 14:08:54
    概述 Spring的DAO理念 统一的异常体系 统一的数据访问模板 使用模板和回调机制 模板类 数据源 配置数据源 DBCP数据源 C3P0数据源 ... 此外Spring还提供了一个简化JDBC API操作的Spring JDBC框架。 Sprin
  • 文章目录Spring的数据库编程Spring JDBC的配置(jdbcTemplate)案例实现 Spring的数据库编程   Spring框架为开发者提供了JDBC模板模式,即jdbcTemplate,它可以简化许多代码,但在实际应用中jdbcTemplate并不常用。...
  • Spring JDBC-混合框架的事务管理

    千次阅读 热门讨论 2017-09-27 09:17:18
    示例Hibernate Spring JDBC概述Spring 抽象的 DAO 体系兼容多种数据访问技术,它们各有特色,各有千秋。 Hibernate 是非常优秀的 ORM 实现方案,但对底层 SQL 的控制不太方便 MyBatis 则通过模板化技术让我们能方便...

空空如也

1 2 3 4 5 ... 20
收藏数 37,033
精华内容 14,813
关键字:

springjdbc

spring 订阅