这里也比较重要,因为这是方便的写法👨
第一章 数据库连接池
1.1 概述
引入案例: 我们开了一家餐馆,每当我们要给客人服务的时候(操作sql),我们就招聘一名服务员(getConnection),但是每当我们服务完毕了,我们却总是会解雇这个服务员(关闭资源),下次再有客人来的时候,我们再重新雇一名服务员,如此反复,简直离谱。
而我们餐馆正常逻辑就是 提前故几个服务员来一直服务
因此,我们需要一个容器,来提前保存连接对象,而这个东西就叫做数据库线程池
数据库连接池: 其实就是一个容器(集合),存放数据库连接的容器
- 当系统初始化好之后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库的时候,从容器中获取连接对象;用户访问完之后,就将连接对象归还
优点
- 节约资源
- 用户访问高效(如果自己getConnection的话就要对底层获取资源,比较没有效率)
基本实现
-
使用标准接口javax.sql.DataSource(接口的实现类由数据库的厂商提供),对于DriverManager设备的替代方案,这个接口是获取连接的首选方法
-
方法:
//获取连接 Connection getConnection() 尝试建立与此 DataSource对象所代表的数据源的连接。 Connection getConnection(String username, String password) 尝试建立与此 DataSource对象所代表 //归还连接:如果连接对象是从连接池中获取,那么调用Connection.close()方法,就不会再关闭连接,而是归还连接 connecion.close();
-
-
有下面几种常用的数据库连接池技术
- C3P0: 比较老
- Druid(德鲁伊): 比较新的数据库连接池实现技术,由阿里巴巴提供的,非常高效,号称全世界最好的数据库连接池技术之一
1.2 C3P0技术
使用步骤:
- 导入jar包: c3p0-0.9.5.5-sources.jar 和 mchange-commons-java-0.2.19-sources.jar (注意,数据库的jar包也记得要导)
- 定义配置文件
- 名称:c3p0.peoperties 或者 c3p0-config.xml
- 路径: src源目录文件下
- 创建核心对象: 数据库连接池对象: new ComboPooledDataSource()
- 获取连接: getConnection()
1.3 Druid技术
使用步骤
- 导入jar包
- 定义配置文件:
- 可以叫做任意名称,可以放在任意目录下,我们要手动读取这个配置文件
- 获取数据库连接池对象: 通过工厂类 DruidDataSourceFactory
- 获取连接: getConnection()
配置文件
url = jdbc:mysql:///cjy?serverTimezone=GMT%2B8
username = root
password = root
initialSize = 5
maxActive = 10
maxWait = 3000
小试牛刀
import com.alibaba.druid.pool.DruidDataSourceFactory;
import demo12.util.JDBCUtils;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;
public class DataSource {
public static void main(String[] args) throws Exception {
Properties pro = new Properties();
ClassLoader cll = DataSource.class.getClassLoader();
InputStream rs = cll.getResourceAsStream("druid.properties");
pro.load(rs);
//创建连接对象,这里参数要一个Properties
javax.sql.DataSource dataSource = DruidDataSourceFactory.createDataSource(pro);
Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement();
String sql = "select * from account";
// 执行sql
ResultSet res = stmt.executeQuery(sql);
//7 处理结果
while (res.next()){
System.out.println(res.getInt(1) + " \t" + res.getString(2) + "\t" + res.getDouble(3));
}
//8 释放资源
JDBCUtils.close(stmt,conn);
}
}
//处理结果
4月 22, 2021 11:39:13 上午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-1} inited
1 zhangsan 2000.0
2 lisi 6000.0
1.4 Druid工具类
通过上面的小试牛刀,我们发现这个过程还是有点麻烦,所以我们打算通过自己写一个改良版的工具类来简化这个过程.
步骤
- 定义一个工具类
- 开头要写一个静态代码块加载配置文件,同时初始化连接池对象
- 里面包含的方法就是获取连接对象,释放资源和获取连接池对象的方法
我写的工具类
package demo12.util;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* JDBC工具类
*/
public class JDBCDruidUtils {
private static final Properties pro;
private static DataSource dataSource;
//配置文件的读取,只需要一次就可以了,用静态代码块
static {
pro = new Properties();
ClassLoader cl = JDBCDruidUtils.class.getClassLoader();
InputStream is = cl.getResourceAsStream("druid.properties");
try {
pro.load(is);
} catch (IOException e) {
e.printStackTrace();
}
//初始化连接池对象
try {
dataSource = DruidDataSourceFactory.createDataSource(pro);
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接对象
public static Connection getConnection() throws Exception {
return dataSource.getConnection();
}
//关闭资源
public static void close(Statement stmt, Connection conn) {
judge(stmt, conn);
}
public static void judge(Statement stmt, Connection conn) {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
//获取连接池的方法
public static DataSource getDataSource(){
return dataSource;
}
}
代码测试
package demo13;
import demo12.util.JDBCDruidUtils;
import demo12.util.JDBCUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
public class DataSource {
public static void main(String[] args) throws Exception {
Connection conn = JDBCDruidUtils.getConnection();
Statement stmt = conn.createStatement();
String sql = "select * from account";
// 执行sql
ResultSet res = stmt.executeQuery(sql);
//7 处理结果
while (res.next()){
System.out.println(res.getInt(1) + " \t" + res.getString(2) + "\t" + res.getDouble(3));
}
//8 释放资源
JDBCUtils.close(stmt,conn);
}
}
//运行结果
4月 22, 2021 12:38:17 下午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-1} inited
1 zhangsan 2000.0
2 lisi 6000.0
我们会发现步骤简化了很多
第二章 JDBC Spring
2.1 简述
Spring框架对JDBC的简单封装. 提供了一个JDBCTemplate对象简化了JDBC的开发
步骤
-
导入jar包
-
创建JDBCTemplate对象.这个对象依赖于数据源DataSource
JdbcTemplate temp = new JdbcTemplate(datesource)
-
调用JdbcTemplate的方法来万册灰姑娘CRUD操作
-
update();执行DML语句. queryForMap(): 查询结果将结果封装为Map集合(将列名作为key,将值作为values,封装为一个map集合)这个方法只能存取一个记录 queryForList():查询结果并将结果封装为list集合 (里面存取多个map) query():查询结果,并将结果封装为JavaBean对象 (一般用于将数据封装到我们自己创建的对象里面,然后再返回一个List<我们创建的对象>) queryForObject():查询结果,并将结果封装为Object对象 (一般用于聚合函数)
-
2.2 快速入门
import demo12.util.JDBCDruidUtils;
import org.springframework.jdbc.core.JdbcTemplate;
public class JDBCTemplate {
public static void main(String[] args) {
//创建jdbctemplate对象
JdbcTemplate temp = new JdbcTemplate(JDBCDruidUtils.getDataSource());
//他使用的是PreparedStatement对象
String sql = "update account set balance = 5000 where id = ?";
int update = temp.update(sql, 2);
System.out.println(update);
//不需要释放资源,他会自动释放掉
}
//运行结果 1
//我们会发现用了Template + 我们自己写的工具类,语句既高效又简单,同时也不会发生注入问题等,简直无敌朋友们
2.3 DML语句练习
需求
对于下面这个表
-
我们修改一号数据的分数为100
-
public class JDBCTemplate { public static void main(String[] args) { //创建jdbctemplate对象 JdbcTemplate temp = new JdbcTemplate(JDBCDruidUtils.getDataSource()); //他使用的是PreparedStatement对象 String sql = "update stu set score = 100.0 where id = ?"; int update = temp.update(sql, 1); System.out.println(update); //不需要释放资源,他会自动释放掉 } } //运行结果 2 //查看表格,我们会发现内容被正确修改
-
-
添加一条记录(随便什么内容都行)
-
import demo12.util.JDBCDruidUtils; import org.springframework.jdbc.core.JdbcTemplate; public class JDBCTemplate { public static void main(String[] args) { //创建jdbctemplate对象 JdbcTemplate temp = new JdbcTemplate(JDBCDruidUtils.getDataSource()); //他使用的是PreparedStatement对象 String sql = "insert into stu (id,name,age,score) values(1,'老铁双击666',66,66)"; int update = temp.update(sql); System.out.println(update); //不需要释放资源,他会自动释放掉 } } //运行结果 1 结果在数据表已经成功被创建出来
-
-
删除刚才添加的记录
-
package demo13; import demo12.util.JDBCDruidUtils; import org.springframework.jdbc.core.JdbcTemplate; public class JDBCTemplate { public static void main(String[] args) { //创建jdbctemplate对象 JdbcTemplate temp = new JdbcTemplate(JDBCDruidUtils.getDataSource()); //他使用的是PreparedStatement对象 String sql = "delete from stu where id = ? and name = ?"; int update = temp.update(sql,1,"老铁双击666"); System.out.println(update); //不需要释放资源,他会自动释放掉 } } //运行结果 1 数据已经成功被删除
-
2.4 DQL语句练习
承接上面的练习
-
查询id为2的记录,将其封装为Map集合 (注意,这个方法,我们只能返回一个结果)
-
package demo13; import demo12.util.JDBCDruidUtils; import org.springframework.jdbc.core.JdbcTemplate; import java.util.Map; public class JDBCTemplate { public static void main(String[] args) { //创建jdbctemplate对象 JdbcTemplate temp = new JdbcTemplate(JDBCDruidUtils.getDataSource()); //他使用的是PreparedStatement对象 String sql = "select * from stu where id =?"; Map<String, Object> res = temp.queryForMap(sql, 2); System.out.println(res); //不需要释放资源,他会自动释放掉 } } //运行结果 {id=2, name=儿子, age=11, score=88.0, birthday=2020-11-11, insert_time=2021-04-16 16:38:39.0, sex=男, address=null}
-
-
查询所有的记录,将其封装为list (返回两条以上的记录的时候,我们就要用到list集合来存储多个map结果)
-
package demo13; import demo12.util.JDBCDruidUtils; import org.springframework.jdbc.core.JdbcTemplate; import java.util.List; import java.util.Map; public class JDBCTemplate { public static void main(String[] args) { //创建jdbctemplate对象 JdbcTemplate temp = new JdbcTemplate(JDBCDruidUtils.getDataSource()); //他使用的是PreparedStatement对象 String sql = "select * from stu"; List<Map<String, Object>> res = temp.queryForList(sql); System.out.println(res); //不需要释放资源,他会自动释放掉 } } //运行结果 4月 22, 2021 5:11:24 下午 com.alibaba.druid.pool.DruidDataSource info 信息: {dataSource-1} inited [{id=2, name=儿子, age=11, score=88.0, birthday=2020-11-11, insert_time=2021-04-16 16:38:39.0, sex=男, address=null}, {id=1, name=爸爸, age=20, score=100.0, birthday=2000-11-19, insert_time=2021-04-17 19:21:23.0, sex=男, address=杭州}, {id=3, name=爷爷, age=88, score=66.0, birthday=1920-11-11, insert_time=2021-04-17 19:21:29.0, sex=男, address=深圳}, {id=4, name=孙子, age=1, score=0.0, birthday=2020-11-11, insert_time=2021-04-17 19:21:32.0, sex=男, address=香港}, {id=1, name=爸爸, age=20, score=100.0, birthday=2000-11-19, insert_time=2021-04-17 19:21:23.0, sex=男, address=杭州}, {id=5, name=你爹, age=20, score=66.0, birthday=1998-06-11, insert_time=2021-04-17 19:21:44.0, sex=男, address=南京}, {id=6, name=妈妈, age=20, score=89.0, birthday=1965-06-11, insert_time=2021-04-17 19:21:58.0, sex=女, address=北京}, {id=7, name=奶奶, age=88, score=99.0, birthday=1945-06-11, insert_time=2021-04-22 16:49:54.0, sex=女, address=广州}, {id=8, name=ouMygod, age=28, score=100.0, birthday=2021-04-30, insert_time=2021-04-22 16:39:59.0, sex=无, address=湖南}]
-
-
查询所有记录,将其封装为stu对象的list集合
懒得创建stu对象了,就不演示了哈哈哈哈哈哈哈哈哈哈
这里我直接截图了(例子和我的不太一样,这里主要使用query方法来实现这个,其他方法实现不了)
这里RowMapper的作用就是使用将查询得到的Result对象通过我们自己的重写方法,将数据都存放到我们自己的对象里面,然后返回这个对象
所以说
query()
方法还是返回一个list集合,里面存放的就是我们创建的对象 同时,我们注意到,刚才那个方法太麻烦了,我们总是要获取值,然后又set进我们的属性里面,十分的麻烦,所以,其实java提供了一个实现类,我么通过这个实现类可以直接完成这个复杂的过程(前提是类中的成员变量和表格的成员变量完全一致,同时里面要有getter和setter方法)
代码就变成了这么一小段,但是结果是一样的,原因就是我们使用了BeanProertiesRowMapper类,他是RowMapper类
的子类
-
查询表的总记录数(我们使用QueryForObject()方法)
-
import demo12.util.JDBCDruidUtils; import org.springframework.jdbc.core.JdbcTemplate; public class JDBCTemplate { public static void main(String[] args) { //创建jdbctemplate对象 JdbcTemplate temp = new JdbcTemplate(JDBCDruidUtils.getDataSource()); //他使用的是PreparedStatement对象 String sql = "select count(id) from stu"; Long res = temp.queryForObject(sql, Long.class);//这里传进去一个字节码文件,可以决定返回值类型 System.out.println(res); //不需要释放资源,他会自动释放掉 } } //运行结果 9
-