JDBC应用
记录JDBC的学习笔记,为后期使用JDBC建立一个简单的速查页面,省去中间的推导等
池: Druid
DBUtil: Apache Commons DbUtils
也作为八股文的速查
数据库连接的基本步骤
以 Druid 连接池包为例,读取 /src/druid.properties 并建立 DataSource
-
准备配置文件
与硬编码解耦合
-
配置文件(properties为例) -- 通常包含四个基本信息
url, username, password, driverClassName
(不同连接池工具的命名方式不一致,以Druid为例)
url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&rewriteBatchedStatements=true driverClassName=com.mysql.cj.jdbc.Driver username=root password=root initialSize=10 maxActive=10
-
-
读取配置文件
-
通常使用 Properties + InputStream
(以/src/druid.properties
为例)private static DataSource source = null; /* 工具类的静态代码块,在类加载时读取配置并生成 DataSource */ static { try { Properties pros = new Properties(); /* 这里是一种读取方式 */ InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties"); pros.load(is); // DruidDataSourceFactory 读取配置并返回 DataSource对象 source = DruidDataSourceFactory.createDataSource(pros); } catch (Exception e) { throw new RuntimeException(e); } } /* 工具类提供获取 DataSource 的方法 */ public static DataSource getSource() { return source; }
-
-
获取连接 Connection
public static Connection getConnection() throws SQLException { return source.getConnection(); }
-
关闭资源
Connection, Statement, ResultSet/* 重载closeResource */ public static void closeResource(Connection conn, Statement ps) { closeResource(conn, ps, null); } /* 重载closeResource */ public static void closeResource(Connection conn, Statement ps, ResultSet rs) { // 关闭资源 try { if (ps != null) ps.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (rs != null) rs.close(); } catch (SQLException e) { e.printStackTrace(); } }
ORM 编写 对象类和 DAO
ORM
Object Relational Mapping,简称ORM,以将数据库表中查询记录封装为类对象(称为Bean),便于编程语言操作。
例如表如下
Field | Type | Null | Key | Default | Extra |
---|---|---|---|---|---|
id | int | NO | PRI | NULL | auto_increment |
name | varchar(15) | YES | NULL | ||
varchar(20) | YES | NULL | |||
birth | date | YES | NULL | ||
photo | mediumblob | YES | NULL |
Bean
Bean中变量命的命名与SQL中有区别,并且表中请不要使用SQL已有的字段如 order 命名,例如(命名不一致的应使用别名)
select first_name as firstName from yourtable;
SQL Col Name | Bean Field Name |
---|---|
first_name | firstName |
last_find_time | lastFindTime |
close_time | closeTime |
import java.sql.Date;
public class Customer {
private int id;
private String name;
private String email;
private Date birth;
/* constructor */
public Customer() {super();}
public Customer(int id, String name, String email, Date birth) {
this.id = id;
this.name = name;
this.email = email;
this.birth = birth;
}
/* getter setter */
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 +
'}';
}
/* 省略 equals & hashCode */
}
DAO
data access object
DAO是和数据库的某一张表一一对应的,其中封装了增删改查基本等原子操作
通常建立如下的继承关系结构
(抽象类, 无抽象方法, 定义通用的增删改查操作, 多数工具包已经提供) BaseDAO
(接口, 定义对应表的原子操作) CustomerDAO
(实现类) CustomerDAOImpl extends BaseDAO implements CustomerDAO
BaseDAO
/**
* DAO: data(base) access object
* java代码操作数据库表的基本方法
*/
public abstract class BaseDAO {
/**
* SQL中占位符的数量应当与args形参列表的数目一致
* @param conn 传入的SQL 连接, 本方法不会关闭 conn连接
* @param sql SQL
* @param args 形参列表
* @return 表中改动的条目数
*/
public int update(Connection conn, String sql, Object ... args) {
int query = 0;
QueryRunner queryRunner = null;
try {
queryRunner = new QueryRunner();
query = queryRunner.update(conn, sql, args);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JDBCUtils.closeResource(null, null);
}
return query;
}
/**
* 针对不同表的通用查询操作,返回一条查询记录
* @param conn sql连接
* @param clazz 要返回的对象 T.class
* @param sql sql语句
* @param args sql语句的补充参数
* @return 返回一个 T 对象
* @param <T> 返回对象的类型
*/
public <T> T getInstance(Connection conn, Class<T> clazz, String sql, Object...args) {
T query = null;
QueryRunner queryRunner = null;
BeanHandler<T> beanHandler = null;
try {
queryRunner = new QueryRunner();
beanHandler = new BeanHandler<>(clazz);
query = queryRunner.query(conn, sql, beanHandler, args);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
// 迷惑操作, 步骤得有
JDBCUtils.closeResource(null, null, null);
}
return query;
}
/**
* 针对不同表的通用查询操作,返回查询集合
* @param conn sql连接
* @param clazz 要返回的对象 T.class
* @param sql sql语句
* @param args sql语句的补充参数
* @return 返回一个 List<T> 对象
* @param <T> 返回对象的类型
*/
public <T> List<T> getForList(Connection conn, Class<T> clazz, String sql, Object...args) {
BeanListHandler<T> beanListHandler = null;
QueryRunner queryRunner = null;
List<T> query = null;
try {
beanListHandler = new BeanListHandler<>(clazz);
queryRunner = new QueryRunner();
query = queryRunner.query(conn, sql, beanListHandler, args);
} catch (SQLException | SecurityException |
IllegalArgumentException e) {
throw new RuntimeException(e);
} finally {
JDBCUtils.closeResource(null, null, null);
}
return query;
}
/**
* 允许 SQL 中使用 count(*)\ max(col) 等组函数
* @param conn sql连接
* @param sql sql语句
* @param args sql语句的补充参数
* @return 返回查询值
* @param <E> 返回值的类型
*/
public <E> E getValue(Connection conn, String sql, Object...args) {
E query = null;
QueryRunner queryRunner = null;
ScalarHandler scalarHandler = new ScalarHandler();
try {
queryRunner = new QueryRunner();
query = (E)queryRunner.query(conn, sql, scalarHandler);
} catch (SQLException ex) {
throw new RuntimeException(ex);
} finally {
JDBCUtils.closeResource(null, null, null);
}
return query;
}
}
CustomerDAO
public interface CustomerDAO {
/**
* 将 customer 添加到数据表中
* @param conn sql连接
* @param customer 待添加的 Customer 对象
*/
void inset(Connection conn, Customer customer);
/**
* 根据指定的id删除一条记录
* @param conn sql连接
* @param id 待删除的id
*/
void deleteById(Connection conn, int id);
/**
* 针对于内存中的 Customer 对象, 去修改 id=customer.id 的记录
* @param conn sql连接
* @param customer 内存中的 Customer 对象
*/
void updateById(Connection conn, Customer customer);
/**
* 根据指定的id, 查询对应的 Customer
* @param conn sql连接
* @param id 待查找的id
* @return Customer对象
*/
Customer getCustomerById(Connection conn, int id);
/**
* 查询表中所有记录
* @param conn sql连接
* @return List<Customer>对象
*/
List<Customer> getAll(Connection conn);
/**
*
* 返回表的条目数
* @param conn sql连接
* @return 条目数
*/
Long getCount(Connection conn);
/**
* 返回数据表中的最大生日
* @param conn sql连接
* @return java.sql.Date 最大生日
*/
Date getMaxBirth(Connection conn);
}
CustomerDAOImpl
public class CustomerDAOImpl extends BaseDAO implements CustomerDAO {
@Override
public void inset(Connection conn, Customer customer) {
String sql = "insert into customers(name, email, birth) values (?,?,?)";
update(conn, sql, customer.getName(), customer.getEmail(), customer.getBirth());
}
@Override
public void deleteById(Connection conn, int id) {
String sql = "delete from customers where id = ?";
update(conn, sql, id);
}
@Override
public void updateById(Connection conn, Customer customer) {
String sql = "update customers set name=?, email=?, birth=? where id=?";
update(conn, sql, customer.getName(), customer.getEmail(), customer.getBirth(), customer.getId());
}
@Override
public Customer getCustomerById(Connection conn, int id) {
String sql = "select id, name, email, birth from customers where id = ?";
return getInstance(conn, Customer.class, sql, id);
}
@Override
public List<Customer> getAll(Connection conn) {
String sql = "select id, name, email, birth from customers";
return getForList(conn, Customer.class, sql);
}
@Override
public Long getCount(Connection conn) {
String sql = "select count(*) from customers";
return getValue(conn, sql);
}
@Override
public Date getMaxBirth(Connection conn) {
String sql = "select max(birth) from customers";
return getValue(conn, sql);
}
}
测试用例
import org.junit.jupiter.api.Test;
class CustomerDAOImplTest {
private CustomerDAOImpl dao = new CustomerDAOImpl();
@Test
void inset() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
java.util.Date date = simpleDateFormat.parse("2022-08-26");
Customer customer = new Customer(1, "猪鼻二号", "[email protected]", new Date(date.getTime()));
dao.inset(conn, customer);
System.out.println("添加成功");
} catch (SQLException | ParseException e) {
throw new RuntimeException(e);
} finally {
JDBCUtils.closeResource(conn, null);
}
}
@Test
void deleteById() {
}
@Test
void updateById() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
java.util.Date date = simpleDateFormat.parse("1914-01-17");
Customer customer = new Customer(28, "猪鼻二号", "[email protected]", new Date(date.getTime()));
dao.updateById(conn, customer);
System.out.println("修改成功");
} catch (SQLException | ParseException e) {
throw new RuntimeException(e);
} finally {
JDBCUtils.closeResource(conn, null);
}
}
@Test
void getCustomerById() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
Customer customer = dao.getCustomerById(conn, 28);
System.out.println(customer);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JDBCUtils.closeResource(conn, null);
}
}
@Test
void getAll() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
List<Customer> all = dao.getAll(conn);
all.forEach(System.out::println);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JDBCUtils.closeResource(conn, null);
}
}
@Test
void getCount() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
Long count = dao.getCount(conn);
System.out.println("表中的记录数为:" + count);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JDBCUtils.closeResource(conn, null);
}
}
@Test
void getMaxBirth() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
Date maxBirth = dao.getMaxBirth(conn);
System.out.println("表中最大的生日是:" + maxBirth);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
JDBCUtils.closeResource(conn, null);
}
}
}
注意事项
本文由于直接使用 Druid + Apache Commons DbUtils缩减代码量,java.sql 中两个重要的类
- Statement/ PreparedStatement
- ResultSet
-
其中 Statement 由于无法解决SQL 注入,并且每一次调用他都要验证一次 SQL 语句,不利于实际开发使用,因此多采用 PreparedStatement,获取方式为
ps = conn.prepareStatement(sql);
-
PreparedStatement预编译了SQL语句,如
String sql = "select name, email from customers where name = ?"; ps.setObject(1, "张学友");
也即预编译一次,可以设置多次执行多次
ResultSet rs = ps.executeQuery(); // 以查询为例, 也可以调用 ps.execute()
-
ResultSet的获取方式见2,是一个集合,可通过获取每一行的数据
// 处理结果集 while (rs.next()) { int id = rs.getInt(1); // id String name = rs.getString(2); // name String email = rs.getString(3);// email Date date = rs.getDate(4); // birth Customer customer = new Customer(id, name, email, date); System.out.println(customer); }
总之,原生JDBC使用非常不便,前文中的DAO仅是列出了一张表的原子操作,如果多张表联查,复杂操作等,使用JDBC会非常麻烦
END~!
标签:JDBC,public,Connection,应用,sql,null,id,conn From: https://www.cnblogs.com/jentreywang/p/16632587.html