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 

Spring之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坐标
Spring之JdbcTemplate使用
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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
Spring之JdbcTemplate使用
#####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资料

Spring之JdbcTemplate使用

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);
    }

⑤:删、改简单演示

Spring之JdbcTemplate使用
//删除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:批量删除、更新、添加

Spring之JdbcTemplate使用

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

Spring之JdbcTemplate使用
    //批量添加数据
    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);
    }
完成批量添加数据
Spring之JdbcTemplate使用
 //批量更新数据
    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);
    }
完成批量更新操作
Spring之JdbcTemplate使用
//完成批量删除
    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方法之介绍 

Spring之JdbcTemplate使用

Spring之JdbcTemplate使用
①: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方法之介绍

Spring之JdbcTemplate使用

Spring之JdbcTemplate使用
①: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方法之介绍(用的最多,也是要掌握的)

Spring之JdbcTemplate使用

Spring之JdbcTemplate使用
一:查询无返回值   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可以为任意类型

Spring之JdbcTemplate使用
    //查询全部数据 不懈怠参数
    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()获取

.

Spring之JdbcTemplate使用

上一篇:indexDB出坑指南


下一篇:android gridview 九宫格布局,膜拜