认识常用的jar包
connection(Driver接口实现的jar包),druid(数据库池管理包),commons-dbutils(简化jdbc数据库的增删改查操作,并且可以封装查询到的结果,同时简化了事务管理)
认识几个常用的类和接口
Driver接口->提供给数据库厂商使用,把需要用到的数据库驱动程序或者类库加载到项目的classpath(jar)包中
DriverManager类->用于加载JDBC驱动并创建于数据库的连接,创建驱动最常使用的是Class类的静态方法forName()来实现,但是在jdk4.0之后就不需要手动加载了(在配置文件里面写 driverClass=com.mysql.jdbc.Driver),连接数据库最长使用的方法是getConnection建立连接,返回一个Connection对象
Connection接口可以与数据库进行交互,有了这个对象就可以在进行事务的处理,以及对数据库的增删改查
认识配置文件里的几个参数
url->通过url可以选择正确的驱动程序,从而连接到我们需要的数据库。
由四部分组成:协议,子协议,子名称,其他参数,例如修改数据库事务为自动提交
配置文件的好处
配置信息需要修改的时候无须改动代码,并且维护简单,省去了配置信息编译的过程
增删改使用的PreparedStatement 对象调用executeUpdate()
查询使用的是executeQuery()
但是传统的JDBC连接比较麻烦,并且在对数据库进行增删改查的时候也是代码比较繁琐,因此引入了,druid(数据库池管理包),commons-dbutils(简化jdbc数据库的增删改查操作)
引入德鲁伊(Druid)
druid里使用频率最高的是数据源接口DataSource(基于数据库连接池),数据源调用getConnection()方法,可以说这种方式就是使用了连接池技术.DataSource内部封装了一个连接池,当你获取DataSource的时候,它已经悄悄的与数据库建立了多个Connection,并将这些Connection放入了连接池,此时调用DataSource.getConnection()它从连接池里取一个Connection返回,Connection使用完后被close,但这个close并不是真正的与数据库断开连接,而是告诉连接池"我"已经被使用完,"你"可以把我分配给其它"人"使用了.就这样连接池里的Connection被循环利用,避免了每次获取Connection时重新去连接数据库。
暴露数据源的原因
将数据源对象暴露出去的原因可能是为了让使用者能够更灵活地操作数据源,比如可以通过数据源对象获取一些元数据信息,或者使用数据源对象来创建其他类型的连接等。另外,有些情况下可能需要在不同的方法或类之间共享同一个数据源对象,这时候将数据源对象暴露出去就可以方便地实现这种共享。但是,在暴露数据源对象时需要注意安全性和封装性,避免使用者对数据源对象进行不合理的操作或者直接修改数据源对象的状态。
在这里学会到了两个新的反射方法
getGenericSuperclass()获取父类的类型,得到父类类型后。getActualTypeArguments()得到父类的泛型,在这里父类指的是抽象类(是增删改查的逻辑语句)。
先说说数据库的连接:
在配置文件中有必须的四个参数和其他参数,由于配置文件是键值对的方式,所以可以使用Properties来加载这个文件。
可以把数据库的连接操作单独成一个工具类,最主要的工具是数据源,使用DruidDataSourceFactory.createDataSource(pro)这个方法创建数据源,通过数据源连接数据库.
封装CURD
QueryRunner来自commons-dbutils这个jar包,用于简化增删改查的逻辑语句,无论哪个实体调用抽象类都可以,获取到泛型指定的类型就可以把得到的ResultSetHandler集合转成一个Bean实体进行封装。一般来说,增删改都需要用到事务处理,传入数据源,作为参数让QueryRunner对象知道要使用哪个数据库连接来执行查询操作,当使用QueryRunner进行操作的时候,传入数据源,会自动从数据源中获取一个数据库连接,并且在查询结束后,连接会被自动关闭和放回连接池,避免手动创建和管理数据库连接。
数据库连接的最终增删改查版本
package com.sun.utils;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
//创建一个数据源的工具类,此类得到数据源对象
public class JDBCUtils {
private static DruidDataSource dataSource;
static {
try {
Properties properties = new Properties();
// 读取 jdbc.properties 属性配置文件
InputStream inputStream = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
// 从流中加载数据
properties.load(inputStream);
// 创建 数据库连接池
dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() {
Connection conn = null;
try {
conn = dataSource.getConnection();
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
//获取数据源方法
public static DataSource getDataSource() {
return dataSource;
}
//关闭连接,放回数据库连接池
public static void close(Connection conn) {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
package com.sun.utils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
/**
* 定义一个用来被继承的对数据库进行基本操作的Dao
* @param <T> 泛型 -> bean类型
*/
public abstract class BaseDao<T>{
private QueryRunner queryRunner ;
// 定义一个变量来接收泛型的类型
private Class<T> type;
// 获取T的Class对象,获取泛型的类型,泛型是在被子类继承时才确定
public BaseDao() {
queryRunner = new QueryRunner(JDBCUtils.getDataSource());
// 获取子类的类型
Class clazz = this.getClass();
// 获取父类的类型
// getGenericSuperclass()用来获取当前类的父类的类型<这里的当前类指的是继承这个抽象类的子类>
// ParameterizedType表示的是带泛型的类型
ParameterizedType parameterizedType =
(ParameterizedType) clazz.getGenericSuperclass();
// 获取具体的泛型类型 getActualTypeArguments获取具体的泛型的类型
// 这个方法会返回一个Type的数组
Type[] types = parameterizedType.getActualTypeArguments();
// 获取具体的泛型的类型·
this.type = (Class<T>) types[0];
}
/**
* 获取所有对象
*
* @param sql
* @param params
* @return
*/
public List<T> getBeanList(Connection conn, String sql, Object... params) {
List<T> list = null;
try {
list = queryRunner.query(conn, sql, new BeanListHandler<T>(type), params);
} catch (SQLException e) {
e.printStackTrace();
}
return list;
}
/**
* 获取一个对象
*
* @param sql
* @param params
* @return
*/
public T getBean(Connection conn,String sql, Object... params) {
T t = null;
try { //type == String.class
t = queryRunner.query(conn, sql, new BeanHandler<T>(type), params);
} catch (SQLException e) {
e.printStackTrace();
}
return t;
}
/**
* 通用的增删改操作(事务有关)
*
* @param sql
* @param params
* @return
*/
public int update(Connection conn, String sql, Object... params) {
int count = 0;
try {
count = queryRunner.update(conn, sql, params);
} catch (SQLException e) {
e.printStackTrace();
}
return count;
}
/**
* 通用的增删改操作(没有办法使用事务)
*
* @param sql
* @param params
* @return
*/
public int update(String sql, Object... params) {
int count = 0;
try {
count = queryRunner.update(sql, params);
} catch (SQLException e) {
e.printStackTrace();
}
return count;
}
/**
* 添加数据后返回主键id(事务有关)
* @param sql
* @param params
* @return
*/
public int insert(Connection conn ,String sql, Object... params){
try {
return queryRunner.insert(conn,sql , new ScalarHandler<Long>() , params).intValue();
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
/**
* 添加数据后返回主键id(没有办法使用事务)
* @param sql
* @param params
* @return
*/
public int insert(String sql, Object... params){
try {
return queryRunner.insert(sql , new ScalarHandler<Long>() , params).intValue();
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
/**
* 获取单行单列值得方法,专门用来执行像 select count(*)...这样的sql语句
*
* @param sql
* @param params
* @return
*/
public int getValue(Connection conn,String sql, Object... params) {
int count = 0;
try {
// 调用queryRunner的query方法获取一个单一的值
count = queryRunner.query(conn, sql, new ScalarHandler<Long>(), params).intValue();
} catch (SQLException e) {
e.printStackTrace();
}
return count;
}
/**
* 获取所有对象
*
* @param sql
* @param params
* @return
*/
public List<T> getBeanList( String sql, Object... params) {
List<T> list = null;
try {
list = queryRunner.query(sql, new BeanListHandler<T>(type), params);
} catch (SQLException e) {
e.printStackTrace();
}
return list;
}
/**
* 获取一个对象
*
* @param sql
* @param params
* @return
*/
public T getBean(String sql, Object... params) {
T t = null;
try { //type == String.class
t = queryRunner.query(sql, new BeanHandler<T>(type), params);
} catch (SQLException e) {
e.printStackTrace();
}
return t;
}
/**
* 获取单行单列值得方法,专门用来执行像 select count(*)...这样的sql语句
*
* @param sql
* @param params
* @return
*/
public int getValue(String sql, Object... params) {
int count = 0;
try {
// 调用queryRunner的query方法获取一个单一的值
count = queryRunner.query( sql, new ScalarHandler<Long>(), params).intValue();
} catch (SQLException e) {
e.printStackTrace();
}
return count;
}
}
总结:
一个javaBean,一个封装好增删改查的逻辑操作(工具类<抽象类>可以供多个JavaBean使用),一个Dao接口,是对应实体类的增删改查,一个实现类,继承工具类,同时实现Dao接口。一套JDBC连接数据库的增删改查就封装好了
使用三个第三方Jar包,其中QueryRunner类简化了代码的书写,使用这个类可以直接调用封装好的增删改查逻辑,传入数据源在查询的时候无须手动管理数据池里的连接,如需要事务处理,可以直接传入Connection数据库连接手动管理。最终版本依赖数据源,数据库连接池,第三方代码简化工具。
但是这个封装仍有不足,比如:QueryRunner没有提供直接的删除方法,要删除数据可以通过update方法来实现,插入方法返回的是受影响的行数,而不是插入的条数。