大家好,我是IT修真院成都分院第12期学员,一枚正直善良的java程序员。
今天给大家分享一下,修真院官网java任务一中会使用到的知识点:
JDBCTemplate和JDBC。
1. 背景介绍
最近在做任务一,按照任务步骤学习了JDBC,以及JDBCTemplate,自己总结了一下两者的相关知识点,对比了两者之间的关系。
2. 知识剖析
2.1什么是JDBC
JDBC, 全称为Java DataBase Connectivity, 它是一个面向对象的应用程序接口(API), 通过它可訪问各类关系数据库。JDBC也是java核心类库的一部分。
JDBC的最大特点是它独立于详细的关系数据库。JDBC API 中定义了一些Java类分别用来表示与数据库的连接(connections), SQL语句(SQL statements), 结果集(resultsets)以及其他的数据库对象, 使得Java程序能方便地与数据库交互并处理所得的结果。使用JDBC, 全部Java程序(包括Java applications , applets和servlet)都能通过SQL语句或存储在数据库中的过程(storedprocedures)来存取数据库。
要通过JDBC来存取某一特定的数据库。必须有对应的JDBC driver。它往往是由生产数据库的厂家提供,是连接JDBC API与详细数据库之间的桥梁。
通常,Java程序首先使用JDBCAPI来与JDBCDriver Manager交互,由JDBC Driver Manager加载指定的JDBC drivers, 以后就能够通过JDBCAPI来存取数据库。
Java程序员在工作中很重要的一点就是得和数据库打交道,但是java代码和数据库是两个不相干的,这个时候就用到JDBC,来使用代码对数据库进行操作。
首先讲JDBC肯定离不开DAO(DataAccess Object)层,而JDBC的原理就是通过Connection对象获取数据库的连接,然后通过PreparedStatement对象处理SQL语句,再通过它的.setObject方法传入数据,最后通过方法.executeUpdate()和.executeQuery()执行更新和查询,这就是JDBC的基本原理。
可以看到,JDBC涉及到了连接数据库、然后处理SQL语句、然后传值,最后释放资源这几个过程,可以说任何通过java代码对数据库的操作都离不开这几个过程。
JDBC访问数据库的步骤
(1)得到数据库驱动程序
(2)创建数据库连接
(3)执行SQL语句
(4)得到结果集
(5)对结果集做相应的处理(增,删,改,查)
(6)关闭资源:这里释放的是DB中的资源
2.2什么是JDBC Template?
JdbcTemplate是Spring框架自带的对JDBC操作的封装,目的是提供统一的模板方法使对数据库的操作更加方便、友好,效率也不错。但是功能还是不够强大(比如不支持级联属性),在实际应用中还需要和hibernate、mybaties等框架混合使用。
JDBCTemplate就是Spring对JDBC的封装,通俗点说就是Spring对jdbc的封装的模板,那么JDBCTemplate怎么做到的,这就和spring有关系了
SpringIOC容器将管理数据库连接的数据源当作普通Java Bean一样管理,然后将数据源注入封装类JdbcTemplate中,JdbcTemplate的dataSource属性就是注入配置的数据源
首先JDBCTemplate是涉及到spring框架的一种方法,JDBCTemplate就是通过.xml配置文件极大的简化了我们代码量,并且提高了安全性。 通过配置文件实现代码简化
其实和JDBC的思路是大同小异的。
首先我们需要在接口实现类中拥有一个set方法,这个是使用JDBCTemplate一定要注意的,没有会报错,而这个set方法就是用来连接数据库的。然后创建DataSource对象和JDBCTemplate对象,分别是用来连接数据库和处理SQL语句的。最后使用JDBCTemplate对象的.update方法和.query方法分别实现插入和查找。
讲完原理,还有配置文件的功用还没讲。
配置文件中的东西不多,一个是DataSource对象的bean,另一个是接口实现类的bean并且和DataSource的bean做了引用。最后在测试类中(就是驱动函数中),我们需要用ApplicationContext对象存储配置文件的路径,该路径通过ClassPathXmlApplicationContext得到,
然后用ApplicationContext对象的.getBean方法连接到配置文件里的接口实现类的bean,然后就使用方法了.
可以看到JDBCTemplate其实对JDBC的获取数据库连接的Connection对象做了封装,提高了整体安全性,并且减少了大量冗余代码,提高工作效率;插入、删除等操作时也不用像JDBC一个一个字段进行操作了,极大简化代码量,减少失误;并且摒弃了TryCatch块,更加安全了。
2.3三种种方式得到JDBCTemplate对象
要使用JDBCTemplate对象来完成JDBC操作。通常情况下,有三种种方式得到JDBCTemplate对象。
(1)我们可以在自己定义的DAO 实现类中注入一个DataSource引用来完成JdbcTemplate 的实例化。也就是它是从外部“注入”DataSource 到DAO 中,然后自己实例化JdbcTemplate,然后将DataSource设置到JdbcTemplate对象中。
(2)在 Spring的 IoC 容器中配置一个JdbcTemplate 的 bean,将 DataSource 注入进来,然后再把JdbcTemplate 注入到自定义DAO 中。
(3)Spring 提供了org.springframework.jdbc.core.support.JdbcDaoSupport 类,这个类中定义了 JdbcTemplate 属性,也定义了DataSource属性,当设置DataSource属性的时候,会创建jdbcTemplate的实例,所以我们自己编写的DAO 只需要继承JdbcDaoSupport类,然后注入DataSource 即可
2.4JdbcTemplate类主要提供以下几类方法:
(1)execute方法:用于执行任何SQL语句,一般用于执行DDL语句;
(2)update方法及batchUpdate方法:update方法用于执行新增、修改、删除等语句,batchUpdate方法用于执行批处理相关语句;
(3)query方法及queryForXXX方法:用于执行查询相关语句;
3. 常见问题
3.1 JDBCTemplate和JDBC的优缺点
3.2 PreparedStatement和Statement有什么区别?
4. 解决方案
4.1 JDBCTemplate和JDBC的优缺点
JDBC
优点:运行期:快捷、高效
缺点:编辑器:代码量大、繁琐异常处理、不支持数据库跨平台
JDBCTemplate
优点:运行期:高效、内嵌Spring框架中、支持基于AOP的声明式事务
缺点:必须于Spring框架结合在一起使用、不支持数据库跨平台、默认没有缓存
4.2 PreparedStatement和Statement有什么区别
Statement每次执行SQL语句,相关数据库都要进行SQL语句编译,PreparedStatement是预编译,对批量处理大大提高效率,可以防止SQL注入攻击,防止数据库缓冲池溢出,代码的可读性,可维护性都更好
5. 编码实战
下面是使用JDBCTemplate的一个例子
5.1定义一个简单类Student,并定义它的三个属性age、name、id,并定义好各属性的set与get方法,方便以后调用
package com.example;
/*
定义一个简单类Student,并定义它的三个属性age、name、id,并定义好各属性的set与get方法,方便以后调用
*/
public class Student {
private Integer age;
private String name;
private Integer id;
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
}
5.2定义接口StudentDAO,并创建后面需要使用的方法
package com.example;
import javax.sql.DataSource;
import java.util.List;
/*
定义接口StudentDAO,并创建后面需要使用的方法
*/
public interface StudentDAO {
public void setDataSource(DataSource ds);//这个方法用于连接数据库,并完成初始化
public void create(String name,Integer age);//这个方法用于录入数据
public Student getStudent(Integer id);//这个方法用于获得表中对应id的记录
public List<Student> listStudents();//这个方法用于列出表中所有记录
public void delete(Integer id);//这个方法用于删除表中对应id的记录
public void update(Integer id, Integer age);//这个方法用于更新表中的记录
}
5.3建立Student的映射器StudentMapper,它用于实现RowMapper接口,RowMapper接口用来把数据库中的列字段和java bean中属性对应上,以方便赋值
package com.example;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
/*
建立Student的映射器StudentMapper,它用于实现RowMapper接口,RowMapper接口用来把数据库中的列字段和java bean中属性对应上,
以方便赋值
*/
public class StudentMapper implements RowMapper<Student> {
public Student mapRow(ResultSet rs, int rowNum) throws SQLException {
//实例化Student对象student,以便调用Student类下个属性的set方法来给各属性赋值
Student student = new Student();
student.setId(rs.getInt("id"));
student.setName(rs.getString("name"));
student.setAge(rs.getInt("age"));
//返回student对象
return student;
}
}
5.4定义一个实现类StudentJDBCTemplate来实现前面定义的StudentDAO接口
package com.example;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
import java.util.List;
/*
定义一个实现类StudentJDBCTemplate来实现前面定义的StudentDAO接口
*/
public class StudentJDBCTemplate implements StudentDAO {
//定义了 StudentJDBCTemplate类的一个属性dataSource
private DataSource dataSource;
//定义了 StudentJDBCTemplate类的一个属性jdbcTemplateObject
private JdbcTemplate jdbcTemplateObject;
//定义了属性dataSource的set方法,并初始化属性dataSource的值为dataSource
public void setDataSource(DataSource dataSource){
this.dataSource = dataSource;
// 实例化了JdbcTemplate类的一个带参数(dataSource)的对象,并将让jdbcTemplateObject指向此对象
//必须传入对应参数才能调用此有参构造创建的对象
this.jdbcTemplateObject = new JdbcTemplate(dataSource);
}
public void create(String name, Integer age) {
String SQL = "insert into Student (name, age) values (?, ?)";
jdbcTemplateObject.update( SQL, name, age);
System.out.println("Created Record Name = " + name + " Age = " + age);
return;
}
public Student getStudent(Integer id) {
String SQL = "select * from Student where id = ?";
Student student = jdbcTemplateObject.queryForObject(SQL,
new Object[]{id}, new StudentMapper());
return student;
}
public List<Student> listStudents() {
String SQL = "select * from Student";
List <Student> students = jdbcTemplateObject.query(SQL,
new StudentMapper());
return students;
}
public void delete(Integer id){
String SQL = "delete from Student where id = ?";
jdbcTemplateObject.update(SQL, id);
System.out.println("Deleted Record with ID = " + id );
return;
}
public void update(Integer id, Integer age){
String SQL = "update Student set age = ? where id = ?";
jdbcTemplateObject.update(SQL, age, id);
System.out.println("Updated Record with ID = " + id );
return;
}
}
5.5测试类:
public class MainApp {
public static void main(String[] args) {
//1.创建spring的IOC容器对象context,并加载配置文件
ApplicationContext context =
new ClassPathXmlApplicationContext("Beans.xml");
//2.从IOC容器中获取Bean实例
StudentJDBCTemplate studentJDBCTemplate =
(StudentJDBCTemplate)context.getBean("studentJDBCTemplate");
//3.调用对象的方法
//录入数据
System.out.println("------Records Creation--------" );
studentJDBCTemplate.create("小明", 22);
studentJDBCTemplate.create("刘备", 45);
studentJDBCTemplate.create("赵云", 26);
//查询记录并将结果保存在结果集中
System.out.println("------Listing Multiple Records--------" );
List<Student> students = studentJDBCTemplate.listStudents();
for (Student record : students) {
System.out.print("ID : " + record.getId() );
System.out.print(", Name : " + record.getName() );
System.out.println(", Age : " + record.getAge());
}
//更新记录并将结果保存在结果集中
System.out.println("----Updating Record with ID = 2 -----" );
studentJDBCTemplate.update(2, 20);
System.out.println("----Listing Record with ID = 2 -----" );
Student student = studentJDBCTemplate.getStudent(2);
System.out.print("ID : " + student.getId() );
System.out.print(", Name : " + student.getName() );
System.out.println(", Age : " + student.getAge());
}
}
5.6bean.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 初始化数据来源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/my_db"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <!-- 定义StudentJDBCTemplate的bean --> <bean id="studentJDBCTemplate" class="com.example.StudentJDBCTemplate"> <property name="dataSource" ref="dataSource" /> </bean> </beans>
6. 扩展思考
mybatis与jdbc、JDBCTemplate三者有什么区别?
MyBatis也是对JDBC的封装,MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装。MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(普通的Java对象)映射成数据库中的记录。
使用mapper代理的方法来开发dao
1.编写mapper.xml映射文件
2.编写mapper接口(相当于dao接口).不需要写具体实现类,只需要写接口即可,但是接口不能直接使用
7. 参考文献
https://blog.youkuaiyun.com/u011305680/article/details/60879953
https://www.cnblogs.com/love-Stefanie/p/6838269.html
https://blog.youkuaiyun.com/u014282557/article/details/76761403
https://www.cnblogs.com/tuhooo/p/6491913.html
https://blog.youkuaiyun.com/lxlmycsdnfree/article/details/73287396
https://blog.youkuaiyun.com/qq_32671287/article/details/70212584
8. 更多讨论
(1)使用 jdbc.properties配置文件有什么好处?
假如我们需要更改连接数据库的配置,我们直接在jdbc.properties里面更改一次就可以了,提高了工作效率,减少了重复代码,适应不同的数据库连接,而JDBC的话需要增删改查里面每个连接数据库的配置都需要更改。
(2)如果发生数据库连接资源被占用,有什么解决方法
修改 MySQL 安装目录下 my.ini 或者 my.cnf 文件内的max_user_connections 参数的数值,重启 MySQL 服务器。
(3)jdbctemplate和mybatis分别在什么情况下使用,jdbc template 和spring+mybatis一样吗?,和mybatis相比有什么优缺点?
这三个问题其实差不多,JDBCTemplate和Mybatis不一样,Mybatis的sql语句放在了mapper.xml文件里面.相当于配置在了环境当中,随时都可以使用. 而JDBCTemplate则是写在了实现类里面,使用的时候还要进行重新调用配置之类的. 以后的工作中可能对多个表进行操作,配置在环境中使用起来非常方便.
(4)如何来控制数据库的打开关闭.
Class.forName("com.mysql.jdbc.Driver");这个初始化驱动就是打开了数据库链接
Connectionc = DriverManager.//连接数据库,
//关闭数据链接
ps.close();
c.close();
而JDBCTemplate的打开关闭则是封装在JDBCTemplate当中。
(5)jdbc 和 jdbctemplate 相比 效率有差别么
单从效率方面来说,实际上jdbc比jdbctemplate的效率会高一点.jdbcTemplate则更方便我们进行更改内容,和理解当中的逻辑。
(6)jdbcTemplate 实现执行多条sql语句。
一种直接用mysql的批量处理语句.
另外一种拼接SQL语句直接一次性提交多少条数据
(7)jdbcTemplate有什么缺点。
必须于Spring框架结合在一起使用、不支持数据库跨平台、默认没有缓存。
(8)连接池
1).当有多个线程,每个线程都需要连接数据库执行SQL语句的话,那么每个线程都会创建一个连接,并且在使用完毕后,关闭连接。创建连接和关闭连接的过程也是比较消耗时间的,当多线程并发的时候,系统就会变得很卡顿。同时,一个数据库同时支持的连接总数也是有限的,如果多线程并发量很大,那么数据库连接的总数就会被消耗光,后续线程发起的数据库连接就会失败。
2.).连接池在使用之前,就会创建好一定数量的连接。 如果有任何线程需要使用连接,那么就从连接池里面借用,而不是自己重新创建. 使用完毕后,又把这个连接归还给连接池供下一次或者其他线程使用。倘若发生多线程并发情况,连接池里的连接被借用光了,那么其他线程就会临时等待,直到有连接被归还回来,再继续使用。整个过程,这些连接都不会被关闭,而是不断的被循环使用,从而节约了启动和关闭连接的时间。