[超全+实战演示] jdbc知识汇总,适合小白入门+进阶
文章目录
前言
周末花了一天时间整理的jdbc知识,本笔记是按照jdbc的三个主要步骤来展开的(获取连接—增删查改—关闭连接)每个步骤都有两种实现方式,由浅入深,每一步都有代码实测,如有什么问题,欢迎大家评论私信讨论,肝!
提示:以下是本篇文章正文内容,下面案例可供参考
一、JDBC介绍
在正式介绍JDBC知识之前,首先我们要先了解一下,Java中的数据存储技术:
-
在Java中,数据库存取技术可分为如下几类:
-
JDBC直接访问数据库
-
JDO (Java Data Object )技术
-
第三方O/R工具,如Hibernate, Mybatis 等
-
-
JDBC是java访问数据库的基石,JDO、Hibernate、MyBatis等只是更好的封装了JDBC。
JDBC(Java DataBase Connectivity:Java数据库连接)是Java和数据库之间的一个桥梁,是一个规范而不是一个实现,能够执行SQL语句。它由一组用Java语言编写的类和接口组成。各种不同类型的数据库都有相应的实现,本文中的代码都是针对MySQL数据库实现的。
二、JDBC使用步骤
2.1 实验前的准备工作
建立数据库test,建立数据库表customers,然后创建该表的相关属性。
2.2 方式一:用手写的方式实现数据库的连接、增删查改、资源关闭
2.2.1 数据库连接
在进行这一步之前要先,将MySQL数据库连接的jar包导入到工程中,具体的导入操作请参考
2.2.2 增删查改操作
使用PreparedStatement实现增、删、改操作
这一步的操作是建立在上面数据库连接成功的情况下的测试
先在【src】目录下面建立com.atguigu.preparedstatement.crud包,然后在该包下面建立PreparedStatementUpdateTest类(里面包含了通用的增删改操作+测试)
package com.atguigu.preparedstatement.crud;
import java.sql.Connection;
import java.sql.PreparedStatement;
import org.junit.Test;
import com.atguigu.util.JDBCUtils;
public class PreparedStatementUpdateTest {
//1.修改数据库数据的测试
@Test
public void testUpdate() {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JDBCUtils.getConnection();
String sql = "update customers set name = ? where id = ?";
ps = conn.prepareStatement(sql);
ps.setObject(1, "黄月玉");
ps.setObject(2, 18);
ps.execute();
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.closeResource(conn, ps);
}
}
//3. 删除指定记录测试
@Test
public void testUpdate1() throws Exception{
String sql = "delete from customers where id = ?";
update(sql, 3);
}
//通用的增删改查操作
public void update(String sql,Object ...args) throws Exception {
//1.获取数据库连接
Connection conn = null;
//2.获取PreparedStatement的实例 (或:预编译sql语句)
PreparedStatement ps = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
//3.填充占位符
for(int i = 0; i<args.length;i++){
ps.setObject(i + 1, args[i]);
}
//4.执行sql语句
ps.execute();
} catch (Exception e) {
e.printStackTrace();
}finally {
//5.关闭资源
JDBCUtils.closeResource(conn, ps);
}
}
}
使用PreparedStatement实现查询操作
在使用PreparedStatement实现查询操作之前,我们先来了解一下一种编程思想:
ORM编程思想 (object relational mapping:对象关系映射)
-
一个数据表对应一个java类
-
表中的一条记录对应java类的一个对象
-
表中的一个字段对应java类的一个属性
所以按照上面的思想就创建了两个类:Customer类、Order类对应数据库中的两个表Customer、Order表。(这两个类我们放在com.atguigu3.bean包下面)
【Customer类】
package com.atguigu3.bean;
import java.sql.Date;
public class Customer {
private int id;
private String name;
private String email;
private Date birth;
public Customer() {
super();
}
public Customer(int id, String name, String email, Date birth) {
super();
this.id = id;
this.name = name;
this.email = email;
this.birth = birth;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
@Override
public String toString() {
return "Customer [id=" + id + ", name=" + name + ", email=" + email + ", birth=" + birth + "]";
}
}
【Order类】
package com.atguigu3.bean;
import java.sql.Date;
public class Order {
//提供相应的属性
private int orderId;
private String orderName;
private Date orderDate;
//提供相应的构造器
public Order() {
super();
}
public Order(int orderId, String orderName, Date orderDate) {
super();
this.orderId = orderId;
this.orderName = orderName;
this.orderDate = orderDate;
}
//提供相应的getset方法
public int getOrderId() {
return orderId;
}
public void setOrderId(int orderId) {
this.orderId = orderId;
}
public String getOrderName() {
return orderName;
}
public void setOrderName(String orderName) {
this.orderName = orderName;
}
public Date getOrderDate() {
return orderDate;
}
public void setOrderDate(Date orderDate) {
this.orderDate = orderDate;
}
//重写toString
@Override
public String toString() {
return "Order [orderId=" + orderId + ", orderName=" + orderName + ", orderDate=" + orderDate + "]";
}
}
【测试一:针对于不同的表的通用的查询操作,返回表中的一条记录 (通用代码+测试)】**
package com.atguigu.preparedstatement.crud;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import org.junit.Test;
import com.atguigu3.bean.Customer;
import com.atguigu3.bean.Order;
import com.atguigu.util.JDBCUtils;
public class PreparedStatementQueryTest {
@Test
//测试
public void testGetInstance(){
String sql = "select id,name,email from customers where id = ?";
Customer customer = getInstance(Customer.class,sql,12);
System.out.println(customer);
//如果类中定义的属性名字和数据库表中的列名不一样那么sql语句就要用别名
String sql1 = "select order_id orderId,order_name orderName from `order` where order_id = ?";
Order order = getInstance(Order.class, sql1, 1);
System.out.println(order);
}
//针对于不同的表的通用的查询操作,返回表中的一条记录
public <T> T getInstance(Class<T> clazz,String sql, Object... args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
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 {
JDBCUtils.closeResource(conn, ps, rs);
}
return null;
}
}
【测试二:针对于不同的表的通用的查询操作,返回表中的多条记录 (通用代码+测试)】
package com.atguigu.preparedstatement.crud;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import com.atguigu3.bean.Customer;
import com.atguigu3.bean.Order;
import com.atguigu.util.JDBCUtils;
public class PreparedStatementQueryTest1 {
@Test
//测试
public void testGetForList(){
String sql = "select id,name,email from customers where id < ?";
List<Customer> list = getForList(Customer.class,sql,12);
list.forEach(System.out::println);
String sql1 = "select order_id orderId,order_name orderName from `order`";
List<Order> orderList = getForList(Order.class, sql1);
orderList.forEach(System.out::println);
}
//针对于不同的表的通用的查询操作,返回表中的多条记录**
public <T> List<T> getForList(Class<T> clazz,String sql, Object... args){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
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 {
JDBCUtils.closeResource(conn, ps, rs);
}
return null;
}
}
2.2.3 资源关闭
手动的资源关闭已经包括在上面的第一步数据库连接里面了,所以大家参考一下上面的就可以了。
2.3 方式二:用引入相关jar包的方式,实现数据库的连接、增删查改、资源关闭
2.3.1 数据库连接(使用阿里提供的Druid 数据库连接池)
Druid是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0、DBCP、Proxool等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池,可以说是目前最好的连接池之一。
在做这个测试之前需要导入相应的druid-1.1.10.jar 包导入到工程中,还要注意引用到库中,还要再【src】目录下面建立druid.properties文件
src下的配置文件为:【druid.properties】
url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
username=root
password=123456
driverClassName=com.mysql.jdbc.Driver
initialSize=10
maxActive=20
maxWait=1000
filters=wall
连接测试
在com.atguigu4.connection包下面,建立一个类DruidTest用来测试连接
package com.atguigu4.connection;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
import javax.sql.DataSource;
import org.junit.Test;
import com.alibaba.druid.pool.DruidDataSourceFactory;
public class DruidTest {
@Test
public void getConnection() throws Exception{
Properties pros = new Properties();
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
pros.load(is);
DataSource source = DruidDataSourceFactory.createDataSource(pros);
Connection conn = source.getConnection();
System.out.println(conn);
}
}
上面就是建立数据库连接池操作了,而且测试也成功了,那么为什么还需要下面的操作呢??
因为下面这个操作只是把Druid数据库连接池的相关操作封装到工具类JDBCUtils类中,以便更好地进行下面的增删查改相关的操作。
然后把Druid数据库连接池和DbUtils类的关闭操作封装到工具类JDBCUtils类中
数据库连接池和资源的关闭相关操作我们一般都是放在com.atguigu4.util包下面的JDBCUtils类中(在这个类中我们资源关闭的代码也放在里面了),后面操作的数据库连接和关闭资源的相关操作都是基于这个工具类
package com.atguigu4.util;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbutils.DbUtils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
public class JDBCUtils {
/**
* 使用Druid数据库连接池技术
*/
private static DataSource source1;
static{
try {
Properties pros = new Properties();
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 = source1.getConnection();
return conn;
}
/**
*
* @Description 使用dbutils.jar中提供的DbUtils工具类,实现资源的关闭
* @author shkstart
* @date 下午4:53:09
* @param conn
* @param ps
* @param rs
*/
public static void closeResource1(Connection conn,Statement ps,ResultSet rs){
DbUtils.closeQuietly(conn);
DbUtils.closeQuietly(ps);
DbUtils.closeQuietly(rs);
}
}
2.3.2 增删查改
这里的增删查改操作DBUtils工具类库来实现,包括下面的资源关闭也是调用这个类库中的APi来完成
Apache-DBUtils简介
-
commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装(封装了增删查改与关闭资源的操作),学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。
-
API介绍:
- org.apache.commons.dbutils.QueryRunner (实现增删查改操作)
- 工具类:org.apache.commons.dbutils.DbUtils (实现关闭资源操作)
- org.apache.commons.dbutils.ResultSetHandler (实现按照特殊要求的一些输出操作)
在使用之前要先引入commons-dbutils-1.3.jar包,然后加载到引用库中先,如下图所示:
这里的增删查改其实已经很简单了,因为QueryRunner类中都封装了相关的操作,我们直接调用就好了(该类中封装的增删查改操作其实和我们第一种方式中的通过增删改代码和通用查询类似的,只是人家的代码更加完美)
在正式的测试之前我们先建立com.atguigu4.bean包,在该包下面建立Customer类,用于对应数据库中的Customer表,其对象就对应Customer表中的一条数据。
【Customer类】
package com.atguigu4.bean;
import java.sql.Date;
/*
* ORM编程思想 (object relational mapping)
* 一个数据表对应一个java类
* 表中的一条记录对应java类的一个对象
* 表中的一个字段对应java类的一个属性
*
*/
public class Customer {
private int id;
private String name;
private String email;
private Date birth;
public Customer() {
super();
}
public Customer(int id, String name, String email, Date birth) {
super();
this.id = id;
this.name = name;
this.email = email;
this.birth = birth;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
@Override
public String toString() {
return "Customer [id=" + id + ", name=" + name + ", email=" + email + ", birth=" + birth + "]";
}
}
下面进行正式的测试:
先在【src】目录下面建立com.atguiu4.dbutils包,然后在该包下面建立QueryRunnerTest类,用来测试增删查改的相关操作
package com.atguiu4.dbutils;
import java.sql.Connection;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.MapHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import org.junit.Test;
import com.atguigu4.bean.Customer;
import com.atguigu4.util.JDBCUtils;
/*
* commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,封装了针对于数据库的增删改查操作
*
*/
public class QueryRunnerTest {
//测试插入
@Test
public void testInsert() {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "insert into customers(name,email,birth)values(?,?,?)";
int insertCount = runner.update(conn, sql, "蔡徐坤","caixukun@126.com","1997-09-08");
System.out.println("添加了" + insertCount + "条记录");
} catch (SQLException e) {
e.printStackTrace();
}finally{
JDBCUtils.closeResource1(conn, null, null);
}
}
//测试查询
/*
* BeanHander:是ResultSetHandler接口的实现类,用于封装表中的一条记录。
*/
@Test
public void testQuery1(){
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select id,name,email,birth from customers where id = ?";
BeanHandler<Customer> handler = new BeanHandler<>(Customer.class);
Customer customer = runner.query(conn, sql, handler, 23);
System.out.println(customer);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
JDBCUtils.closeResource1(conn, null, null);
}
}
/*
* BeanListHandler:是ResultSetHandler接口的实现类,用于封装表中的多条记录构成的集合。
*/
@Test
public void testQuery2() {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select id,name,email,birth from customers where id < ?";
BeanListHandler<Customer> handler = new BeanListHandler<>(Customer.class);
List<Customer> list = runner.query(conn, sql, handler, 23);
list.forEach(System.out::println);
} catch (SQLException e) {
e.printStackTrace();
}finally{
JDBCUtils.closeResource1(conn, null, null);
}
}
/*
* MapHander:是ResultSetHandler接口的实现类,对应表中的一条记录。
* 将字段及相应字段的值作为map中的key和value
*/
@Test
public void testQuery3(){
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select id,name,email,birth from customers where id = ?";
MapHandler handler = new MapHandler();
Map<String, Object> map = runner.query(conn, sql, handler, 23);
System.out.println(map);
} catch (SQLException e) {
e.printStackTrace();
}finally{
JDBCUtils.closeResource1(conn, null, null);
}
}
/*
* MapListHander:是ResultSetHandler接口的实现类,对应表中的多条记录。
* 将字段及相应字段的值作为map中的key和value。将这些map添加到List中
*/
@Test
public void testQuery4(){
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select id,name,email,birth from customers where id < ?";
MapListHandler handler = new MapListHandler();
List<Map<String, Object>> list = runner.query(conn, sql, handler, 23);
list.forEach(System.out::println);
} catch (SQLException e) {
e.printStackTrace();
}finally{
JDBCUtils.closeResource1(conn, null, null);
}
}
/*
* ScalarHandler:用于查询特殊值
*/
@Test
//返回customers表中的顾客数
public void testQuery5(){
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select count(*) from customers";
ScalarHandler handler = new ScalarHandler();
Long count = (Long) runner.query(conn, sql, handler);
System.out.println(count);
} catch (SQLException e) {
e.printStackTrace();
}finally{
JDBCUtils.closeResource1(conn, null, null);
}
}
//返回最大的生日时间
@Test
public void testQuery6(){
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select max(birth) from customers";
ScalarHandler handler = new ScalarHandler();
Date maxBirth = (Date) runner.query(conn, sql, handler);
System.out.println(maxBirth);
} catch (SQLException e) {
e.printStackTrace();
}finally{
JDBCUtils.closeResource1(conn, null, null);
}
}
/*
* 自定义ResultSetHandler的实现类
*/
@Test
public void testQuery7(){
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection3();
String sql = "select id,name,email,birth from customers where id = ?";
ResultSetHandler<Customer> handler = new ResultSetHandler<Customer>(){
@Override
public Customer handle(ResultSet rs) throws SQLException {
if(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
String email = rs.getString("email");
Date birth = rs.getDate("birth");
Customer customer = new Customer(id, name, email, birth);
return customer;
}
return null;
}
};
Customer customer = runner.query(conn, sql, handler,23);
System.out.println(customer);
} catch (SQLException e) {
e.printStackTrace();
}finally{
JDBCUtils.closeResource1(conn, null, null);
}
}
}
2.3.3 资源关闭
这里资源的关闭也是放在了上面连接数据库连接池的JDBCUtil工具类中。
三、总结
以上介绍了JDBC的两种实现方式,里面的每个案例都是自己亲自认证过的,按照里面一步步来就好了,其实第二种方式才是我们平时开发中用到的,第一种方式只是有助于我们去理解第二种方式中一些类中封装的操作,还有就是要注意好实验前的准备工作,包括数据库的建立数据表的建立这些加油!!