JDBC高级(连接池&自定义JDBC框架)
数据库连接池
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。这项技术能明显提高对数据库操作的性能.
作用:避免重复创建连接.
C3P0数据库连接池
-
导入 jar 包。
c3p0-0.9.5.2.jar
mchange-commons-java-0.2.12.jar
-
导入配置文件到 src 目录下。
-
创建 C3P0 连接池对象。
-
获取数据库连接进行使用。
注意:C3P0 的配置文件会自动加载,但是必须叫 c3p0-config.xml 或 c3p0-config.properties 。
C3P0配置文件
<c3p0-config>
<!-- 使用默认的配置读取连接池对象 -->
<default-config>
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/db14</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<!--初始化的连接数量-->
<property name="initialPoolSize">5</property>
<!--最大连接数量-->
<property name="maxPoolSize">10</property>
<!--超时等待时间-->
<property name="checkoutTimeout">3000</property>
</default-config>
<named-config name="otherc3p0">
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/db15</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">8</property>
<property name="checkoutTimeout">1000</property>
</named-config>
</c3p0-config>
C3P0小案例
public class Demo1C3p0 {
public static void main(String[] args) throws SQLException {
//1.创建连接池,使用默认配置。ComboPooledDataSource实现了DataSource接口
ComboPooledDataSource ds = new ComboPooledDataSource();
//2.使用连接池,从连接池中获取10个连接对象
for (int i = 1; i <= 11; i++) {
Connection connection = ds.getConnection();
System.out.println("第" + i + "个连接对象:" + connection);
//第5个释放
if (i==5) {
//放回到连接池中
connection.close();
}
}
}
}
Druid 数据库连接池
-
导入 jar 包。
druid-1.0.9.jar
-
编写配置文件,放在 src 目录下。
-
通过 Properties 集合加载配置文件。
-
通过 Druid 连接池工厂类获取数据库连接池对象。
-
获取数据库连接进行使用。
注意:Druid 不会自动加载配置文件,需要我们手动加载,但是文件的名称可以自定义。
druid配置文件
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db3
username=root
password=root
# 初始化连接数量
initialSize=5
# 最大连接数量
maxActive=10
# 超时等待时间
maxWait=3000
druid小案例
public class Demo2Druid {
public static void main(String[] args) throws Exception {
//1.从类路径下加载配置文件,获取一个输入流。如果不指定路径,默认是读取同一个包下资源文件
InputStream inputStream = Demo2Druid.class.getClassLoader().getResourceAsStream("druid.properties");
//2.使用Properties对象的方法将配置文件中属性加载到Properties对象中
Properties properties = new Properties();
//加载了配置文件中所有的属性
properties.load(inputStream);
//3.通过druid的工厂类创建连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//获取10个连接对象
for (int i = 1; i <= 11; i++) {
Connection connection = dataSource.getConnection();
System.out.println("第" + i + "个连接对象:" + connection);
//第3个连接关闭
if (i==3) {
connection.close();
}
}
}
}
JDBC工具类简单版
/**
* 访问数据库的工具类
*/
public class JdbcUtils {
//声明一个连接池对象,使用druid连接池
private static DataSource dataSource;
//在类加载的时候就创建连接池
static {
//1.从类路径下加载配置文件,获取一个输入流。如果不指定路径,默认是读取同一个包下资源文件
try (InputStream inputStream = JdbcUtils.class.getResourceAsStream("/druid.properties")) {
//2.使用Properties对象的方法将配置文件中属性加载到Properties对象中
Properties properties = new Properties();
//加载了配置文件中所有的属性
properties.load(inputStream);
//3.通过druid的工厂类创建连接池
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取连接池对象
* @return
*/
public static DataSource getDataSource() {
return dataSource;
}
/**
* 得到数据库的连接
*/
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
/**
* 关闭连接
* 查询调用这个方法
*/
public static void close(Connection connection, Statement statement, ResultSet resultSet) {
try {
if (resultSet != null) {
resultSet.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (statement != null) {
statement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 关闭连接
* 增删改没有结果集
*/
public static void close(Connection connection, Statement statement) {
//直接调用上面的方法
close(connection, statement, null);
}
/**
* 通用的增删改的方法
* @param sql 要执行的SQL语句
* @param params 替换占位符的真实地址,在方法内部是一个数组(从0开始)
* @return 影响的行数
*/
public static int update(String sql, Object... params) {
Connection connection = null;
PreparedStatement ps = null;
int row = 0; //影响的行数
try {
//1.创建连接对象
connection = getConnection();
//2.创建预编译的语句对象
ps = connection.prepareStatement(sql);
//2.5 得到参数元数据
ParameterMetaData metaData = ps.getParameterMetaData();
int count = metaData.getParameterCount(); //获取有几个参数
//3.替换占位符为真实的值
for (int i = 0; i < count; i++) {
ps.setObject(i + 1, params[i]); //一定是个对象类型
}
//4.执行SQL语句
row = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(connection, ps); //调用自己的方法
}
return row;
}
/**
* 通用的查询方法
* @param sql 要执行的SQL语句
* @param clazz 要实例化的类型,如:Student.class, Employee.class
* @param params 替换占位符的真实值
* @return 封装好的集合对象
*/
public static <T> List<T> query(String sql, Class<T> clazz, Object... params) {
Connection connection = null;
PreparedStatement ps = null;
ResultSet resultSet = null;
List<T> list = new ArrayList<>();
try {
//1.创建连接对象
connection = getConnection();
//2. 创建预编译的语句对象
ps = connection.prepareStatement(sql);
//2.5 得到参数元数据(源信息)
ParameterMetaData metaData = ps.getParameterMetaData();
int count = metaData.getParameterCount(); //获取有几个参数
//3.替换占位符为真实的值
for (int i = 0; i < count; i++) {
ps.setObject(i + 1, params[i]); //一定是个对象类型
}
//4.执行查询操作
resultSet = ps.executeQuery();
//5.遍历整个结果集,封装到集合中,每个元素是一个对象
while (resultSet.next()) {
//每条记录封装成一个对象,创建一个对象
T obj = clazz.getConstructor().newInstance();
//先得到实体类中有哪些属性
Field[] fields = clazz.getDeclaredFields(); //得到所有成员变量,包含私有的
//遍历每个成员变量,进行赋值
for (Field field : fields) {
//私有的要暴力
field.setAccessible(true);
//列名=属性名
String name = field.getName();
//要赋值的对象,值
field.set(obj, resultSet.getObject(name)); //从结果集中获取数据
}
//6.添加到集合中
list.add(obj);
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
close(connection, ps, resultSet);
}
return list;
}
}
使用工具类
/**
* 使用工具类
*/
public class Demo3UseUtils {
public static void main(String[] args) {
//使用工具类添加1条记录
int row = JdbcUtils.update("insert into student values(null,?,?,?)", "嫦娥", 0, "1997-07-07");
System.out.println("添加了" + row + "条");
//使用工具类查询所有的数据
List<Student> students = JdbcUtils.query("select * from student", Student.class);
//打印
students.forEach(System.out::println);
}
}
JDBC工具类加强版(自定义JDBC框架)
DataSourceUtils.java
/*
数据库连接池的工具类
*/
public class DataSourceUtils {
//1.私有构造方法
private DataSourceUtils(){}
//2.声明数据源变量
private static DataSource dataSource;
//3.提供静态代码块,完成配置文件的加载和获取数据库连接池对象
static{
try{
//完成配置文件的加载
InputStream is = DataSourceUtils.class.getClassLoader().getResourceAsStream("druid.properties");
Properties prop = new Properties();
prop.load(is);
//获取数据库连接池对象
dataSource = DruidDataSourceFactory.createDataSource(prop);
} catch (Exception e) {
e.printStackTrace();
}
}
//4.提供一个获取数据库连接的方法
public static Connection getConnection() {
Connection con = null;
try {
con = dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return con;
}
//5.提供一个获取数据库连接池对象的方法
public static DataSource getDataSource() {
return dataSource;
}
//6.释放资源
public static void close(Connection con, Statement stat, ResultSet rs) {
if(con != null) {
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stat != null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection con, Statement stat) {
if(con != null) {
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stat != null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
Student.java(实体类)
/*
学生的实体类
*/
public class Student {
private Integer sid;
private String name;
private Integer age;
private Date birthday;
public Student() {
}
public Student(Integer sid, String name, Integer age, Date birthday) {
this.sid = sid;
this.name = name;
this.age = age;
this.birthday = birthday;
}
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Student{" +
"sid=" + sid +
", name='" + name + '\'' +
", age=" + age +
", birthday=" + birthday +
'}';
}
}
ResultSetHandler.java
/*
用于处理结果集方式的接口
*/
public interface ResultSetHandler<T> {
<T> T handler(ResultSet rs);
}
BeanHandler.java
/*
实现类1:用于将查询到的一条记录,封装为Student对象并返回
*/
//1.定义一个类,实现ResultSetHandler接口
public class BeanHandler<T> implements ResultSetHandler<T>{
//2.定义Class对象类型变量
private Class<T> beanClass;
//3.通过有参构造为变量赋值
public BeanHandler(Class<T> beanClass) {
this.beanClass = beanClass;
}
//4.重写handler方法。用于将一条记录封装到自定义对象中
@Override
public T handler(ResultSet rs) {
//5.声明自定义对象类型
T bean = null;
try {
//6.创建传递参数的对象,为自定义对象赋值
bean = beanClass.newInstance();
//7.判断结果集中是否有数据
if(rs.next()) {
//8.通过结果集对象获取结果集源信息的对象
ResultSetMetaData metaData = rs.getMetaData();
//9.通过结果集源信息对象获取列数
int count = metaData.getColumnCount();
//10.通过循环遍历列数
for(int i = 1; i <= count; i++) {
//11.通过结果集源信息对象获取列名
String columnName = metaData.getColumnName(i);
//12.通过列名获取该列的数据
Object value = rs.getObject(columnName);
//13.创建属性描述器对象,将获取到的值通过该对象的set方法进行赋值
PropertyDescriptor pd = new PropertyDescriptor(columnName.toLowerCase(),beanClass);
//获取set方法
Method writeMethod = pd.getWriteMethod();
//执行set方法,给成员变量赋值
writeMethod.invoke(bean,value);
}
}
} catch (Exception e) {
e.printStackTrace();
}
//14.返回封装好的对象
return bean;
}
}
BeanListHandler.java
/*
实现类2:用于将查询到的多条记录,封装为Student对象并添加到集合返回
*/
//1.定义一个类,实现ResultSetHandler接口
public class BeanListHandler<T> implements ResultSetHandler<T>{
//2.定义Class对象类型变量
private Class<T> beanClass;
//3.通过有参构造为变量赋值
public BeanListHandler(Class<T> beanClass) {
this.beanClass = beanClass;
}
//4.重写handler方法。用于将多条记录封装到自定义对象中并添加到集合返回
@Override
public List<T> handler(ResultSet rs) {
//5.声明集合对象类型
List<T> list = new ArrayList<>();
try {
//6.判断结果集中是否有数据
while(rs.next()) {
//7.创建传递参数的对象,为自定义对象赋值
T bean = beanClass.newInstance();
//8.通过结果集对象获取结果集源信息的对象
ResultSetMetaData metaData = rs.getMetaData();
//9.通过结果集源信息对象获取列数
int count = metaData.getColumnCount();
//10.通过循环遍历列数
for(int i = 1; i <= count; i++) {
//11.通过结果集源信息对象获取列名
String columnName = metaData.getColumnName(i);
//12.通过列名获取该列的数据
Object value = rs.getObject(columnName);
//13.创建属性描述器对象,将获取到的值通过该对象的set方法进行赋值
PropertyDescriptor pd = new PropertyDescriptor(columnName.toLowerCase(),beanClass);
//获取set方法
Method writeMethod = pd.getWriteMethod();
//执行set方法,给成员变量赋值
writeMethod.invoke(bean,value);
}
//将对象保存到集合中
list.add(bean);
}
} catch (Exception e) {
e.printStackTrace();
}
//14.返回封装好的对象
return list;
}
}
ScalarHandler.java
/*
1.定义一个类,实现ResultSetHandler接口
2.重写handler方法
3.定义一个Long类型变量
4.判断结果集对象中是否还有数据
5.获取结果集源信息的对象
6.获取第一列的列名
7.根据列名获取该列的值
8.返回结果
*/
//1.定义一个类,实现ResultSetHandler接口
public class ScalarHandler<T> implements ResultSetHandler<T> {
//2.重写handler方法
@Override
public Long handler(ResultSet rs) {
//3.定义一个Long类型变量
Long value = null;
try{
//4.判断结果集对象中是否还有数据
if(rs.next()) {
//5.获取结果集源信息的对象
ResultSetMetaData metaData = rs.getMetaData();
//6.获取第一列的列名
String columnName = metaData.getColumnName(1);
//7.根据列名获取该列的值
value = rs.getLong(columnName);
}
}catch (Exception e) {
e.printStackTrace();
}
//8.返回结果
return value;
}
}
JDBCTemplate.java(框架)
/*
JDBC框架类
*/
public class JDBCTemplate {
//1.定义参数变量(数据源、连接对象、执行者对象、结果集对象)
private DataSource dataSource;
private Connection con;
private PreparedStatement pst;
private ResultSet rs;
//2.通过有参构造为数据源赋值
public JDBCTemplate(DataSource dataSource) {
this.dataSource = dataSource;
}
/*
查询方法:用于将聚合函数的查询结果进行返回
*/
public Long queryForScalar(String sql, ResultSetHandler<Long> rsh, Object...objs){
Long value = null;
try{
//通过数据源获取一个数据库连接
con = dataSource.getConnection();
//通过数据库连接对象获取执行者对象,并对sql语句进行预编译
pst = con.prepareStatement(sql);
//通过执行者对象获取参数的源信息对象
ParameterMetaData parameterMetaData = pst.getParameterMetaData();
//通过参数源信息对象获取参数的个数
int count = parameterMetaData.getParameterCount();
//判断参数数量是否一致
if(count != objs.length) {
throw new RuntimeException("参数个数不匹配");
}
//为sql语句占位符赋值
for(int i = 0; i < objs.length; i++) {
pst.setObject(i+1,objs[i]);
}
//执行sql语句并接收结果
rs = pst.executeQuery();
//通过ScalarHandler方式对结果进行处理
value = rsh.handler(rs);
} catch (Exception e) {
e.printStackTrace();
} finally {
//释放资源
DataSourceUtils.close(con,pst,rs);
}
//返回结果
return value;
}
/*
查询方法:用于将多条记录封装成自定义对象并添加到集合返回
*/
public <T> List<T> queryForList(String sql, ResultSetHandler<T> rsh, Object...objs){
List<T> list = new ArrayList<>();
try{
//通过数据源获取一个数据库连接
con = dataSource.getConnection();
//通过数据库连接对象获取执行者对象,并对sql语句进行预编译
pst = con.prepareStatement(sql);
//通过执行者对象获取参数的源信息对象
ParameterMetaData parameterMetaData = pst.getParameterMetaData();
//通过参数源信息对象获取参数的个数
int count = parameterMetaData.getParameterCount();
//判断参数数量是否一致
if(count != objs.length) {
throw new RuntimeException("参数个数不匹配");
}
//为sql语句占位符赋值
for(int i = 0; i < objs.length; i++) {
pst.setObject(i+1,objs[i]);
}
//执行sql语句并接收结果
rs = pst.executeQuery();
//通过BeanListHandler方式对结果进行处理
list = rsh.handler(rs);
} catch (Exception e) {
e.printStackTrace();
} finally {
//释放资源
DataSourceUtils.close(con,pst,rs);
}
//返回结果
return list;
}
/*
查询方法:用于将一条记录封装成自定义对象并返回
*/
public <T> T queryForObject(String sql, ResultSetHandler<T> rsh,Object...objs){
T obj = null;
try{
//通过数据源获取一个数据库连接
con = dataSource.getConnection();
//通过数据库连接对象获取执行者对象,并对sql语句进行预编译
pst = con.prepareStatement(sql);
//通过执行者对象获取参数的源信息对象
ParameterMetaData parameterMetaData = pst.getParameterMetaData();
//通过参数源信息对象获取参数的个数
int count = parameterMetaData.getParameterCount();
//判断参数数量是否一致
if(count != objs.length) {
throw new RuntimeException("参数个数不匹配");
}
//为sql语句占位符赋值
for(int i = 0; i < objs.length; i++) {
pst.setObject(i+1,objs[i]);
}
//执行sql语句并接收结果
rs = pst.executeQuery();
//通过BeanHandler方式对结果进行处理
obj = rsh.handler(rs);
} catch (Exception e) {
e.printStackTrace();
} finally {
//释放资源
DataSourceUtils.close(con,pst,rs);
}
//返回结果
return obj;
}
/*
用于执行增删改功能的方法
*/
//3.定义update方法。参数:sql语句、sql语句中的参数
public int update(String sql,Object...objs) {
//4.定义int类型变量,用于接收增删改后影响的行数
int result = 0;
try{
//5.通过数据源获取一个数据库连接
con = dataSource.getConnection();
//6.通过数据库连接对象获取执行者对象,并对sql语句进行预编译
pst = con.prepareStatement(sql);
//7.通过执行者对象获取参数的源信息对象
ParameterMetaData parameterMetaData = pst.getParameterMetaData();
//8.通过参数源信息对象获取参数的个数
int count = parameterMetaData.getParameterCount();
//9.判断参数数量是否一致
if(count != objs.length) {
throw new RuntimeException("参数个数不匹配");
}
//10.为sql语句占位符赋值
for(int i = 0; i < objs.length; i++) {
pst.setObject(i+1,objs[i]);
}
//11.执行sql语句并接收结果
result = pst.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
//12.释放资源
DataSourceUtils.close(con,pst);
}
//13.返回结果
return result;
}
}
JDBCTemplateTest.java(测试)
/*
模拟dao层
*/
public class JDBCTemplateTest {
private JDBCTemplate template = new JDBCTemplate(DataSourceUtils.getDataSource());
@Test
public void queryForScalar() {
//查询聚合函数的测试
String sql = "SELECT COUNT(*) FROM student";
Long value = template.queryForScalar(sql,new ScalarHandler<Long>());
System.out.println(value);
}
@Test
public void queryForList() {
//查询所有学生信息的测试
String sql = "SELECT * FROM student";
List<Student> list = template.queryForList(sql, new BeanListHandler<>(Student.class));
for(Student stu : list) {
System.out.println(stu);
}
}
@Test
public void queryForObject() {
//查询一条记录并封装自定义对象的测试
String sql = "SELECT * FROM student WHERE sid=?";
Student stu = template.queryForObject(sql,new BeanHandler<>(Student.class),1);
System.out.println(stu);
}
@Test
public void delete() {
//删除数据的测试
String sql = "DELETE FROM student WHERE name=?";
int result = template.update(sql, "周七");
System.out.println(result);
}
@Test
public void update() {
//修改数据的测试
String sql = "UPDATE student SET age=? WHERE name=?";
Object[] params = {37,"周七"};
int result = template.update(sql, params);
System.out.println(result);
}
@Test
public void insert() {
//新增数据的测试
String sql = "INSERT INTO student VALUES (?,?,?,?)";
Object[] params = {5,"周七",27,"1997-07-07"};
int result = template.update(sql, params);
if(result != 0) {
System.out.println("添加成功");
}else {
System.out.println("添加失败");
}
}
}
策略设计模式
- 执行者
- 策略
- 策略的实现
- …
- 调用者
- 创建策略传递给执行