jdbc最基础的六步
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try {
// 1.注册驱动
//driver = new com.oracle.jdbc.driver.OracleDriver();这是oracle的驱动
Driver driver = new com.mysql.cj.jdbc.Driver();
DriverManager.registerDriver(driver);
// 2.获取连接
/*
url:统一资源定位符(网络中某一资源的绝对路径)
jdbc:mysql://127.0.0.1:3306/bjpowernode
jdbc:mysql:// 协议
127.0.0.1 ip地址(localhost也是本机ip)
3306 端口号
bjpowernode 具体的数据库实例名
*/
String url = "jdbc:mysql://127.0.0.1:3306/bjpowernode";
String user = "root";
String password = "123456";
conn = DriverManager.getConnection(url, user, password);
System.out.println("数据库连接对象:" + conn);//数据库连接对象:com.mysql.cj.jdbc.ConnectionImpl@5fdba6f9
// 3.获取数据库操作对象
stmt = conn.createStatement();
// 4.执行sql语句
String sql = "insert into dept(deptno,dname,loc) values (50,'人事部','北京')";
int count = stmt.executeUpdate(sql);
System.out.println(count == 1 ? "保存成功" : "保存失败");
// 5.处理查询结果集
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 6.释放资源(在finally语句块中关闭资源可以保证资源一定被释放)
if(stmt!= null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn!= null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
注册驱动获取连接经常写很繁琐,所以我们封装成DBUtil中的一个静态方法
public class DBUtil {
/**
* 工具类的构造器都是私有的,因为工具类的方法都是静态方法,直接调用,根本用不上实例化。
*/
private DBUtil() {
}
// 静态代码块在类加载的同时就执行,并且只执行一次
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取数据库连接对象
* @return 数据库的连接对象
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {//我们知道,main()中会对这些异常进行捕捉处理,所以这里就抛上去。
// Class.forName("com.mysql.cj.jdbc.Driver");
// 注册驱动的操作,一个程序只用执行一次,但是我们会多次调用这个getConnection()导致注册多个驱动,所以我们想到了静态代码块
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root", "123456");
return connection;
}
同样的关闭资源的步骤也很繁琐,我们也封装成DBUtil中的一个静态方法
public class DBUtil {
/**
* 工具类的构造器都是私有的,因为工具类的方法都是静态方法,直接调用,根本用不上实例化。
*/
private DBUtil() {
}
/**
* 释放资源
* @param conn 连接对象
* @param ps 数据库操作对象
* @param rs 结果集
*/
public static void close(Connection conn, Statement ps, ResultSet rs){
if (rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
于是我们使用上自己定义的一个DBUtil类之后
public class G2 {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement ps = null;
ResultSet resultSet = null;
try {
connection = DBUtil.getConnection();//注册驱动+获取连接
String sql = "select ename from emp where ename like ?";
ps = connection.prepareStatement(sql);
ps.setString(1,"_A%");
resultSet = ps.executeQuery();
while(resultSet.next()){
System.out.println(resultSet.getString("ename"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(connection,ps,resultSet);
}
}
}
但是普通的statement存在sql注入问题,我们平时几乎都用的PrepareStatement,代码在上一个文章中
到了这里便已经学完了jdbc的连接操作知识,但是平时的开发根本不会这样写
自定义DAO文件
我们写sql语句也很是繁琐,所以我们自己定义一个sql语句的通用方法,我们把连接数据库的变量conn定义到了外面(即需要我们自己填写),这样我们可以有不同的连接也可以使用自己定义的DAO,代码更加健壮。
public abstract class BaseDAO {
// 通用的增删改操作---version 2.0 (考虑上事务)
public int update(Connection conn, String sql, Object... args) {// sql中占位符的个数与可变形参的长度相同!
PreparedStatement ps = null;
try {
// 1.预编译sql语句,返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
// 2.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);// 小心参数声明错误!!
}
// 3.执行
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 4.资源的关闭
JDBCUtil.closeResource(null, ps);
}
return 0;
}
// 通用的查询操作,用于返回数据表中的一条记录(version 2.0:考虑上事务)
public <T> T getInstance(Connection conn, Class<T> clazz, String sql, Object... args) {
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
// 获取结果集的元数据 :ResultSetMetaData
ResultSetMetaData rsmd = rs.getMetaData();
// 通过ResultSetMetaData获取结果集中的列数
int columnCount = rsmd.getColumnCount();
if (rs.next()) {
T t = clazz.newInstance();
// 处理结果集一行数据中的每一个列
for (int i = 0; i < columnCount; i++) {
// 获取列值
Object columValue = rs.getObject(i + 1);
// 获取每个列的列名
// String columnName = rsmd.getColumnName(i + 1);
String columnLabel = rsmd.getColumnLabel(i + 1);
// 给t对象指定的columnName属性,赋值为columValue:通过反射
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columValue);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.closeResource(null, ps, rs);
}
return null;
}
// 通用的查询操作,用于返回数据表中的多条记录构成的集合(version 2.0:考虑上事务)
public <T> List<T> getForList(Connection conn, Class<T> clazz, String sql, Object... args) {
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
// 获取结果集的元数据 :ResultSetMetaData
ResultSetMetaData rsmd = rs.getMetaData();
// 通过ResultSetMetaData获取结果集中的列数
int columnCount = rsmd.getColumnCount();
// 创建集合对象
ArrayList<T> list = new ArrayList<T>();
while (rs.next()) {
T t = clazz.newInstance();
// 处理结果集一行数据中的每一个列:给t对象指定的属性赋值
for (int i = 0; i < columnCount; i++) {
// 获取列值
Object columValue = rs.getObject(i + 1);
// 获取每个列的列名
// String columnName = rsmd.getColumnName(i + 1);
String columnLabel = rsmd.getColumnLabel(i + 1);
// 给t对象指定的columnName属性,赋值为columValue:通过反射
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columValue);
}
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.closeResource(null, ps, rs);
}
return null;
}
//用于查询特殊值的通用的方法
public <E> E getValue(Connection conn,String sql,Object...args){
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql);
for(int i = 0;i < args.length;i++){
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
if(rs.next()){
return (E) rs.getObject(1);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
JDBCUtil.closeResource(null, ps, rs);
}
return null;
}
}
定义一套接口
通用的方法有了,具体的实现就需要真正增删改查的操作了,同样是为了代码的复用性,我们定义一套增删改查的接口,之后只需要实现这套接口便有了具体的增删改查的方法
public interface CustomerDAO {
/**
*
* @Description 将cust对象添加到数据库中
* @author shkstart
* @date 上午11:00:27
* @param conn
* @param cust
*/
void insert(Connection conn, Customer cust);
/**
*
* @Description 针对指定的id,删除表中的一条记录
* @author shkstart
* @date 上午11:01:07
* @param conn
* @param id
*/
void deleteById(Connection conn,int id);
/**
*
* @Description 针对内存中的cust对象,去修改数据表中指定的记录
* @author shkstart
* @date 上午11:02:14
* @param conn
* @param cust
*/
void update(Connection conn,Customer cust);
/**
*
* @Description 针对指定的id查询得到对应的Customer对象
* @author shkstart
* @date 上午11:02:59
* @param conn
* @param id
*/
Customer getCustomerById(Connection conn,int id);
/**
*
* @Description 查询表中的所有记录构成的集合
* @author shkstart
* @date 上午11:03:50
* @param conn
* @return
*/
List<Customer> getAll(Connection conn);
/**
*
* @Description 返回数据表中的数据的条目数
* @author shkstart
* @date 上午11:04:44
* @param conn
* @return
*/
Long getCount(Connection conn);
/**
*
* @Description 返回数据表中最大的生日
* @author shkstart
* @date 上午11:05:33
* @param conn
* @return
*/
Date getMaxBirth(Connection conn);
}
有了这套通用的方法和接口,我们的实现类只需要继承并且实现接口
public class CustomerDAOImpl extends BaseDAO implements CustomerDAO{
@Override
public void insert(Connection conn, Customer cust) {
String sql = "insert into customer(name,email,birth)values(?,?,?)";
update(conn, sql,cust.getName(),cust.getEmail(),cust.getBirth());
}
@Override
public void deleteById(Connection conn, int id) {
String sql = "delete from customer where id = ?";
update(conn, sql, id);
}
@Override
public void update(Connection conn, Customer cust) {
String sql = "update customer set name = ?,email = ?,birth = ? where id = ?";
update(conn, sql,cust.getName(),cust.getEmail(),cust.getBirth(),cust.getId());
}
@Override
public Customer getCustomerById(Connection conn, int id) {
String sql = "select id,name,email,birth from customer where id = ?";
Customer customer = getInstance(conn,Customer.class, sql,id);
return customer;
}
@Override
public List<Customer> getAll(Connection conn) {
String sql = "select id,name,email,birth from customer";
List<Customer> list = getForList(conn, Customer.class, sql);
return list;
}
@Override
public Long getCount(Connection conn) {
String sql = "select count(*) from customer";
return getValue(conn, sql);
}
@Override
public Date getMaxBirth(Connection conn) {
String sql = "select max(birth) from customer";
return getValue(conn, sql);
}
}
测试我们上面的方法
public class CustomerDAOImplTest {
CustomerDAOImpl dao = new CustomerDAOImpl();
@Test
public void testInsert() {
Connection conn = null;
try {
conn = JDBCUtil.getConnection();
Customer customer = new Customer(1, "常海玉", "1243523431.com", new Date(6789765678L));
dao.insert(conn,customer);
System.out.println("添加成功");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.closeResource(conn,null);
}
}
@Test
public void testdeleteById(){
Connection conn = null;
try {
conn = JDBCUtil.getConnection();
dao.deleteById(conn,1);
System.out.println("删除成功");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.closeResource(conn,null);
}
}
@Test
public void testUpdate(){
Connection conn = null;
try {
conn = JDBCUtil.getConnection();
Customer c = new Customer(2, "shuai", "345654", new Date(345678765l));
dao.update(conn,c);
System.out.println("修改成功");
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.closeResource(conn,null);
}
}
@Test
public void testGetAll(){
Connection conn = null;
try {
conn = JDBCUtil.getConnection();
List<Customer> all = dao.getAll(conn);
all.forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.closeResource(conn,null);
}
}
数据库连接池技术
我们到了这里学习一下数据库连接池技术,有c3p0、DBCP、Druid。常用的就是Druid,其实三个实现的方式差不多,这里就只演示以下Druid。
实现方法:使用配置文件
在src目录下创建一个以properties为后缀的文件,这便是配置文件,里面主要填写配置信息,也可以填写数据库连接池的一些管理方面的配置(例如:初始时连接池中有多少连接或者最大最小的连接数等等)我的配置文件如下:
url = jdbc:mysql://localhost:3306/bjpowernode
username = root
password = 123456
driverClassName =com.mysql.cj.jdbc.Driver
?使用数据库连接池技术获取连接
public class DruidTest {
@Test
public void testGetConnection3() throws Exception {
Properties pros = new Properties();
// 方式一:得到配置文件的输入流
final InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
// 方式二:得到配置文件的输入流
// FileInputStream is = new FileInputStream(new File("src/dbcp.properties"));
pros.load(is);
// 创建数据库连接池
DataSource source = DruidDataSourceFactory.createDataSource(pros);
// 获取连接
Connection conn = source.getConnection();
// 验证一下是否获取到了
System.out.println(conn);
}
}
对数据库连接池技术获取数据库连接的方式进行封装:
public class PoolUtil {
/**
* 使用Druid数据库连接池技术获取数据库连接
* @return
* @throws Exception
*/
static DataSource source1;
static {
try {
Properties pros = new Properties();
final InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
pros.load(is);
source1 = DruidDataSourceFactory.createDataSource(pros);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection3() throws SQLException {
Connection conn = source.getConnection();
return conn;
}
}
Apache组织提供的JDBC开源工具:commons-dbutils
到了这里我们的实现已经比较简单了,此时我们因为代码的繁琐,自定义了这么多util文件,那么有么有官方的定义好的呢,答案是有的:
commons-dbutils,是Apache组只提供的一个开源JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。
这里我们需要导入对应的jar包才可以使用定义好的方法。
最终版本:
使用数据库连接池技术连接数据库,
使用dbutils来实现增删改查操作,
使用dbutils实现释放资源的操作。
public class QueryRunnerTest {
@Test//插入测试
public void testInsert() {
Connection conn = null;
try {
conn = PoolUtil.getConnection3();//自己封装的用Druid数据库连接池技术来获取连接
QueryRunner runner = new QueryRunner();//Apache组织封装的类的实例化
String sql = "insert into dept(deptno,dname,loc) values(?,?,?) ";//sql语句
int count = runner.update(conn, sql, 60, "haiyu", "shanxi");//Apache组织封转的增删改查通用方法。其实和我们自己封装的差不多,只是考虑到了很多特殊情况,更加健壮
System.out.println(count);//改变了数据库的行数
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtil.closeResource(conn, null);//自己定义的释放资源的方法,其实Apache组织也有封装,但是释放资源就是那样,差不多。
}
}
/*
BeanHandler:这是查询的一个对象,把我们数据库中的表中的元素整一个类,然后返回值是一个实例化的对象
*/
@Test
public void testSelect() {
Connection conn = null;
try {
conn = JDBCUtil.getConnection();
QueryRunner runner = new QueryRunner();
String sql = "select * from customer where id = ?";
BeanHandler<Customer> handler = new BeanHandler<>(Customer.class);
Customer c = runner.query(conn, sql, handler, 2);//这个方法的返回值就在handler中写着,handler是带泛型的,泛型是我们可以自己封转的一个类,返回值就是这个类的一个实例化
System.out.println(c);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.closeResource(conn, null);
}
}
/*
BeanListHandler:把对象封装起来,一个集合
*/
@Test
public void testSelect1() {
Connection conn = null;
try {
conn = JDBCUtil.getConnection();
QueryRunner runner = new QueryRunner();
String sql = "select * from customer where id < ?";
BeanListHandler<Customer> handler = new BeanListHandler<>(Customer.class);
List<Customer> list = runner.query(conn, sql, handler, 6);
list.forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.closeResource(conn, null);
}
}
/*
ScalarHandler用于查询特殊值
*/
@Test
public void testSelect2() {
Connection conn = null;
try {
conn = JDBCUtil.getConnection();
QueryRunner runner = new QueryRunner();
String sql = "select count(*) from emp";
ScalarHandler handler = new ScalarHandler();
Long count = (Long) runner.query(conn, sql, handler);
System.out.println(count);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.closeResource(conn, null);
}
}
}
?
|