• Spring之JdbcTemplate使用


    一:JdbcTemplate概述及入门

      “Don‘t Reinvent the Wheel” , 这是一句很经典的话,出自Spring官方,翻译过来就是说 “不要重复发明轮子” 。由此我们可以猜测,JdbcTemplate的存在使我们开发人员可以摒弃JDBC的原始开发模式,使我们不必重复性的写JDBC原生代码。所以说Spring为了开发的效率,顺带着写了一套JdbcTemplate的模板工具类,它对原始的JDBC有着一个封装,通过模板设计模式帮我们消除了冗余的代码;有经验的朋友们应该会很清楚的知道dbutils工具类,它的封装和JdbcTemplate封装有着相似之处,都是为了简化JDBC开发的方便。

    Tips:大家凡是在Spring中看到xxxTemplate,就是说明被封装了一个模板类

    1:JdbcTemplate类支持的回调类

    (一):预编译语句及存储过程创建回调:用于根据JdbcTemplate提供的连接创建相应的语句
    ①:PreparedStatementCreator:
        通过回调获取JdbcTemplate提供的Connection,由用户使用该Conncetion创建相关的PreparedStatement;
    ②:CallableStatementCreator:
        通过回调获取JdbcTemplate提供的Connection,由用户使用该Conncetion创建相关的CallableStatement;
    
    (二):预编译语句设值回调:用于给预编译语句相应参数设值
    ①:PreparedStatementSetter:
        通过回调获取JdbcTemplate提供的PreparedStatement,由用户来对相应的预编译语句相应参数设值;
    ②:BatchPreparedStatementSetter:
        类似于PreparedStatementSetter,但用于批处理,需要指定批处理大小;
    
    (三):自定义功能回调:提供给用户一个扩展点,用户可以在指定类型的扩展点执行任何数量需要的操作
    ①:ConnectionCallback:
        通过回调获取JdbcTemplate提供的Connection,用户可在该Connection执行任何数量的操作;
    ②:StatementCallback:
        通过回调获取JdbcTemplate提供的Statement,用户可以在该Statement执行任何数量的操作;
    ③:PreparedStatementCallback:
        通过回调获取JdbcTemplate提供的PreparedStatement,用户可以在该PreparedStatement执行任何数量的操作;
    ④:CallableStatementCallback:
        通过回调获取JdbcTemplate提供的CallableStatement,用户可以在该CallableStatement执行任何数量的操作;
    
    (四):结果集处理回调:通过回调处理ResultSet或将ResultSet转换为需要的形式
    ①:RowMapper:
        用于将结果集每行数据转换为需要的类型,用户需实现方法mapRow(ResultSet rs, int rowNum)来完成将每行数据转换为相应的类型。
    ②:RowCallbackHandler:
        用于处理ResultSet的每一行结果,用户需实现方法processRow(ResultSet rs)来完成处理,在该回调方法中无需执行rs.next(),
      该操作由JdbcTemplate来执行,用户只需按行获取数据然后处理即可。 ③:ResultSetExtractor: 用于结果集数据提取,用户需实现方法extractData(ResultSet rs)来处理结果集,用户必须处理整个结果集;

    2:搭建一个最简单的JdbcTemplate 

    <dependencies>
            <!--Spring核心包-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.2.6.RELEASE</version>
            </dependency>
            <!--Spring的操作数据库坐标-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>5.2.6.RELEASE</version>
            </dependency>
            <!--Spring测试坐标-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-test</artifactId>
                <version>5.2.6.RELEASE</version>
            </dependency>
            <!--Mysql驱动-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.32</version>
            </dependency>
            <!--测试包-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
            </dependency>
        </dependencies>
    pom.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"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            https://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
            https://www.springframework.org/schema/aop/spring-aop.xsd">
        <!--开启注解扫描-->
        <context:component-scan base-package="cn.xw"></context:component-scan>
        <!--DriverManagerDataSource放入容器-->
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
            <property name="url" value="jdbc:mysql:///demo_school"></property>
            <property name="username" value="root"></property>
            <property name="password" value="123"></property>
        </bean>
        <!--JdbcTemplate放入容器-->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
        <!--StudentDao放入容器-->
        <bean id="studentDao" class="cn.xw.dao.impl.StudentDaoImpl">
            <property name="template" ref="jdbcTemplate"></property>
        </bean>
        <!--StudentService放入容器-->
        <bean id="studentService" class="cn.xw.service.impl.StudentServiceImpl">
            <property name="studentDao" ref="studentDao"></property>
        </bean>
    </beans>
    applicationContext.xml
    #####Student实体类
    public class Student {
        private int id;            //主键id
        private String name;       //姓名
        private String sex;        //性别
        private int age;           //年龄
        private double credit;     //学分
        private double money;      //零花钱
        private String address;    //住址
        private String enrol;      //入学时间
        //因为简单的单表CRUD就不涉及到外键
        //private int fid;            //外键 连接家庭表信息学生对家庭,一对一
        //private int tid;            //外键 连接老师信息 学生对老师,一对一
        //创建构造器/get/set/toString就不展示了
    }
    
    ++++++++++++++++++++++++++++++++++++++++++
    #####StudentDao接口
    /**
     * Student接口数据操作
     * @author ant
     */
    public interface StudentDao {
        //保存学生
        void save(Student student);
    }
    
    ++++++++++++++++++++++++++++++++++++++++++
    #####StudentDaoImpl实现类
    /**
     * Student数据操作实现类
     * @author ant
     */
    public class StudentDaoImpl implements StudentDao {
        //聚合JdbcTemplate 后面的set注入
        private JdbcTemplate template;
        public void setTemplate(JdbcTemplate template) {
            this.template = template;
        }
        //添加数据
        public void save(Student student) {
            Object[] obj = {student.getName(), student.getSex(), student.getAge(), student.getCredit(),
                    student.getMoney(), student.getAddress(), student.getEnrol()};
            String sql = "insert into student (sname,ssex,sage,scredit,smoney,saddress,senrol) values (?,?,?,?,?,?,?) ";
            //添加
            template.update(sql,obj);
        }
    }
    
    ++++++++++++++++++++++++++++++++++++++++++
    #####StudentService接口
    /**
     * Student业务处理接口
     * @author ant
     */
    public interface StudentService {
        //添加学生
        void save(Student student);
    }
    
    ++++++++++++++++++++++++++++++++++++++++++
    #####StudentServiceImpl实现类
    /**
     * Student业务处理实现类
     * @author ant
     */
    public class StudentServiceImpl implements StudentService {
    
        //聚合StudentDao操作数据 后面set注入对象
        private StudentDao studentDao;
        public void setStudentDao(StudentDao studentDao) {
            this.studentDao = studentDao;
        }
        //保存学生
        public void save(Student student) {
            studentDao.save(student);
        }
    }
    
    ++++++++++++++++++++++++++++++++++++++++++
    #####Client测试类
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = "classpath:applicationContext.xml")
    public class Client {
        @Autowired
        @Qualifier(value="studentService")
        private StudentService ss;
        @Test
        public void saveStudent(){
            Student student = new Student(0, "王二虎", "男", 16, 55.5, 600.5, "安徽滁州", "2018-8-8");
            ss.save(student);
        }
    }
    其它代码

    ①:简单介绍

    <!--Spring的操作数据库坐标-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>5.2.6.RELEASE</version>
            </dependency>
    //上面导入的是使用Spring对数据库简单操作的坐标,其主要用到的对象就是JdbcTemplate

      大家在看我上面的代码会发现我即没使用C3P0也没使用DBCP这2个连接池,其实我使用的是Spring为我们封装的内置数据源DriverManagerDataSource,这个使用也是挺简洁的。

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

    二:JdbcTemplate的单表增删改操作  SQL资料

    1:增加、更新、删除(SQL语句不带参数)

      这增删改的使用方式上都是大同小异,都是对数据库进行写操作,所以在这里我直接使用update就可以完成操作,所以我挑增加操作不带参数详细说一下,后面再简单举两个更新和删除操作

    ①:int update(String sql)

    介绍:这个是最简单的不带参数完成增删改,关注点再SQL语句上  推荐 简单

    //添加数据 不使用参数
        public void saveA() {
            //编写SQL语句
            String sql = "insert into student (sname,ssex,sage,scredit,smoney,saddress,senrol)" +
                    " values ('王老虎','男',25,50,600,'安徽六安','2019-9-8') ";
            //直接放入update方法中
           template.update(sql);
        }

    ②:int update(PreparedStatementCreator psc)

    介绍:这个update方法里面嵌套了一个PreparedStatementCreator接口通过回调会返回一个Connection,由用户自己创建相关的PreparedStatement

        //添加数据 不使用参数
        public void saveB() {
            //sql语句
            final String sql = "insert into student (sname,ssex,sage,scredit,smoney,saddress,senrol)" +
                    " values ('王小二','男',25,50,600,'安徽六安','2019-9-8') ";
            //调用update方法 传入PreparedStatementCreator接口的匿名内部类
            template.update(new PreparedStatementCreator() {
                public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
                    //通过用户自己获得Connection后调用prepareStatement执行sql,原生写法
                    return connection.prepareStatement(sql);
                }
            });
        }
    //这里提示一下,在jdk1.8之前,创建匿名内部类的时候引用外部变量,那个变量要指定为final

    ③:int update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder)

    介绍:说到上一个方法,我们用户通过回调获得Connection,自己操作,这样我们就有扩展性,我可以用这个获取我当前插入数据的主键id(前提主键id是自增长

    //添加数据 不使用参数 并且返回插入数据的主键id
        public void saveC(){
            //sql语句
            final String sql = "insert into student (sname,ssex,sage,scredit,smoney,saddress,senrol)" +
                    " values ('谢霆锋','男',25,50,600,'安徽蚌埠','2019-9-8') ";
            //创建GeneratedKeyHolder对象,用于接收主键id
            final GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
            //调用update方法 并传入PreparedStatementCreator匿名内部类 和keyHolder
            template.update(new PreparedStatementCreator() {
                public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
                    //这里要注意5.1.7版本之后要加入Statement.RETURN_GENERATED_KEYS才可获取自增长的主键id
                    return connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
                }
            },keyHolder);
            //获取id并转换为int类型
            System.out.println("当前插入数据的主键是:"+keyHolder.getKey().intValue());
        }

    ④:void execute(String sql不推荐了解就行 局限性太大

    //补充方法
        public void saveD(){
            //sql语句
            String sql = "insert into student (sname,ssex,sage,scredit,smoney,saddress,senrol)" +
                    " values ('蚂蚁小哥','男',25,50,600,'安徽蚌埠','2019-9-8') ";
            template.execute(sql);
        }

    ⑤:删、改简单演示

    //删除70号id学生
        public void deleteA(){
            template.update("delete  from student where sid=70");
        }
        //删除75号id学生
        public void deleteB(){
            template.update(new PreparedStatementCreator() {
                public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
                    return connection.prepareStatement("delete from student where sid=75");
                }
            });
        }
        //更新学生
        public void update(){
            template.update("update student set sname='潇洒哥' where sid=76 ");
        }
    删改简答操作

    ⑥:关于上面操作可能会遇到的异常

      org.springframework.dao.TransientDataAccessResourceException: PreparedStatementCallback; Generated keys not requested. 
    You need to specify Statement.RETURN_GENERATED_KEYS to Statement.executeUpdate() or Connection.prepareStatement().;
    nested exception is java.sql.SQLException: Generated keys not requested. You need to specify
    Statement.RETURN_GENERATED_KEYS to Statement.executeUpdate() or Connection.prepareStatement().
    //调用update方法 并传入PreparedStatementCreator匿名内部类 和keyHolder
            template.update(new PreparedStatementCreator() {
                public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
                    //这里要注意5.1.7版本之后要加入Statement.RETURN_GENERATED_KEYS才可获取自增长的主键id
                    return connection.prepareStatement(sql);
                }
            },keyHolder);

    我在写之前插入数据后返回自增长的主键id的时候报的错误,我根据我标黑的地方查了一下api和网上的解释,才发现,我导入的mysql驱动坐标是5.1.32,但是5.1.7版本版本之后的mysql-connector增加了返回GeneratedKeys的条件,

    如果需要返回GeneratedKeys,则PreparedStatement需要显示添加一个参数Statement.RETURN_GENERATED_KEYS

    static int CLOSE_ALL_RESULTS 
              该常量指示调用 getMoreResults 时应该关闭以前一直打开的所有 ResultSet 对象。 
    static int CLOSE_CURRENT_RESULT 
              该常量指示调用 getMoreResults 时应该关闭当前 ResultSet 对象。 
    static int EXECUTE_FAILED 
              该常量指示在执行批量语句时发生错误。 
    static int KEEP_CURRENT_RESULT 
              该常量指示调用 getMoreResults 时应该关闭当前 ResultSet 对象。 
    static int NO_GENERATED_KEYS 
              该常量指示生成的键应该不可用于获取。 
    static int RETURN_GENERATED_KEYS 
              该常量指示生成的键应该可用于获取。 
    static int SUCCESS_NO_INFO 
              该常量指示批量语句执行成功但不存在受影响的可用行数计数。 
    template.update(new PreparedStatementCreator() {
                public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
                    //这里要注意5.1.7版本之后要加入Statement.RETURN_GENERATED_KEYS才可获取自增长的主键id
                    return connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
                }
            },keyHolder);

     2:增加、更新、删除(SQL语句带参数)

    ①:int update(String sql,PreParedStatementSetter pss)

    //添加数据 带参数
        public void saveE(final Student student){
            //sql语句
            final String sql = "insert into student (sname,ssex,sage,scredit,smoney,saddress,senrol)" +
                    " values (?,?,?,?,?,?,?) ";
            //调用update方法完成添加
            template.update(sql, new PreparedStatementSetter() {
                public void setValues(PreparedStatement preparedStatement) throws SQLException {
                    //对sql的占位符一个一个手动赋值  ,要想使用原生,下面回介绍
                    preparedStatement.setString(1,student.getName());
                    preparedStatement.setString(2,student.getSex());
                    preparedStatement.setInt(3,student.getAge());
                    preparedStatement.setDouble(4,student.getCredit());
                    preparedStatement.setDouble(5,student.getMoney());
                    preparedStatement.setString(6,student.getAddress());
                    preparedStatement.setString(7,student.getEnrol());
                }
            });
        }

    ②:int update(String sql,Object[] args,int[] argTypes) 不推荐

    介绍:这里主要就是数据和类型对应放入sql占位符上,sql:就是传入带占位符的sql语句,args:sql需要传入占位符的参数,argTypes:需要注入的SQL参数的JDBC类型(可以从java.sql.Types类中获取类型常量)

    //添加数据 带参数
        public void saveF(Student student){
            //sql语句
            final String sql = "insert into student (sname,ssex,sage,scredit,smoney,saddress,senrol)" +
                    " values (?,?,?,?,?,?,?) ";
            //把student数据封装到Object数组中
            Object[] obj = {student.getName(), student.getSex(), student.getAge(), student.getCredit(),
                    student.getMoney(), student.getAddress(), student.getEnrol()};
            //各数据对应的类型
            int [] types={Types.VARCHAR,Types.VARCHAR,Types.INTEGER,Types.DOUBLE,
                    Types.DOUBLE,Types.VARCHAR,Types.VARCHAR};
            //调用方法存储  数据和类型对应
            template.update(sql,obj,types);
        }

    ③:int update(String sql ,Object...args)    推荐,最常用

      其实内部还是调用①实现的,JdbcTemplate提供这种更简单的方式“update(String sql, Object... args)”来实现设值,所以只要当使用该种方式不满足需求时才应使用PreparedStatementSetter(上面方法saveE)。

    //添加数据 带参数
        public void saveG(Student student){
            //sql语句
            final String sql = "insert into student (sname,ssex,sage,scredit,smoney,saddress,senrol)" +
                    " values (?,?,?,?,?,?,?) ";
            //把student数据封装到Object数组中
            Object[] obj = {student.getName(), student.getSex(), student.getAge(), student.getCredit(),
                    student.getMoney(), student.getAddress(), student.getEnrol()};
            //这update后面可以传入一个可变参,可变参的本身也是个伪数组,所以传数组和直接传值一样的
            template.update(sql,obj);
        }

    ③:int update(PreparedStatementCreator psc)

    介绍:使用该方法可以得到回调对象Connection,自己通过这个Connection对象使用原生JDBC方式来给sql注入参数,从而达到增删改

        //添加数据 带参数
        public void saveH(final Student student){//参数也是局部变量,也必须用final修饰,内部类中才能访问(全局变量不用)
            //sql语句
            final String sql = "insert into student (sname,ssex,sage,scredit,smoney,saddress,senrol)" +
                    " values (?,?,?,?,?,?,?) ";
            template.update(new PreparedStatementCreator() {
                public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
                    PreparedStatement prepareStatement = connection.prepareStatement(sql);
                    //这就是原生jdbc底层使用prepareStatemnt一个一个问号赋值
                    prepareStatement.setString(1,student.getName());
                    prepareStatement.setString(2,student.getSex());
                    prepareStatement.setInt(3,student.getAge());
                    prepareStatement.setDouble(4,student.getCredit());
                    prepareStatement.setDouble(5,student.getMoney());
                    prepareStatement.setString(6,student.getAddress());
                    prepareStatement.setString(7,student.getEnrol());
                    return prepareStatement;
                }
            });
        }

    ④:int update(PreparedStatementCreator psc ,KeyHolder generatedKeyHolder)

    介绍:在插入数据的同时获取被插入数据自增长的主键id,并返回

     //添加数据 并返回添加的主键id
        public void saveI(final Student student){
            //sql语句
            final String sql = "insert into student (sname,ssex,sage,scredit,smoney,saddress,senrol)" +
                    " values (?,?,?,?,?,?,?) ";
            //用来接收返回的主键id
            KeyHolder keyHolder = new GeneratedKeyHolder();
            //调用添加方法
            template.update(new PreparedStatementCreator() {
                public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
                    //这里和前面的介绍一样 mysql驱动版本不同 要携带Statement.RETURN_GENERATED_KEYS
                    PreparedStatement prepareStatement = connection.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
                    //这就是原生jdbc底层使用preparedStatement一个一个问号赋值
                    prepareStatement.setString(1,student.getName());
                    prepareStatement.setString(2,student.getSex());
                    prepareStatement.setInt(3,student.getAge());
                    prepareStatement.setDouble(4,student.getCredit());
                    prepareStatement.setDouble(5,student.getMoney());
                    prepareStatement.setString(6,student.getAddress());
                    prepareStatement.setString(7,student.getEnrol());
                    return prepareStatement;
                }
            },keyHolder);
            System.out.println("插入数据的id是:"+keyHolder.getKey().intValue());
        }

     ⑤:更新和删除

      在这里的增删改都使用同一种方法update,无非里面的参数不同,其实它们的操作都是一样的,可以参照上面的添加操作完成删除、更新功能

     3:批量删除、更新、添加

     ①:int [] batchUpdate(String sql,BatchPreparedStatementSetter bpss)

        //批量添加数据
        public void batchSave(final List<Student> stus){
            //sql语句
            final String sql = "insert into student (sname,ssex,sage,scredit,smoney,saddress,senrol)" +
                    " values (?,?,?,?,?,?,?) ";
            //实现批量添加 返回操作完成参数  完成就返回一个 1 ,假设3条记录都添加上就返回[1,1,1]
            int[] totalSave = template.batchUpdate(sql, new BatchPreparedStatementSetter() {
                public void setValues(PreparedStatement preparedStatement, int i) throws SQLException {
                    //注入参数
                    preparedStatement.setString(1, stus.get(i).getName());
                    preparedStatement.setString(2, stus.get(i).getSex());
                    preparedStatement.setInt(3, stus.get(i).getAge());
                    preparedStatement.setDouble(4, stus.get(i).getCredit());
                    preparedStatement.setDouble(5, stus.get(i).getMoney());
                    preparedStatement.setString(6, stus.get(i).getAddress());
                    preparedStatement.setString(7, stus.get(i).getEnrol());
                }
                //返回批量操作的数量
                public int getBatchSize() {
                    //传来的集合的size
                    return stus.size();
                }
            });
            System.out.println("添加的总记录数:"+ totalSave.length);
        }
    
    
    
    #####测试代码方法
        @Test
        public void saveStudentD(){
            List<Student> list=new ArrayList<Student>();
            Student stu1 = new Student(0, "张小俊", "男", 16, 55.5, 600.5, "安徽滁州", "2018-8-8");
            Student stu2 = new Student(0, "王打破", "男", 16, 55.5, 600.5, "安徽滁州", "2018-8-8");
            Student stu3 = new Student(0, "吴小莉", "男", 16, 55.5, 600.5, "安徽滁州", "2018-8-8");
            list.add(stu1);
            list.add(stu2);
            list.add(stu3);
            ss.batchSave(list);
        }
    完成批量添加数据
     //批量更新数据
        public void batchUpdate(final  List<Student> stus){
            //更新的sql语句
            final String sql="update student set sname=?,sage=?,saddress=? where sid=?";
            //实现批量更改 返回操作完成参数  完成就返回一个 1 ,假设3条记录都添加上就返回[1,1,1]
            int [] totalUpdate=template.batchUpdate(sql, new BatchPreparedStatementSetter() {
                public void setValues(PreparedStatement preparedStatement, int i) throws SQLException {
                    //注入参数
                    preparedStatement.setString(1, stus.get(i).getName());
                    preparedStatement.setInt(2, stus.get(i).getAge());
                    preparedStatement.setString(3, stus.get(i).getAddress());
                    preparedStatement.setInt(4, stus.get(i).getId());
                }
                //返回批量操作更新的数量
                public int getBatchSize() {
                    return stus.size();
                }
            });
            System.out.println("更新的总数量:"+totalUpdate.length);
        }
    
    
    ######测试方法
    @Test
        public void saveStudentD(){
            List<Student> list=new ArrayList<Student>();
            Student stu1 = new Student(1, "王大炮", null, 16, 0, 0, "安徽滁州", null);
            Student stu2 = new Student(2, "李筱思", null, 16, 0, 0, "安徽滁州", null);
            Student stu3 = new Student(3, "吴凡亦", null, 16, 0, 0, "安徽滁州", null);
            list.add(stu1);
            list.add(stu2);
            list.add(stu3);
            ss.batchUpdate(list);
        }
    完成批量更新操作
    //完成批量删除
        public void batchDelete(final List<Integer> ids) {
            //实现批量删除操作
            int[] totalDelete = template.batchUpdate("delete from student where sid=?", new BatchPreparedStatementSetter() {
                public void setValues(PreparedStatement preparedStatement, int i) throws SQLException {
                    preparedStatement.setInt(1, ids.get(i));
                }
                //返回批量删除的数量
                public int getBatchSize() {
                    return ids.size();
                }
            });
            System.out.println("删除的总数量:" + totalDelete.length);
        }
    
    
    #####测试代码
     @Test
        public void saveStudentD(){
            List<Integer> list=new ArrayList<Integer>();
            list.add(65);
            list.add(66);
            list.add(67);
            ss.batchDelete(list);
        }
    完成批量删除操作

    三:JdbcTemplate的单表查询操作(重点

    ㈠####==》:queryForObject方法之介绍 

    ①:String sql:
        传入的SQL语句,如果SQL是预处理SQL带'?'占位符的,可通过后面有的参数指定数据源
    ②:Object [] args:
        指定数据源为Object数组,为预处理SQL传入指定的参数值
    ③:Object...args:
        指定数据源为Object伪数组,为预处理SQL传入指定参数值
    ④:int [] argTypes:
        指定数据库字段类型,可以通过java.sql.Type中获取类型常量
    ⑤:Class<T> requiredType
        指定返回的数据类型,只能是JDK内置类型,如String、Integer、Double等,自定义实体类型不行
    ⑥:RowMapper<T> rowMapper:
        用来对数据库字段和实体类属性不匹配,从而映射出对应的对象
    方法参数属性介绍

    1:查询一个值(不携带参数)

     ①:T  queryForObject(String sql,Class<T> requiredType);

    介绍:这个返回的是一个数值,注意参数requiredType只能是String、Integer、Double等JDK内置的类型,不能是自定义的实体类型

    //查询一个值 聚合查询等一系列操作
        public Integer totalCount(){
            //查询表中数据的总记录数
            Integer total = template.queryForObject("select count(sid) from student", Integer.class);
            //比如查询学生的总学分等等
            //Integer sumCredit = template.queryForObject("select sum(scredit) from student", Integer.class);
            return total;
        }

    2:查询一个值(携带参数)

     ①:T  queryForObject(String sql ,Object [] obj ,Class<T> requiredType)

     介绍:此查询返回一个值,但是可传入预处理SQL,后期通过Object数组来传入占位符的数值,还是和上面一样只能指定Class类型为JDK内置的

    //查询一个值带参数  返回一个名字
        public String findByIdReturnName(Integer id){
            //因为只能接收Object数组,所以我把传来的数据封装成数组传入下面方法
            Object [] obj={id};
            //查询
            String name = template.queryForObject("select sname from student where sid=?", obj, String.class);
            return name;
        }

    3:查询一行记录,映射为对象实体类(不携带参数)

    ①:T  queryForObject(String sql,RowMapper<T>  rowMapper)

    介绍:此查询返回一个实体对象,通过RowMapper接口的实现类来映射

        //查询一行记录,无传入参数
        public Student findOneForId(){
            //查询固定SQL指定的id为6的学生
            Student student = template.queryForObject("select * from student where sid=6", new RowMapper<Student>() {
                public Student mapRow(ResultSet resultSet, int i) throws SQLException {
                    //创建一个学生对象,因为实体类和字段不对应 ,我要每个字段都要映射
                    Student stu = new Student();
                    stu.setId(resultSet.getInt("sid"));
                    stu.setName(resultSet.getString("sname"));
                    stu.setSex(resultSet.getString("ssex"));
                    stu.setAge(resultSet.getInt("sage"));
                    stu.setCredit(resultSet.getDouble("scredit"));
                    stu.setMoney(resultSet.getDouble("smoney"));
                    stu.setAddress(resultSet.getString("saddress"));
                    stu.setEnrol(resultSet.getString("senrol"));
                    return stu;
                }
            });
            return student;
        }

    4:查询一行记录,映射为对象实体类(携带参数)

     ①:T  queryForObject(String  sql , Object [] args, RowMapper<T> rowMapper)

    介绍:和上面一种操作差不多,都用到了映射对象,但是唯一多一个数组参数,所以sql可以是预处理SQL

    //查询一行记录,传入指定id
        public Student findById(Integer id){
            //因为只能接收Object []数组 所以我把传来的数据加工成数组传入查询中
            Object [] obj={id};
            //查询指定id的学生数据
            Student student = template.queryForObject("select * from student where sid=?",obj, new RowMapper<Student>() {
                public Student mapRow(ResultSet resultSet, int i) throws SQLException {
                    //创建一个学生对象,因为实体类和字段不对应 ,我要每个字段都要映射
                    Student stu = new Student();
                    stu.setId(resultSet.getInt("sid"));
                    stu.setName(resultSet.getString("sname"));
                    stu.setSex(resultSet.getString("ssex"));
                    stu.setAge(resultSet.getInt("sage"));
                    stu.setCredit(resultSet.getDouble("scredit"));
                    stu.setMoney(resultSet.getDouble("smoney"));
                    stu.setAddress(resultSet.getString("saddress"));
                    stu.setEnrol(resultSet.getString("senrol"));
                    return stu;
                }
            });
            return student;
        }

     5:查询多行多列数据,存入List中(不携带参数)

     ①:List<Map<String,Object>> queryForList(String sql)

     介绍:直接把查询的数据放入List<Map<String,Object>>类型中,假设有3条记录,把每一条记录都以键值对放入map中,键是String,值全部使用Object类型,然后封装,有3条记录就封装这样的类型3次,然后依次放入List中。注:也可以当作多行单列查询,但是数据封装为List<Map<String,Object>>

     //查询多行多列数据 封装到List中类型为Map
        public List<Map<String,Object>> findByStudent(){
            //查询  固定sql不带参
            List<Map<String, Object>> maps = template.queryForList("select * from student where sid in(1,6,8)");
            return maps;
        }
    ####测试代码
    @Test
        public void TestA() {
            List<Map<String, Object>> byStudent = ss.findByStudent();
            //遍历数据
            for (Map<String, Object> maps : byStudent) {
                for(Map.Entry<String,Object> obj:maps.entrySet()){
                    System.out.println(obj.getKey()+"  "+obj.getValue());
                }
            }
        }

    ㈡####==》:queryForList方法之介绍

    ①:String sql:
        传入的SQL语句,如果SQL是预处理SQL带'?'占位符的,可通过后面有的参数指定数据源
    ②:Class<T> elementType:
        传入获取每个数据要封装的类型,传入这个就代表只可以获取多列单行数据了
    ③:Object [] args:
        指定数据源为Object数组,为预处理SQL传入指定的参数值
    ④:Object...args:
        指定数据源为Object伪数组,为预处理SQL传入指定参数值
    ⑤:int [] argTypes:
        指定数据库字段类型,可以通过java.sql.Type中获取类型常量
    方法参数属性介绍

    1:查询多行单列数据,存入List中(不携带参数)

    ①:List<T> queryForList(String sql , Class<T> elementType)

     介绍:查询多行单列,因为指定了获取的数据的类型,如果要获取全部数据可以使用第5种方法,

    //查询一行单列数据 封装到List<T>中
        public List<String> findAllByReturnName(){
            //查询多行单列数据  Class<T> elementType参数指定当前要查询列的类型
            List<String> stunames = template.queryForList("select sname from student where sid in(1,5,3,6,8,9)", String.class);
            return stunames;
        }

    2:查询多行多列数据,存入List中(携带参数)①

     ①:List<Map<String,Object>> queryForList(String sql , Object . . . args)

     介绍:这种和第5的差不多,只是多了一个可变参,也就是我们常说的伪数组,可以传入指定的值,最后封装的类型还是List<Map<String,Object>>

    //查询多行多列,根据传入多个id查询(携带参数)
        public List<Map<String,Object>> findByManyId(Integer ... args){
            List<Map<String, Object>> maps = template.queryForList("select * from student where sid in(?,?,?)", args);
            return maps;
        }

    3:查询多行多列数据,存入List中(携带参数)②

     ①:List<Map<String,Object>> queryForList(String,Object [] args,int [] types)

    介绍:这个查询总体来说和第5一样,只是多了2个参数Object [] args 和int [] type,说明传入的参数和传入的类型必须挨个对应

    //查询多行多列,更根据性别和模糊姓名查询,并指定limit查询几行(携带参数)  这里的name参数因为是模糊查询 传入 “张%”
        public List<Map<String,Object>> findBySexAndLikeNameAndLimit(String sex,String name,Integer order){
            //带有占位符的sql语句
            String sql="select * from student where ssex=? and sname like ? limit ?";
            //把传入的数据封装为Object数组
            Object [] obj={sex,name,order};
            //封装传入数值对应的数据库字段类型
            int [] types={Types.VARCHAR,Types.VARCHAR,Types.INTEGER};
            List<Map<String, Object>> maps = template.queryForList(sql, obj, types);
            return maps;
            /**
             * 这里说明一下Object[]和int[]里面的数值和类型必须挨个对应,就算是传入limit查询个数,也要加入
             * 数据库类型INTEGER类型
             */
        }

    4:查询多行单列数据,存入List中(携带参数)

    ①:List<T> queryForList(String sql,Object [] args , Class<T> elementType)

    介绍:查询多行单列,因为指定了Class<T> elementType参数,而且还是携带参数

    //查询多行单列 携带参数 查询性别为?的姓名
        public List<String> findAllReturnName(String sex){
            //因为接收Object[] 类型,所以转换传入的数据
            Object [] obj={sex};
            //查询封装返回
            List<String> strings = template.queryForList("select sname from student where sex=?", obj, String.class);
            return strings;
        }

    5:说明其它相关方法

    ①:List<T> queryForList(String sql ,Class<T> elementType , Object...args)

    介绍:这个也是查询多行单列的查询方法,但是唯独出现了一个可变参伪数组,还有就算传入获取数据的类型

    ②:List<T> queryForList(String sql , Object [] args,int [] argType , Class<T> elementType)

    介绍:这个也是查询多行单列,这里注意args和argType传入的数据和类型必须挨个对应,还有就是传入一个获取的数据类型

    ㈢####==》:query方法之介绍(用的最多,也是要掌握的)

    一:查询无返回值   void
    ①:query (String sql, RowCallbackHandler rch)
    ②:query (String sql, Object[] args, RowCallbackHandler rch)
    ③:query (String sql, 0bject[] args, int[] argTypes, RowCallbackHandler rch)
    ④:query (String sql, RowCallbackHandler rch, 0bject... args)
    ⑤:query (String sql, PreparedStatementSetter pss, RowCa1lbackHandler rch)
    ⑥:query (PreparedStatementCreator psc, RowCallbackHandler rch)
    
    
    二:查询后返回List<T> 类型集合     List<T>
    ①:query (String sql, RowMapper<T> rowMapper )
    ②:query (String sql, 0bject[] args, RowMapper<T> rowMapper )
    ③:query (String sql, 0bject[] args, intl] argTypes, RowMapper<T> rowMapper )
    ④:query (String sql, RowMapper<T> rowMapper, 0bject.... args)
    ⑤:query (String sql, PreparedStatementSetter pss, RowMapper<T> rowMapper )
    ⑥:query (PreparedStatementCreator psc, RowMapper<T> rowMapper )
    
    
    三:返回自定义数据类型         T
    ①:query (String sql, ResultSetExtractor<T> rse )
    ②:query (String sql, 0bject[] args, ResultSetExtractor<T> rse)
    ③:query (String sql, 0bject[] args, int[] argTypes, ResultSetExtractor<T> rse )
    ④:query (String sql, ResultSetExtractor<T> rse, 0bject... args)
    ⑤:query (String sql, PreparedStatementSetter pss, ResultSetExtractor<T> rse)
    ⑥:query (PreparedStatementCreator psc, ResultSetExtractor<T> rse )
    ⑦:query (PreparedStatementCreator psc, PreparedStatementSetter pss, ResultSetExtractor<T> rse)
    对上面的方法进行3部分分类 属性介绍在开头和文章中都已经介绍过了

     1:一些常用的查询介绍

     ①:List<T> query(String sql , RowMapper<T> rowMapper)

     介绍:查询多行多列数据,但是查询的具体类型可以由RowMapper来映射出自己的实体类

    //查询全部数据 不懈怠参数
        public List<Student> findAll(){
            //查询全部数据  通过RowMapper的实现类来映射匹配
            List<Student> students = template.query("select * from student", new RowMapper<Student>() {
                public Student mapRow(ResultSet resultSet, int i) throws SQLException {
              //创建一个学生对象 然后挨个映射赋值 Student stu
    = new Student(); stu.setId(resultSet.getInt("sid")); stu.setName(resultSet.getString("sname")); stu.setSex(resultSet.getString("ssex")); stu.setAge(resultSet.getInt("sage")); stu.setCredit(resultSet.getDouble("scredit")); stu.setMoney(resultSet.getDouble("smoney")); stu.setAddress(resultSet.getString("saddress")); stu.setEnrol(resultSet.getString("senrol")); return stu; } }); return students; }

      但是我们只停留表面,其实RowMapper<T>还是挺强大的,我们可以把每一行数据转换为自定义key-value的map对象映射为RowMapper<Map<String,Object>>等一系列操作,由此会发现使用起来特别方便,像这种实现RowMapper<T>接口的匿名类,T可以为任意类型

        //查询全部数据 不懈怠参数
        public List<Map<String, Object>> findAll(){
            //查询全部数据  通过RowMapper的实现类来映射匹配
            List<Map<String, Object>> lits = template.query("select * from student limit 1", new RowMapper<Map<String, Object>>() {
                public Map<String, Object> mapRow(ResultSet resultSet, int i) throws SQLException {
                    Map<String, Object> map = new HashMap<String, Object>();
                    map.put("ID", resultSet.getInt("sid"));
                    map.put("姓名",resultSet.getString("sname"));
                    return map;
                }
            });
            return lits;
        }
    映射为RowMapper<Map<String,Object>>演示

    ②:void query(String  sql , RowCollbackHandler rch)

    介绍:这个query方法,如果内部使用了RowCollbackHandler回调类后,这个方法就变成了没有返回值,如果要获取数据必须在外部创建一个List对象,然后在回调类里面通过添加的方式来为list添加数据,这种操作也可以转换为任意类型,比如Map等等

    //查询全部数据 不携带参数
        public List<Student> findAll(){
            //创建一个List集合 用来存入查询的数据 (要使用final才可以在内部类中使用该变量)
            final List<Student> students=new ArrayList<Student>();
            //查询全部信息
            template.query("select * from student", new RowCallbackHandler() {
                public void processRow(ResultSet resultSet) throws SQLException {
                    //创建一个学生对象,因为实体类和字段不对应 ,我要每个字段都要映射
                    Student stu = new Student();
                    stu.setId(resultSet.getInt("sid"));
                    stu.setName(resultSet.getString("sname"));
                    stu.setSex(resultSet.getString("ssex"));
                    stu.setAge(resultSet.getInt("sage"));
                    stu.setCredit(resultSet.getDouble("scredit"));
                    stu.setMoney(resultSet.getDouble("smoney"));
                    stu.setAddress(resultSet.getString("saddress"));
                    stu.setEnrol(resultSet.getString("senrol"));
                    //把数据存入到带有final属性的list对象中
                    students.add(stu);
                }
            });
            return students;
        }

    ③:T query(String sql , ResultSetExtractor<T> rse)

    介绍:ResultSetExtractor使用回调方法extractData(ResultSet rs)提供给用户整个结果集,让用户来决定这个结果集,同理也可以封装为Map类型等等

    //查询全部数据 不携带参数   
        public List<Student> findAll() {
    //查询全部 这里注意如果ResultSetExtractor<T> 为什么类型,后面的返回结果也是指定这个类型 List
    <Student> students = template.query("select * from student", new ResultSetExtractor<List<Student>>() { public List<Student> extractData(ResultSet resultSet) throws SQLException, DataAccessException { //创建list集合用来存储Student List<Student> list = new ArrayList<Student>(); //遍历ResultSet结果集 while (resultSet.next()) { Student stu = new Student(); stu.setId(resultSet.getInt("sid")); stu.setName(resultSet.getString("sname")); stu.setSex(resultSet.getString("ssex")); stu.setAge(resultSet.getInt("sage")); stu.setCredit(resultSet.getDouble("scredit")); stu.setMoney(resultSet.getDouble("smoney")); stu.setAddress(resultSet.getString("saddress")); stu.setEnrol(resultSet.getString("senrol")); //存储添加Student list.add(stu); } //返回集合 return list; } }); //返回查询数据 return students; }

    ③:List<T> query(PreparedStatementCreator psc ,RowMapper<T> rowMapper)

    //查询全部数据 不携带参数
        public List<Student> findAll() {
            List<Student> students = template.query(new PreparedStatementCreator() {
                public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
                    return connection.prepareStatement("select * from student");
                }
            }, new RowMapper<Student>() {
                public Student mapRow(ResultSet resultSet, int i) throws SQLException {
                    //创建一个学生对象,因为实体类和字段不对应 ,我要每个字段都要映射
                    Student stu = new Student();
                    stu.setId(resultSet.getInt("sid"));
                    stu.setName(resultSet.getString("sname"));
                    stu.setSex(resultSet.getString("ssex"));
                    stu.setAge(resultSet.getInt("sage"));
                    stu.setCredit(resultSet.getDouble("scredit"));
                    stu.setMoney(resultSet.getDouble("smoney"));
                    stu.setAddress(resultSet.getString("saddress"));
                    stu.setEnrol(resultSet.getString("senrol"));
                    return stu;
                }
            });
            return students;
        }

    2:复杂的query查询

    这下面要介绍的查询稍微有点繁琐,但是看明白了就明了对query方法有一定的认识了,这下面的三个方法也是对应上面查询的复杂3大类,但是在平常用不到

     ①:query (PreparedStatementCreator psc, PreparedStatementSetter pss, ResultSetExtractor<T> rse)

    //查询全部数据 不携带参数   可对下面方法拆分为3部分回调函数,也和原生JDBC流程一样
        public List<Student> findAll() {
            //query (PreparedStatementCreator psc, PreparedStatementSetter pss, ResultSetExtractor<T> rse)
            List<Student> students = template.query(new PreparedStatementCreator() {
                //new PreparedStatementCreator匿名内部类  就像我们以前使用jdbc原生来执行sql语句
                public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
                    return connection.prepareStatement("select * from student where ssex=? limit ?");
                }
            }, new PreparedStatementSetter() {
                //new PreparedStatementSetter匿名内部类 此操作就像我们原生使用jdbc对占位符使用preparedStatement赋值
                public void setValues(PreparedStatement preparedStatement) throws SQLException {
                    preparedStatement.setString(1, "男");
                    preparedStatement.setInt(2, 5);
                }
            }, new ResultSetExtractor<List<Student>>() {
                //new ResultSetExtractor<T> 匿名内部类让用户自定义数据  就像原生jdbc我们操作resultSet一样
                public List<Student> extractData(ResultSet resultSet) throws SQLException, DataAccessException {
                    List<Student> list = new ArrayList<Student>();
                    while (resultSet.next()) {
                        Student stu = new Student();
                        stu.setId(resultSet.getInt("sid"));
                        stu.setName(resultSet.getString("sname"));
                        stu.setSex(resultSet.getString("ssex"));
                        stu.setAge(resultSet.getInt("sage"));
                        stu.setCredit(resultSet.getDouble("scredit"));
                        stu.setMoney(resultSet.getDouble("smoney"));
                        stu.setAddress(resultSet.getString("saddress"));
                        stu.setEnrol(resultSet.getString("senrol"));
                        list.add(stu);
                    }
                    return list;
                }
            });
            return students;
        }

    ②:List<T>  query (String sql, PreparedStatementSetter pss, RowMapper<T> rowMapper )

    //查询全部数据 不携带参数
        public List<Student> findAll() {
            //query (String sql, PreparedStatementSetter pss, RowMapper<T> rowMapper )
            //SQL语句,带有占位符
            String sql = "select * from student where ssex=? and sname like ?";
            //查询数据
            List<Student> students = template.query(sql, new PreparedStatementSetter() {
                public void setValues(PreparedStatement preparedStatement) throws SQLException {
                    preparedStatement.setString(1, "男");
                    preparedStatement.setString(2, "张%");
                }
            }, new RowMapper<Student>() {
                public Student mapRow(ResultSet resultSet, int i) throws SQLException {
                    //关系映射并返回映射好的对象
                    Student stu = new Student();
                    stu.setId(resultSet.getInt("sid"));
                    stu.setName(resultSet.getString("sname"));
                    stu.setSex(resultSet.getString("ssex"));
                    stu.setAge(resultSet.getInt("sage"));
                    stu.setCredit(resultSet.getDouble("scredit"));
                    stu.setMoney(resultSet.getDouble("smoney"));
                    stu.setAddress(resultSet.getString("saddress"));
                    stu.setEnrol(resultSet.getString("senrol"));
                    return stu;
                }
            });
            return students;
        }

    ③:void query (String sql, PreparedStatementSetter pss, RowCa1lbackHandler rch)

     //查询全部数据 不携带参数
        public List<Student> findAll() {
            //query (String sql, PreparedStatementSetter pss, RowCa1lbackHandler rch)
            //创建一个final类型的集合用来存储查询的数据
            final List<Student> students=new ArrayList<Student>();
            //SQL语句,带有占位符
            String sql = "select * from student where ssex=? and sname like ?";
            //查询  query不带返回值,只有通过内部数据存入到事先定义好的集合上才可获取
            template.query(sql, new PreparedStatementSetter() {
                public void setValues(PreparedStatement preparedStatement) throws SQLException {
                    preparedStatement.setString(1, "男");
                    preparedStatement.setString(2, "张%");
                }
            }, new RowCallbackHandler() {
                public void processRow(ResultSet resultSet) throws SQLException {
                    Student stu = new Student();
                    stu.setId(resultSet.getInt("sid"));
                    stu.setName(resultSet.getString("sname"));
                    stu.setSex(resultSet.getString("ssex"));
                    stu.setAge(resultSet.getInt("sage"));
                    stu.setCredit(resultSet.getDouble("scredit"));
                    stu.setMoney(resultSet.getDouble("smoney"));
                    stu.setAddress(resultSet.getString("saddress"));
                    stu.setEnrol(resultSet.getString("senrol"));
                    //把映射好的对象放入final修饰的集合上
                    students.add(stu);
                }
            });
            return students;
        }

     四:JdbcTemplate的细节使用

       在前面的操作CRUD操作已经介绍完了,其实用到的方法也就那么5种左右,其它的方法里面属性只是为了使程序有一个更好的拓展和灵活性,在接下来我讲为大家介绍JdbcTemplate的细节及技巧

    1:关以RowMapper<T>映射的问题

      大家在写带有RowMapper属性的时候,发现映射关系每次都要重写一下,如果有20个查询方法,那RowMapper就得写20次,这显然代码冗余了,那么怎么解决呢?因为RowMapper<T>原本就算接口,那么为什么我们不使用一个类继承它呢?接下来就看操作

    //编写RowMapper实现类
    class StudentRowMapper implements RowMapper<Student>{
        //Student属性
        private Student student;
        //重写方法
        public Student mapRow(ResultSet resultSet, int i) throws SQLException {
            student = new Student();
            student.setId(resultSet.getInt("sid"));
            student.setName(resultSet.getString("sname"));
            student.setSex(resultSet.getString("ssex"));
            student.setAge(resultSet.getInt("sage"));
            student.setCredit(resultSet.getDouble("scredit"));
            student.setMoney(resultSet.getDouble("smoney"));
            student.setAddress(resultSet.getString("saddress"));
            student.setEnrol(resultSet.getString("senrol"));
            return student;
        }
    }
    
    ####### 查询方法
    
    //查询全部数据 不携带参数
        public List<Student> findAll() {
            //查询方法  后面就不创建RowMapper的匿名内部类了,直接创建一个实现类
            List<Student> students = template.query("select * from student", new StudentRowMapper());
            return students;
        }

    2:关于创建JdbcTemplate对象问题

    public class StudentDaoImpl implements StudentDao {
        //聚合JdbcTemplate 后面的set注入
        private JdbcTemplate template;
        public void setTemplate(JdbcTemplate template) {
            this.template = template;
        }
      ...后面是一些增删改查的操作

    这是一个普通的创建方式,由applicationContext.xml配置文件来创建这个容器并注入数据

     <!--DriverManagerDataSource放入容器-->
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
            <property name="url" value="jdbc:mysql:///demo_school"></property>
            <property name="username" value="root"></property>
            <property name="password" value="123"></property>
        </bean>
        <!--JdbcTemplate放入容器-->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
        <!--StudentDao放入容器-->
        <bean id="studentDao" class="cn.xw.dao.impl.StudentDaoImpl">
            <property name="template" ref="jdbcTemplate"></property>
        </bean>

      我有个假设,如果现在的项目足够大,xxxDao实现类就有50多个,那么我们每个xxxDao的实现类里面都要包含private JdbcTemplate  template;属性吗?现在我们就是这样的,然后在配置文件里面注入50多次,这显然代码冗余,其实String也为我们想到了这个问题,所以我接下来就和大家说说吧!

    public class StudentDaoImpl extends JdbcDaoSupport implements StudentDao {}
    //①:说明:其实我们只需要继承这个类就可以完成了,那我们看看这到底是什么类
    
    
    public abstract class JdbcDaoSupport extends DaoSupport {
        @Nullable
        private JdbcTemplate jdbcTemplate;
        .....下面的方法暂时省略
    }
    //②说明:这不正是我们平常写的JdbcTemplate属性吗?那猜测肯定有get/set方法吧
    
    //③:废话不多说 我翻翻这个类JdbcDaoSupport
    public final void setJdbcTemplate(@Nullable JdbcTemplate jdbcTemplate){...}
    public final JdbcTemplate getJdbcTemplate() {...}
    //可以看出原来JdbcDaoSupport为我们提供了get/set,但是都使用final修饰,表示不可继承
    //我们就可以通过super.getJdbcTemplate()来得到一个JdbcTemplate对象,
    
    //④:那继承了就可以使用了吧?别忘了这个要修改一下注入,看看属性名是否匹配,因为是被继承
    //到StudentDaoImpl中,所以我直接在这里面注入
    //<!--StudentDao放入容器-->
        <bean id="studentDao" class="cn.xw.dao.impl.StudentDaoImpl">
            <property name="jdbcTemplate" ref="jdbcTemplate"></property>
        </bean>

    //后期在dao里面使用jdbcTemplate就使用getJdbcTemplate()获取

    .

  • 相关阅读:
    c++ 动态生成string类型的数组
    c++ string类型的定义及方法
    c++数字和字符串的转换
    c++ 怎么输出保留2位小数的浮点数
    c++中结构体sort()排序
    O(N)时间的排序
    复杂链表的复制
    反转链表
    判断是否为平衡二叉树
    学习笔记_过滤器详细_2(过滤器JavaWeb三大组件之一)
  • 原文地址:https://www.cnblogs.com/antLaddie/p/12859647.html
Copyright © 2020-2023  润新知