zoukankan      html  css  js  c++  java
  • 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()获取

    .

  • 相关阅读:
    从零开始入门 K8s | 应用编排与管理
    209. Minimum Size Subarray Sum
    208. Implement Trie (Prefix Tree)
    207. Course Schedule
    203. Remove Linked List Elements
    183. Customers Who Never Order
    182. Duplicate Emails
    181. Employees Earning More Than Their Managers
    1261. Find Elements in a Contaminated Binary Tree
    1260. Shift 2D Grid
  • 原文地址:https://www.cnblogs.com/antLaddie/p/12859647.html
Copyright © 2011-2022 走看看