赞
踩
为了项目代码的可移植性,可维护性,SUN 公司从最初就制定了 Java 程序连接各种数据库的统一接口规范。这样的话,不管是连接哪一种 DBMS 软件,Java 代码可以保持一致性。
因为各个数据库厂商的 DBMS 软件各有不同,那么内部如何通过 sql 实现增、删、改、查等管理数据,只有这个数据库厂商自己更清楚,因此把接口规范的实现交给各个数据库厂商自己实现。
Java 程序打成的一种压缩包格式,你可以将这些 jar 包引入你的项目中,然后你可以使用这个 Java 程序中类和方法以及属性。
preparedstatement 使用为重点
驱动版本的选择
MySQL 版本 | 推荐驱动版本 | 备注 |
---|---|---|
MySQL 5.5.x | 5.0.x | com.mysql.jdbc.Driver |
MySQL 5.7.x | 5.1.x | com.mysql.jdbc.Driver |
MySQL 8.x | 8.0.x | 建议:8.0.25+ 省略时区设置 com.mysql.cj.jdbc.Driver |
在maven工程中导入依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
不推荐使用
,会被SQL注入攻击
例如:在拼接SQL语句的时候,可以凭借password = ‘’ or 1 = 1,这样查询即使password不正确也是返回true
//执行SQL语句 [动态SQL语句,需要字符串拼接]
String sql = "select * from t_user where account = '"+account+"' and password = '"+password+"' ;";
1、准备数据
use `database-work`;
create table if not exists user (
username varchar(100),
password varchar(100)
);
insert into user values ("root","123456");
select * from user;
2、Java实现代码
public class StatementQueryPart {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
// 1、注册驱动
//导入依赖:8+版本的驱动要带cj,低版本的需要导入 com.mysql.jdbc.Driver
/*
* 下面方法会new两次对象,增加了资源消耗不推荐使用
* DriverManager.registerDriver(new Driver());调用两次。不用
* new Driver() 频繁修改不优雅
*/
// 通过反射的方式去注册驱动,推荐使用
Class.forName("com.mysql.cj.jdbc.Driver");
// 2、连接数据库
/*
* java连接数据库肯定是调用某个方法,方法也需要填入数据库的基本信息:
* 数据库ip地址: 192.168.45.128
* 数据库端口号:3306
* 账号:root
* 密码:123456
* 连接数据库的名称:databases-work
* 扩展路径参数(了解):
* 8.0.25以后,自动识别时区 serverTimezone=Asia/Shanghai 不用添加
* 8版本以后, 默认使用utf-8格式, useUnicode=true&characterEncoding=utf8&useSSL=true 不用添加
* serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=true
*/
String url = "jdbc:mysql://192.168.45.128:3306/database-work";
String username = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, username, password);
// 3、创建发送sql语句对象
Statement statement = connection.createStatement();
// 4、执行查询
String sql = "select * from user";
/*
查询语句使用 executeQuery
增、删、改 使用 executeUpdate
DML: delete、insert、update
DQL: select、from、where、group by、having、order by、limit
参数: sql 非 DQL
返回: int
情况1: DML 返回影响的行数, 例如: 删除了三条数据 return 3; 插入了两条 return 2; 修改了 0 条 return 0;
情况2: 非 DML return 0;
int row = executeUpdate(sql)
参数: sql DQL
返回: resultSet 结果封装对象
ResultSet resultSet = executeQuery(sql);
*/
ResultSet resultSet = statement.executeQuery(sql);
// 5、结果集解析
while (resultSet.next()) {
System.out.println("username -> " + resultSet.getString("username") + " , password -> " + resultSet.getString("password"));
}
// 4、关闭资源
resultSet.close();
statement.close();
connection.close();
}
}
推荐使用
,通过掉方法传入参数不会有SQL注入的风险
public class PreparedStatementQueryPart {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
// 1、注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2、获取连接
String url = "jdbc:mysql://192.168.45.128:3306/database-work";
String username = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, username, password);
// 3、创建语句
// SQL语句动态部分使用 ?占位符
String sql = "select * from user where username = ? and password = ?";
// 创建一个预编译的SQL语句对象,没有执行这个SQL
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//占位符赋值
//给占位符赋值! 从左到右,从1开始!
/*
* int 占位符的下角标
* object 占位符的值
*/
preparedStatement.setObject(1,"root");
preparedStatement.setObject(2,"123456");
// 4、执行查询
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
System.out.println("username -> " + resultSet.getString("username") + " , password -> " + resultSet.getString("password"));
}
// 5、关闭资源
preparedStatement.close();
connection.close();
}
}
1、数据库的插入
// 插入数据
@Test
public void testInsert() throws Exception{
//注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//获取连接
String url = "jdbc:mysql://192.168.45.128:3306/database-work";
String username = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, username, password);
String sql = "insert into user(username,password) values (?,?);";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//占位符赋值
preparedStatement.setString(1, "admin");
preparedStatement.setString(2, "123456");
//发送SQL语句
int rows = preparedStatement.executeUpdate();
//输出结果
System.out.println(rows);
//关闭资源close
preparedStatement.close();
connection.close();
}
2、数据库的修改
// 修改数据
@Test
public void testUpdate() throws Exception{
//注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//获取连接
String url = "jdbc:mysql://192.168.45.128:3306/database-work";
String username = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, username, password);
String sql = "update user set password =? where username =?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//占位符赋值
preparedStatement.setString(1, "123456789");
preparedStatement.setString(2, "admin");
//发送SQL语句
int rows = preparedStatement.executeUpdate();
//输出结果
System.out.println(rows);
//关闭资源close
preparedStatement.close();
connection.close();
}
3、数据库的删除
// 删除数据
@Test
public void testDelete() throws Exception{
//注册驱动
//获取连接
String url = "jdbc:mysql://192.168.45.128:3306/database-work";
String username = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, username, password);
String sql = "delete from user where username =?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//占位符赋值
preparedStatement.setString(1, "admin");
//发送SQL语句
int rows = preparedStatement.executeUpdate();
//输出结果
System.out.println(rows);
//关闭资源close
preparedStatement.close();
connection.close();
}
4、数据库的查询
// 数据查询
@Test
public void testQuery() throws Exception {
//注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//获取连接
String url = "jdbc:mysql://192.168.45.128:3306/database-work";
String username = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, username, password);
String sql = "select * from user";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//发送SQL语句
ResultSet resultSet = preparedStatement.executeQuery();
//输出结果
while (resultSet.next()) {
System.out.println("username -> " + resultSet.getString("username") + " , password -> " + resultSet.getString("password"));
}
//关闭资源close
preparedStatement.close();
connection.close();
}
1、使用场景
java程序获取插入数据时mysql维护自增长维护的主键id值,这就是主键回显,主表默认增长,但是从表不知道值,所以要主键回显给从表知道
作用: 在多表关联插入数据时,
一般主表的主键都是自动生成的,所以在插入数据之前无法知道这条数据的主键,但是从表需要在插入数据之前就绑定主表的主键,这时可以使用主键回显技术
2、实现代码
/**
* 返回插入的主键!
* 主键:数据库帮助维护的自增长的整数主键!
*/
@Test
public void returnPrimaryKey() throws SQLException, ClassNotFoundException {
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://192.168.45.128:3306/database-work";
String username = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, username, password);
String sql = "insert into user(username,password) values(?,?)";
/*
* TODO: 第二个参数填入 1 | Statement.RETURN_GENERATED_KEYS
* 告诉statement携带回数据库生成的主键!
*/
PreparedStatement preparedStatement = connection.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
preparedStatement.setString(1,"admin");
preparedStatement.setString(2,"123456");
// 执行SQL语句 【注意:不需要传入SQL语句】 DML
preparedStatement.executeUpdate();
// 获取生成的主键结果集
ResultSet generatedKeys = preparedStatement.getGeneratedKeys();
// 移动到结果集的第一行(即生成的主键值所在的行)
generatedKeys.next();
// 从结果集中获取自增主键值,参数 1 表示获取第一列的值,即自增主键值
int anInt = generatedKeys.getInt(1);
// 输出自增主键值
System.out.println("anInt = " + anInt);
// 释放资源
statement.close();
connection.close();
}
可以提升大量数据插入时的效率
/**
*改动了三处:(1)路径(2)必写values,且后面不加;(3)装货addBatch()最后executeBatch();
* 批量细节:
* 1.url?rewriteBatchedStatements=true
* 2.insert 语句必须使用 values
* 3.语句后面不能添加分号;
* 4.语句不能直接执行,每次需要装货 addBatch() 最后 executeBatch();
* 批量插入优化!
*/
@Test
public void batchInsertYH() throws Exception{
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://192.168.45.128:3306/database-work?rewriteBatchedStatements=true";
String username = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, username, password);
String sql = "insert into user(username,password) values(?,?)";
PreparedStatement statement = connection.prepareStatement(sql);
for (int i = 0; i < 10000; i++) {
statement.setString(1,"admin");
statement.setString(2,"123456");
statement.addBatch();
}
// 执行批量插入数据的sql
statement.executeBatch();
connection.close();
}
1、事务的概念
数据库事务就是一种SQL语句执行的缓存机制,不会单条执行完毕就更新数据库数据,最终根据缓存内的多条语句执行结果统一判定!
一个事务内所有语句都成功及事务成功,我们可以触发commit提交事务来结束事务,更新数据!
一个事务内任意一条语句失败,及事务失败,我们可以触发rollback回滚结束事务, 数据回到事务之前状态!
2、数据表
CREATE TABLE t_bank(
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '账号主键',
account VARCHAR(20) NOT NULL UNIQUE COMMENT '账号',
money INT UNSIGNED COMMENT '金额,不能为负值') ;
INSERT INTO t_bank(account,money) VALUES
('ergouzi',1000),('lvdandan',1000);
3、代码执行流程
4、实现代码
BankDao.java
package com.liuyi.bank;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class BankDao {
/**
* 加钱方法
* @param account
* @param money
* @param connection 业务传递的connection和减钱是同一个! 才可以在一个事务中!
* @return 影响行数
*/
public int addMoney(String account, int money, Connection connection) throws ClassNotFoundException, SQLException {
String sql = "update t_bank set money = money + ? where account = ? ;";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//占位符赋值
preparedStatement.setObject(1, money);
preparedStatement.setString(2, account);
//发送SQL语句
int rows = preparedStatement.executeUpdate();
//输出结果
System.out.println("加钱执行完毕!");
//关闭资源close
preparedStatement.close();
return rows;
}
/**
* 减钱方法
* @param connection 业务传递的connection和加钱是同一个! 才可以在一个事务中!
* @return 影响行数
*/
public int subMoney(String account, int money,Connection connection) throws ClassNotFoundException, SQLException {
String sql = "update t_bank set money = money - ? where account = ? ;";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//占位符赋值
preparedStatement.setObject(1, money);
preparedStatement.setString(2, account);
//发送SQL语句
int rows = preparedStatement.executeUpdate();
//输出结果
System.out.println("减钱执行完毕!");
//关闭资源close
preparedStatement.close();
return rows;
}
}
BankService.java
package com.liuyi.bank;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class BankService {
private final BankDao bankDao;
public BankService() {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
this.bankDao = new BankDao();
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
//一个事物最基本的是在同一个连接中connection,一个转账方法是一个事物,将connection传入dao
//实现层即可,dao层不用关闭connection,由事物统一关闭
/**
* 转账业务方法
* @param addAccount 加钱账号
* @param subAccount 减钱账号
* @param money 金额
*/
public void transfer(String addAccount, String subAccount, int money) {
Connection connection = null;
try {
String username = "root";
String password = "123456";
String url = "jdbc:mysql://192.168.45.128:3306/database-work?rewriteBatchedStatements=true";
connection = DriverManager.getConnection(url, username, password);
connection.setAutoCommit(false);
bankDao.addMoney(addAccount, money, connection);
bankDao.subMoney(subAccount, money, connection);
connection.commit();
System.out.printf("--- 转账成功:[%s] 收到 %d, [%s] 发送 %d ---\n", addAccount, money, subAccount, money);
} catch (Exception e) {
if (connection != null) {
try {
connection.rollback();
} catch (SQLException se) {
System.err.println("回滚错误!");
throw new RuntimeException(se);
}
}
System.err.println("转账失败!详情:");
e.printStackTrace();
} finally {
if (connection != null) {
try {
connection.close();
} catch (SQLException se) {
System.err.println("关闭连接失败!");
}
}
}
}
}
BankTest.java
package com.liuyi.bank;
import org.junit.Test;
public class BankTest {
@Test
public void testBank() throws Exception {
BankService bankService = new BankService();
bankService.transfer("root", "admin",500);
}
}
1、不使用数据库连接池的缺点
(1)不使用数据库连接池,每次都通过DriverManager获取新连接,用完直接抛弃断开, 连接的利用率太低,太浪费。
(2)对于数据库服务器来说,压力太大了。我们数据库服务器和Java程序对连接数也无法控制 ,很容易导致数据库服务器崩溃。
2、数据库连接池的优点
我们可以建立一个连接池,这个池中可以容纳一定数量的连接对象,一开始, 我们可以先替用户先创建好一些连接对象,等用户要拿连接对象时,就直接从池中拿, 不用新建了,这样也可以节省时间。然后用户用完后,放回去,别人可以接着用。
可以提高连接的使用率。当池中的现有的连接都用完了,那么连接池可以向服务器申 请新的连接放到池中。
直到池中的连接达到“最大连接数”,就不能在申请新的连接了,如果没有拿到连接的用户只能等待。
1、硬编码方式
/**
* 创建druid连接池对象,使用硬编码进行核心参数设置!
* 必须参数: 账号
* 密码
* url
* driverClass
* 非必须参数:
* 初始化个数
* 最大数量等等 不推荐设置
注意他要做的,注册驱动,获取连接,规定最大数量
直接使用代码设置连接池连接参数方式
*/
@Test
public void druidHard() throws SQLException {
//1.连接池对象
DruidDataSource dataSource = new DruidDataSource();
//2.设置四个必须参数
//2.设置四个必须参数
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("123456");
dataSource.setUrl("jdbc:mysql://192.168.45.128:3306/database-work?rewriteBatchedStatements=true");
//非必须
dataSource.setInitialSize(5); // 初始化数量
dataSource.setMaxActive(10); // 最大数量
//3.获取连接
Connection connection = dataSource.getConnection();
// JDBC的步骤 正常curd
//4.回收连接
connection.close();
}
2、软编码方式
使用 druid.properties 配置文件的方式
druid.properties
driverClassName=com.mysql.cj.jdbc.Driver
username=root
password=123456
url=jdbc:mysql://192.168.45.128:3306/database-work?rewriteBatchedStatements=true
druid配置
配置 | 缺省 | 说明 |
---|---|---|
name | 配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:”DataSource-” + System.identityHashCode(this) | |
jdbcUrl | 连接数据库的url,不同数据库不一样。例如:mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto | |
username | 连接数据库的用户名 | |
password | 连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里:https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter | |
driverClassName | 根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置下) | |
initialSize | 0 | 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 |
maxActive | 8 | 最大连接池数量 |
maxIdle | 8 | 已经不再使用,配置了也没效果 |
minIdle | 最小连接池数量 | |
maxWait | 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 | |
poolPreparedStatements | false | 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。 |
maxOpenPreparedStatements | -1 | 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100 |
validationQuery | 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。 | |
testOnBorrow | true | 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 |
testOnReturn | false | 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 |
testWhileIdle | false | 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 |
timeBetweenEvictionRunsMillis | 有两个含义: 1)Destroy线程会检测连接的间隔时间2)testWhileIdle的判断依据,详细看testWhileIdle属性的说明 | |
numTestsPerEvictionRun | 不再使用,一个DruidDataSource只支持一个EvictionRun | |
minEvictableIdleTimeMillis | ||
connectionInitSqls | 物理连接初始化的时候执行的sql | |
exceptionSorter | 根据dbType自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接 | |
filters | 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall | |
proxyFilters | 类型是List,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系 |
druid声明代码
/**
* 不直接在java代码编写配置文件!
* 利用工厂模式,传入配置文件对象,创建连接池!
*/
@Test
public void druidHard() throws Exception {
// 获取外部配置文件 Properties
Properties properties = new Properties();
// 获取输入流,利用类加载器
InputStream druidFile = DruidDbUtils.class.getClassLoader().getResourceAsStream("druid.properties");
properties.load(druidFile);
// 使用连接池工具类的工厂模式,创建连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
// 获取连接
Connection connection = dataSource.getConnection();
// 关闭连接
connection.close();
}
为了避免每次都要创建连接池,我们封装一个工具类,内部包含连接池对象,同时对外提供连接的方法和回收连接的方法!
druid.properties
driverClassName=com.mysql.cj.jdbc.Driver
username=root
password=123456
url=jdbc:mysql://192.168.45.128:3306/database-work?rewriteBatchedStatements=true
JDBCUtilsVersion1.java
package com.liuyi.Utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class JDBCUtilsVersion1 {
private static final DataSource ds;
static {
Properties properties = new Properties();
try {
properties.load(JDBCUtilsVersion1.class.getClassLoader().getResourceAsStream("druid.properties"));
ds = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static Connection getConnection() throws SQLException {
return ds.getConnection(); //这么写,不能保证同一个线程,两次getConnection()得到的是同一个Connection对象
// 如果不能保证是同一个连接对象,就无法保证事务的管理
}
public static void releaseConnection(Connection conn) throws SQLException {
conn.setAutoCommit(true);
conn.close(); // 还给连接池
}
}
优化工具类v1.0版本,考虑事务的情况下!如何一个线程的不同方法获取同一个连接!!!!
ThreadLocal的介绍:
线程本地变量:为同一个线程存储共享变量
JDK 1.2的版本中就提供java.lang.ThreadLocal,为解决多线程程序的并发问题提供了一种新的思路。 使用这个工具类可以很简洁地编写出优美的多线程程序。通常用来在在多线程中管理共享数据库连接、 Session等
ThreadLocal用于保存某个线程共享变量,原因是在Java中,每一个线程对象中都有一个 ThreadLocalMap<ThreadLocal, Object>,其key就是一个ThreadLocal,而Object即为该线程的 共享变量。而这个map是通过ThreadLocal的set和get方法操作的。对于同一个static ThreadLocal, 不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。
1、ThreadLocal对象.get: 获取ThreadLocal中当前线程共享变量的值。
2、ThreadLocal对象.set: 设置ThreadLocal中当前线程共享变量的值。
3、ThreadLocal对象.remove: 移除ThreadLocal中当前线程共享变量的值。
JDBCUtilsVersion2.java
package com.liuyi.Utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
//事物时,Service和dao属于同一线程,不用再传参数了
/*
这个工具类的作用就是用来给所有的SQL操作提供“连接”,和释放连接。
这里使用ThreadLocal的目的是为了让同一个线程,在多个地方getConnection得到的是同一个连接。
这里使用DataSource的目的是为了(1)限制服务器的连接的上限(2)连接的重用性等
*/
public class JDBCUtilsVersion2 {
private static DataSource ds;
private static final ThreadLocal<Connection> tl = new ThreadLocal<>();
static{//静态代码块,JDBCToolsVersion1类初始化执行
try {
Properties pro = new Properties();
pro.load(ClassLoader.getSystemResourceAsStream("druid.properties"));
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
//获取连接
Connection connection = tl.get();
if(connection == null){//当前线程还没有拿过连接,就给它从数据库连接池拿一个
connection = ds.getConnection();
//连接存入共享变量中
tl.set(connection);
}
return connection;
}
public static void releaseConnection() throws SQLException {
Connection connection = tl.get();
if(connection != null){
tl.remove();
//避免还给数据库连接池的连接不是自动提交模式(建议)
//因为其在业务类中会关闭事务的自动提交
connection.setAutoCommit(true);
connection.close();
}
}
}
基本上每一个数据表都应该有一个对应的DAO接口及其实现类,发现对所有表的操作(增、删、改、查)代码重复度很高,所以可以抽取公共代码,给这些DAO的实现类可以抽取一个公共的父类,我们称为BaseDao
针对DQL查询和非DQL进行,分成两类
package com.liuyi.Utils;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
public abstract class BaseDao {
/*
通用的增、删、改的方法
String sql:sql
Object... args:给sql中的?设置的值列表,可以是0~n
*/
protected int update(String sql, Object... args) throws SQLException {
try (Connection connection = JDBCUtilsVersion2.getConnection();
PreparedStatement ps = connection.prepareStatement(sql)) {
// 设置?的值
if (args != null && args.length > 0) {
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);// ?的编号从1开始,不是从0开始,数组的下标是从0开始
}
}
// 执行sql
return ps.executeUpdate();
}
}
/*
通用的查询多个Javabean对象的方法,例如:多个员工对象,多个部门对象等
这里的clazz接收的是T类型的Class对象,
如果查询员工信息,clazz代表Employee.class,
如果查询部门信息,clazz代表Department.class,
返回List<T> list
*/
protected <T> ArrayList<T> query(Class<T> clazz, String sql, Object... args) throws Exception {
try (Connection connection = JDBCUtilsVersion2.getConnection();
PreparedStatement ps = connection.prepareStatement(sql)) {
// 设置?的值
if (args != null && args.length > 0) {
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);//?的编号从1开始,不是从0开始,数组的下标是从0开始
}
}
ArrayList<T> list = new ArrayList<>();
try (ResultSet res = ps.executeQuery()) {
ResultSetMetaData metaData = res.getMetaData();
int columnCount = metaData.getColumnCount();//获取结果集列数
while (res.next()) {
Constructor<T> constructor = clazz.getDeclaredConstructor();
T t = constructor.newInstance();//要求这个类型必须有公共的无参构造
for (int i = 1; i <= columnCount; i++) {
Object value = res.getObject(i);
String columnName = metaData.getColumnLabel(i);//获取第i列的字段名或字段的别名
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t, value);
}
list.add(t);
}
}
return list;
}
}
protected <T> T queryBean(Class<T> clazz, String sql, Object... args) throws Exception {
ArrayList<T> list = query(clazz, sql, args);
return list.isEmpty() ? null : list.get(0);
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。