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()获取
.
Spring之JdbcTemplate使用的更多相关文章
- Spring利用JDBCTemplate实现批量插入和返回id
1.先介绍一下java.sql.Connection接口提供的三个在执行插入语句后可取的自动生成的主键的方法: //第一个是 PreparedStatement prepareStatement(St ...
- Spring 中jdbcTemplate 实现执行多条sql语句
说一下Spring框架中使用jdbcTemplate实现多条sql语句的执行: 很多情况下我们需要处理一件事情的时候需要对多个表执行多个sql语句,比如淘宝下单时,我们确认付款时要对自己银行账户的表里 ...
- spring使用jdbcTemplate和jdbcdaosupport和namedparameter
jdbcTemplate: 首先连接数据库 <!-- 导入外部文件 --> <context:property-placeholder location="classpat ...
- Spring之JDBCTemplate学习
一.Spring对不同的持久化支持: Spring为各种支持的持久化技术,都提供了简单操作的模板和回调 ORM持久化技术 模板类 JDBC org.springframework.jdbc.core. ...
- spring+spring mvc+JdbcTemplate 入门小例子
大家使用这个入门时候 最好能够去 搜一下 spring mvc 的 原理,我放一张图到这里,自己琢磨下,后面去学习就容易了 给个链接 (网上一把,千万不能懒) https://www.cnblo ...
- spring 学习(四): spring 的 jdbcTemplate 操作
spring 学习(四): spring 的 jdbcTemplate 操作 spring 针对 javaee 的每一层,都提供了相应的解决技术,jdbcTemplate 的主要操作在 dao 层. ...
- Spring的jdbcTemplate 与原始jdbc 整合c3p0的DBUtils 及Hibernate 对比 Spring配置文件生成约束的菜单方法
以User为操作对象 package com.swift.jdbc; public class User { private Long user_id; private String user_cod ...
- spring学习(四)spring的jdbcTemplate(增删改查封装)
Spring的jdbcTemplate操作 1.Spring框架一站式框架 (1)针对javaee三层,每一层都有解决技术 (2)到dao 层,使用 jdbcTemplate 2.Spring对不同的 ...
- Spring:JdbcTemplate使用指南
Spring:JdbcTemplate使用指南 Spring:JdbcTemplate使用指南 前言: 本文指在介绍Spring框架中的JdbcTemplate类的使用方法,涉及基本的Spring反转 ...
- Spring中JdbcTemplate的基础用法
Spring中JdbcTemplate的基础用法 1.在DAO中使用JdbcTemplate 一般都是在DAO类中使用JdbcTimplate,在XML配置文件中配置好后,可以在DAO中注入即可. 在 ...
随机推荐
- Linux --如何新增一块硬盘并自动挂载
1. 虚拟机添加硬盘 2. 分区 fdisk /dev/sdb 3. 格式化 mkfs -t ext4 /dev/sdb1 将刚刚创建的盘格式化成 ext4格式 4. 挂载 先创建一个目录,/hom ...
- VS2019 使用
下载 官网下载:链接 安装 1.点击下载程序,会显示这个界面 2.点击“继续”,等待安装程序安装完成 4.安装程序下载安装验证完毕,将会提示进入这个界面 5.为了方便起见,这里仅展示安装C++功能,在 ...
- 基于 abp vNext 和 .NET Core 开发博客项目 - 完善与美化,Swagger登场
上一篇文章(https://www.cnblogs.com/meowv/p/12896898.html)已经成功将博客项目跑起来了,那么本篇主要是将之前遗留的问题解决,现在的代码看起来可能还是比较混乱 ...
- JS异步之宏队列与微队列
1. 原理图 2. 说明 JS 中用来存储待执行回调函数的队列包含 2 个不同特定的列队 宏列队:用来保存待执行的宏任务(回调),比如:定时器回调.DOM 事件回调.ajax 回调 微列队:用来保存待 ...
- 关于RAID小结
独立硬盘冗余阵列(RAID, Redundant Array of Independent Disks),旧称廉价磁盘冗余阵列(Redundant Array of Inexpensive Disks ...
- 4.2 Go switch
4.2 Go switch switch语句用于基于不同条件执行不同动作,每一个case分支唯一,自上而下逐一测试,直到匹配结束,默认自动终止,不需要break. 2. switch基本语法 swit ...
- 团队作业-Beta冲刺 (第一天)
这个作业属于哪个课程 <课程的链接> 这个作业要求在哪里 <作业要求的链接> 团队名称 RTD <团队博客链接> 这个作业的目标 剩余任务预估,分配任务(开发,测试 ...
- Docker 错误:network xxx id xxxx has active endpoints
问题描述:Docker Compose 部署的项目,使用docker-compose down 命令关闭时,提示错误: Removing network xxxl_default ERROR: net ...
- Kafka SSL安装与配置
1.概述 最近有同学咨询说,Kafka的SSL安全认证如何安装与使用?今天笔者将通过以下几个方面来介绍Kafka的SSL: Kafka 权限介绍 Kafka SSL的安装与使用 Kafka Eagle ...
- CSS3和HTML5头部定位自用
body{ max-width: 540px; min-width: 320px; margin: 0 auto; font: normal 14px/1.5 tahoma; color: #000; ...