单纯JDBC连接数据库写法
Connection conn = null;
Statement stml = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql:///db3", "root", "root");
stml = conn.createStatement();
String sql = "select * from account";
rs = stml.executeQuery(sql);
while(rs.next()){
int id = rs.getInt(1);
String name = rs.getString(2);
double balance = rs.getDouble(3);
System.out.println(id + name + balance);
}
} catch (SQLException | ClassNotFoundException throwables) {
throwables.printStackTrace();
}finally {
if(rs != null){ // 流的关闭不要合并
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(stml != null){
try {
stml.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
优化思路:去除代码过度冗余
-
载入驱动,获取连接代码重复
- 关闭文件流代码重复
JDBC工具类创建
public class JDBCUtils {
private static Connection conn = null;
private static Statement stmt = null;
private static final String URL = "jdbc:mysql://localhost:3306/db3";
private static final String DRIVER= "com.mysql.jdbc.Driver";
private static final String USER = "root";
private static final String PWD = "root";
static{
try {
Class.forName(DRIVER);
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306","root","root");
} catch (SQLException | ClassNotFoundException throwables) {
throwables.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL,USER,PWD);
}
public static void close(Statement stmt, Connection conn){
close(null,stmt,conn);
}
public static void close(ResultSet rs , Statement stmt, Connection conn){
if(rs != null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(stmt != null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
为避免数据注入问题,将Statment改为PrepareStatement来优化
public class JdbcDemo {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
String sql = "select * from account";
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
double balance = rs.getDouble("balance");
System.out.println(id + name + balance);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JDBCUtils.close(rs,pstmt,conn);
}
}
}
数据库连接池
定义:存放数据库连接的集合容器,当系统初始化好后被容器被创建,容器会申请连接对象,用户访问完成后将对象归还容器。节约资源,用户访问高效
Druid数据连接池:
- 导入jar包
- 定义配置文件:properities
- 加载配置文件
- 获取连接池对象:DruidDataSourceFactory
- 获取连接:getConnection
JDBCUtils连接池版本
public class JDBCUtils {
private static final String DRIVER = "com.mysql.jdbc.Driver";
private static final String URL = "jdbc:mysql://127.0.0.1:3306/db3";
private static final String USER = "root";
private static final String PASSWORD = "root";
private static DruidDataSource ds = new DruidDataSource();
static {
ds.setDriverClassName(DRIVER);
ds.setUrl(URL);
ds.setUsername(USER);
ds.setPassword(PASSWORD);
}
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
public static void close(Statement stmt, Connection conn){
close(null,stmt,conn);
}
public static void close(ResultSet rs, Statement stmt, Connection conn){
if(rs != null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(stmt != null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
public static DataSource getDataSource(){
return ds;
}
}
全流程优化
SpringJDBC和DBUtils对全流程进行优化,其中DBUtils虽然对全流程没把优化做到极致但也足够用,但SpringJDBC的事务用法我还在摸索之中
全流程优化的思路
- PrepareStatement的创建,sql封装,语句执行可以包装成一个函数来完成
- 结果集的返回可以指定特定类型并进行优化
- 通过数据库来快速创建对象集合
JDBCUtiles最终版本分文件编写
public class JDBCUtils {
private static DataSource ds;
static{ // src根目录下配置druid.properties文件
try {
Properties pro = new Properties();
pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
public static DataSource getDataSource(){
return ds;
}
}
放在src根目录下druid.properties文件
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/db3
username=root
password=root
initialSize=5
maxActive=10
maxWait=3000
JDBCUtiles最终版本单文件编写
public class JDBCUtils0 {
private static final String DRIVER = "com.mysql.jdbc.Driver";
private static final String URL = "jdbc:mysql://127.0.0.1:3306/db3";
private static final String USER = "root";
private static final String PASSWORD = "root";
private static DruidDataSource ds = new DruidDataSource();
static {
ds.setDriverClassName(DRIVER);
ds.setUrl(URL);
ds.setUsername(USER);
ds.setPassword(PASSWORD);
}
public static DataSource getDataSource() {
return ds;
}
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
}
DBUtils全流程优化
使用方式
- 导入jar包
- 创建QueryRunner对象,依赖于数据源DataSource
QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
3. 调用QueryRunner的方法来完成CRUD的操作
增删改优化:update
public class JdbcDemo2 {
private QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
@Test
public void test01() throws SQLException {
String sql = "update account set balance = 20000 where id = 1";
int count = qr.update(sql);
System.out.println(count);
}
@Test
public void test02() throws SQLException {
String sql = "insert into account values (?,?,?)";
Object[] params = {6,"关羽",10000};
int count = qr.update(sql, params);
System.out.println(count);
}
@Test
public void test03() throws SQLException {
String sql = "delete from account where id = ?";
Object[] params = {6};
qr.update(sql,params);
}
}
结果查询优化
- MapHandler():获取单条记录,直接形成单个map
- MapListHandler():获取多条记录,形成map集合
- ScalarHandler():根据记录定义返回值类型,一般与聚合函数集合
public class JdbcDemo3 {
private QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
// 使用MapHandler()返回的是一行数据的map的集合体
@Test
public void test01() throws SQLException {
String sql = "select * from account where id = ?";
Object[] params = {6};
Map<String, Object> maps = qr.query(sql, new MapHandler(), params);
System.out.println(maps);
}
// 使用MapListHandler()返回的是多行数据的map集合的list集合
@Test
public void test02() throws SQLException {
String sql = "select * from account";
List<Map<String, Object>> maplist = qr.query(sql, new MapListHandler());
for (Map<String,Object> map : maplist){
System.out.println(map);
}
}
// 使用ScalarHandler可以自定义返回结果,默认Object
@Test
public void test03() throws SQLException {
String sql = "select count(id) from account";
Long total = qr.query(sql,new ScalarHandler<Long>());
System.out.println(total);
}
}
对象集合创建优化
BeanHandler:将单行数据转换为JavaBean对象。
BeanListHandler:将结果封装为JavaBean对象
public class JdbcDemo4 {
private QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
@Test
public void test01() throws SQLException {
String sql = "select * from account";
List<Account> accounts = qr.query(sql, new BeanListHandler<Account>(Account.class));
for (Account a : accounts){
System.out.println(a);
}
}
}
数据库转为对象数组的代码封装
注意点:对象封装需要set方法,变量名与列名名称需相同但不受大小写影响
public static <T> List<T> getListFromResultSet(ResultSet set, Class<T> clz) throws Exception {
List<T> list = new ArrayList<T>();
ResultSetMetaData md = set.getMetaData();
int count = md.getColumnCount();
String[] clomonNames = new String[count];
for (int i = 1; i <= count; i++) {
clomonNames[i - 1] = md.getColumnName(i);
}
T obj = null;
Method[] methods = clz.getMethods();
while (set.next()) {
obj = clz.newInstance();
for (Method method : methods) {
for (String cn : clomonNames) {
if (method.getName().toLowerCase().endsWith(cn.toLowerCase()) && method.getName().startsWith("set")) {
String ptn = method.getParameterTypes()[0].getSimpleName();
if (ptn.equals("Integer")) {
method.invoke(obj, set.getInt(cn));
} else if (ptn.equals("Double")) {
method.invoke(obj, set.getDouble(cn));
} else {
method.invoke(obj, set.getString(cn));
}
}
}
}
list.add(obj);
}
return list;
}
事务的调用
由Connection调用,调用事务和CRUD必须是同一个Connection
public void test01() throws SQLException {
Connection conn = JDBCUtils.getConnection();
QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource());
try {
conn.setAutoCommit(false);
String sql1 = "update account set balance = balance - 1000 where id = 1";
String sql2 = "update account set balance = balance + 1000 where id = 2";
int i = 1 / 0;
qr.update(conn,sql1); // 核心,一定要保证事务开启的conn是同一个
qr.update(conn,sql2);
conn.commit();
} catch (SQLException throwables) {
conn.rollback();
throwables.printStackTrace();
}
}