概述
1. 获取数据库连接
lib 下添加数据库驱动。
右键【Build Path】⇒【Add to Build Path】
例:mysql-connector-java-8.0.23.jar
@Test
public void connectionTest01() throws SQLException {
// 获取Driver 实现类对象
Driver driver = new com.mysql.jdbc.Driver();
String url = "jdbc:mysql://localhost:3306/book";
Properties info = new Properties();
info.setProperty("user", "root");
info.setProperty("password", "tiger");
Connection conn = driver.connect(url, info);
System.out.println(conn);
}
// 程序中没有第三方api(new com.mysql.jdbc.Driver()),使程序具有更好的可移植性
@Test
public void connectionTest02() throws Exception {
// 获取Driver 实现类对象,使用反射
Class clazz = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver)clazz.newInstance();
String url = "jdbc:mysql://localhost:3306/book";
Properties info = new Properties();
info.setProperty("user", "root");
info.setProperty("password", "tiger");
Connection conn = driver.connect(url, info);
System.out.println(conn);
}
@Test
public void connectionTest03() throws Exception {
// 获取Driver 实现类对象,使用反射
Class clazz = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver)clazz.newInstance();
// 注册驱动
DriverManager.registerDriver(driver);
// 获取连接
String url = "jdbc:mysql://localhost:3306/book";
Connection conn = DriverManager.getConnection(url,"root","tiger");
System.out.println(conn);
}
@Test
public void connectionTest04() throws Exception {
// 加载驱动,mysql 的Driver 实现类进行了驱动的注册
Class.forName("com.mysql.jdbc.Driver");
// 获取连接
String url = "jdbc:mysql://localhost:3306/book";
Connection conn = DriverManager.getConnection(url,"root","tiger");
System.out.println(conn);
}
@Test
public void connectionTest05() throws Exception {
// 读取配置文件
InputStream inStream = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
// 从流中加载数据
Properties properties = new Properties();
properties.load(inStream);
String username = properties.getProperty("username");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driverClassName = properties.getProperty("driverClassName");
// 加载驱动
Class.forName(driverClassName);
// 获取连接
Connection conn = DriverManager.getConnection(url,username,password);
System.out.println(conn);
}
jdbc.properties
username=root
password=tiger
url=jdbc:mysql://localhost:3306/book
driverClassName=com.mysql.jdbc.Driver
2. 操作和访问数据库
2.1 Statement SQL 注入问题
用户名:1’ or
密码: =1 or ‘1’ = '1
String sql = "SELECT USER,PASSWORD FROM USER WHERE USER = '"+user+"'" AND PASSWORD = '"+password+"'";
SELECT USER,PASSWORD FROM USER WHERE USER = '1' AND PASSWORD = '=1 or '1' = '1'
使用PrepareStatement() 可以避免该问题。
2.2 PrepareStatement
PrepareStatement 是Statement 的子接口,预编译SQL 语句。
2.2.1 添加
// 添加
@Test
public void testInsert() {
// 获取连接
Connection conn = null;
PreparedStatement ps = null;
try {
// 读取配置文件
InputStream inStream = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
// 从流中加载数据
Properties properties = new Properties();
properties.load(inStream);
String username = properties.getProperty("username");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driverClassName = properties.getProperty("driverClassName");
// 加载驱动
Class.forName(driverClassName);
// 获取连接
conn = DriverManager.getConnection(url,username,password);
// 预编译SQL语句
String sql = "INSERT INTO t_user(name,pwd,email) VALUES(?,?,?)";
ps = conn.prepareStatement(sql);
ps.setString(1, "chengyu");
ps.setString(2, "123456");
ps.setString(3, "111qq.com");
ps.execute();
} catch (Exception e) {
e.printStackTrace();
} finally{
try {
if(ps != null)
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn != null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
2.2.2 更新
创建数据库连接、关闭的工具类
JDBCUtils.java:抽取数据库连接部分工具类,主要负责数据库的连接及关闭操作。
public class JDBCUtils {
public static Connection getConnection() throws Exception {
// 读取配置文件
InputStream inStream = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
// 从流中加载数据
Properties properties = new Properties();
properties.load(inStream);
String username = properties.getProperty("username");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driverClassName = properties.getProperty("driverClassName");
// 加载驱动
Class.forName(driverClassName);
// 获取连接
Connection conn = DriverManager.getConnection(url,username,password);
return conn;
}
public static void closeResource(Connection conn, Statement ps) {
try {
if(ps != null)
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn != null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
更新操作:
@Test
public void testUpdate() {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JDBCUtils.getConnection();
String sql = "update t_user set name = ? where id = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, "chengwei");
ps.setString(2, "1006");
ps.execute();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, ps);
}
}
2.2.3 删除
抽取增删改共通方法:
public void update(String sql,Object ...obj) {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
for(int i = 0; i < obj.length ; i++) {
ps.setObject(i + 1, obj[i]);
}
ps.execute();
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, ps);
}
}
@Test
public void testDelete() {
String sql = "delete from t_user where id = ?";
update(sql,1007);
}
2.2.4 查询
2.2.4.1 查询指定表所有列
public class UserForQuery {
@Test
public void testQuery1() {
Connection conn = null;
PreparedStatement ps= null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
String sql = "select * from t_user where id = ? ";
ps = conn.prepareStatement(sql);
ps.setObject(1, 1001);
// 返回结果集
rs = ps.executeQuery();
// 处理结果集
while(rs.next()) {// 是否有下一条数据
int id = rs.getInt(1);
String name = rs.getString(2);
String pwd = rs.getString(3);
String email = rs.getString(4);
User user = new User(id,name,pwd,email);
System.out.println(user);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, ps, rs);
}
}
}
JDBCUtils 类中追加ResultSet 的关闭操作:
try {
if(rs != null)
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
2.2.4.2 查询指定表任意列
@Test
public void testQueryForUser() {
String sql = "select id,name from t_user where id = ?";
User user = queryForUser( sql,1001 );
System.out.println(user);
}
// User 表的共同方法
public User queryForUser(String sql,Object ...obj ) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
for(int i = 0; i < obj.length ; i++) {
ps.setObject(i + 1, obj[i]);
}
rs = ps.executeQuery();
// 获取结果集的元数据
ResultSetMetaData rsmd = rs.getMetaData();
// 获取结果集列数
int columnCount = rsmd.getColumnCount();
while(rs.next()) {
User user = new User();
for(int i = 0; i < columnCount; i++) {
Object vaule = rs.getObject(i+1);
String columnName = rsmd.getColumnName(i + 1);
// 获取列名
Field field = User.class.getDeclaredField(columnName);
field.setAccessible(true);
field.set(user, vaule);
}
return user;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, ps, rs);
}
return null;
}
2.2.4.3 查询任意表(返回一条)
@Test
public void testGetInstance() {
String sql = "SELECT * from t_user where id = ?";
User user = getInstance(User.class, sql, 1002);
System.out.println(user);
}
public <T> T getInstance(Class<T> clazz,String sql,Object ...obj) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
for(int i = 0; i < obj.length ; i++) {
ps.setObject(i + 1, obj[i]);
}
rs = ps.executeQuery();
// 获取结果集的元数据
ResultSetMetaData rsmd = rs.getMetaData();
// 获取结果集列数
int columnCount = rsmd.getColumnCount();
while(rs.next()) {
T t = clazz.newInstance();
for(int i = 0;i<columnCount;i++) {
Object vaule = rs.getObject(i+1);
String columnName = rsmd.getColumnName(i + 1);
// 获取列名
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t, vaule);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, ps, rs);
}
return null;
}
2.2.4.4 查询任意表(返回多条)
@Test
public void getListTest() {
String sql = "SELECT * from t_user where id > ?";
List<User> userList = getList(User.class, sql, 1002);
userList.forEach(System.out::println);
}
public <T> List<T> getList(Class<T> clazz,String sql,Object ...obj) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
for(int i = 0; i < obj.length ; i++) {
ps.setObject(i + 1, obj[i]);
}
rs = ps.executeQuery();
// 获取结果集的元数据
ResultSetMetaData rsmd = rs.getMetaData();
// 获取结果集列数
int columnCount = rsmd.getColumnCount();
List<T> list = new ArrayList<T>();
while(rs.next()) {
T t = clazz.newInstance();
for(int i = 0;i<columnCount;i++) {
Object vaule = rs.getObject(i+1);
String columnName = rsmd.getColumnName(i + 1);
// 获取列名
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t, vaule);
}
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, ps, rs);
}
return null;
}
2.2.5 Blob 类型字段
2.2.5.1 Blob 类型
(MySQL)
类型 | 大小(单位:字节) |
---|---|
TinyBlob | 最大 255 |
Blob | 最大 65K |
MediumBlob | 最大 16M |
LongBlob | 最大 4G |
注意:如果存储的文件过大,数据库的性能会下降。
2.2.5.2 插入
public void inserBlobTest() throws Exception {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JDBCUtils.getConnection();
String sql = "insert into t_user(name,pwd,email,photo) values(?,?,?,?)";
ps = conn.prepareStatement(sql);
ps.setObject(1, "chengyu");
ps.setObject(2, "123456");
ps.setObject(3, "aa@qq.com");
FileInputStream is = new FileInputStream(new File("1.jpg"));
ps.setBlob(4, is);
ps.execute();
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, ps);
}
}
2.2.5.3 查看
@Test
public void queryBlobTest() throws Exception {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
InputStream is = null;
FileOutputStream fos = null;
try {
conn = JDBCUtils.getConnection();
String sql = "select id,name,photo from t_user where id = ?";
ps = conn.prepareStatement(sql);
ps.setObject(1, 1014);
rs = ps.executeQuery();
while(rs.next()) {
int id = rs.getInt(1);
String name = rs.getString("name");
Blob photo = rs.getBlob("photo");
is = photo.getBinaryStream();
fos = new FileOutputStream("2.jpg");
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer)) != -1) {
fos.write(buffer,0,len);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, ps,rs);
if(is != null) {
try {
is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if(fos != null) {
try {
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
3. 批量操作
3.1 Statement
Statement 语句因为数据内容不一样,每次都要进行编译、语法、语义检查、翻译成二进制命令、缓存等操作,效率低。
@Test
public void insertTest01() {
Connection conn = null;
Statement ps = null;
try {
long start = System.currentTimeMillis();
conn = JDBCUtils.getConnection();
ps = conn.createStatement();
for(int i = 1 ; i <= 20000 ; i++) {
String sql = "INSERT INTO goods(name) VALUES('name_"+i +"')";
ps.execute(sql);
}
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, ps);
}
}
548.384s
3.2 PreparedStatement
数据库会对预编译语句提供性能优化。因为可能被重复调用,所以编译后的执行代码会被缓存下来,下次调用时不需要编译,只要将参数直接传入编译过的语句执行代码即可。
@Test
public void insertTest02() {
Connection conn = null;
PreparedStatement ps = null;
try {
long start = System.currentTimeMillis();
conn = JDBCUtils.getConnection();
String sql = "INSERT INTO goods(name) VALUES(?)";
ps = conn.prepareStatement(sql);
for(int i = 1 ; i <= 20000 ; i++) {
ps.setObject(1, "name_"+i);
ps.execute();
}
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, ps);
}
}
553.783s
注意:
由于每条数据的插入都要与数据库进行一次交互(ps.execute()),此处仍然可以优化。详细做法参考 3.3。
3.3 PreparedStatement + Batch
mysql 数据库默认不支持batch 处理,需要开启:
(驱动要使用5.1.37 以后的版本)
url=jdbc:mysql://localhost:3306/book?rewriteBatchedStatements=true
@Test
public void insertTest03() {
Connection conn = null;
PreparedStatement ps = null;
try {
long start = System.currentTimeMillis();
conn = JDBCUtils.getConnection();
String sql = "INSERT INTO goods(name) VALUES(?)";
ps = conn.prepareStatement(sql);
for(int i = 1 ; i <= 20000 ; i++) {
ps.setObject(1, "name_"+i);
// 1. 攒SQL
ps.addBatch();
if(i % 500 == 0) {
// 2. 执行
ps.executeBatch();
// 3. 清空batch
ps.clearBatch();
}
}
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, ps);
}
}
2.225s
一百万条数据执行时间为:30s
3.4 PreparedStatement + Batch + 不自动提交
设置不允许自动提交数据。
@Test
public void insertTest04() {
Connection conn = null;
PreparedStatement ps = null;
try {
long start = System.currentTimeMillis();
conn = JDBCUtils.getConnection();
// 设置不允许自动提交数据
conn.setAutoCommit(false);
String sql = "INSERT INTO goods(name) VALUES(?)";
ps = conn.prepareStatement(sql);
for(int i = 1 ; i <= 1000000 ; i++) {
ps.setObject(1, "name_"+i);
// 1. 攒SQL
ps.addBatch();
if(i % 500 == 0) {
// 2. 执行
ps.executeBatch();
// 3. 清空batch
ps.clearBatch();
}
}
// 提交数据
conn.commit();
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, ps);
}
}
一百万条数据执行时间为:14.4s
4. 数据库事务
对一组数据单元的操作,成功则都成功,失败则都失败。
- DML 默认情况下,一旦执行就会自动提交,通过 set autocommit = false 的方式可以取消自动提交;
注意:即使在false 的状态下,连接关闭时也会自动提交。 - DDL 就是自动提交,无法取消。
4.1 代码实现
// 用户转账
@Test
public void testUpdate() {
Connection conn = null ;
try {
conn = JDBCUtils.getConnection();
// 取消自动提交
conn.setAutoCommit(false);
String sql1 = " update account set balance = balance - 100 where name = ?";
update(conn, sql1, "chengyu");
// 制造异常
// int a = 10 / 0;
String sql2 = " update account set balance = balance + 100 where name = ?";
update(conn, sql2, "chengwei");
System.out.println("转账成功!");
// 事物提交
conn.commit();
} catch (Exception e) {
e.printStackTrace();
// 回滚
try {
System.out.println("转账失败!");
conn.rollback();
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
} finally {
JDBCUtils.closeResource(conn, null);
}
}
// 通用增删改
public int update(Connection conn, String sql, Object ...obj) {
PreparedStatement ps = null;
try {
ps = conn.prepareStatement(sql);
for(int i = 0; i < obj.length ; i++) {
ps.setObject(i + 1, obj[i]);
}
return ps.executeUpdate(); // 返回影响几条数据
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, ps);
}
return 0;
}
4.2 事务的ACID 属性
4.2.1 原子性(Atomicity)
事务是一个不可分割的工作单位,操作要么都发生,要么都不发生。
4.2.2 一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另一个一致性状态。
4.2.3 隔离性(Isolation)
一个事物的执行不能被其他事物干扰,即一个事物内部的操作及使用的数据对并发的其他事物是隔离的,并发执行的各个事物之间不能互相干扰。
4.2.4 持久性(Durability)
一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据故障不应该对其有任何影响。
4.3 数据库的并发
4.3.1 并发的问题
4.3.1.1 脏读
两个事务T1、T2,T1 读取了已经被T2 更新但还没有提交的字段,之后,若T2 回滚,T1 读取的内容就是临时且无效的;
4.3.1.2 不可重复读取
两个事务T1、T2,T1 读取了一个字段,然后T2 更新了该字段,之后,T1 再次读取同一个字段,值就不同了。
4.3.1.3 幻读
两个事务T1、T2,T1 从一个表中读取了一个字段,然后T2 在该表中插入了一些新的行,之后,如果T1 再次取同一个表,就会多出几行。
4.3.2 四种隔离级别
针对4.3 的问题,设置的隔离级别。
隔离级别 | 名称 | MySql | Oracle | 脏读 | 不重读 | 幻读 | 描述 |
---|---|---|---|---|---|---|---|
READ UNCOMMITED | 读未提交数据 | ○ | ✖ | ✖ | ✖ | 允许事务读取未被其他事务提交的数据 | |
READ COMMITED | 读已提交数据 | ○ | ○(默认) | ○ | ✖ | ✖ | 只允许事务读取已被其它事务提交的数据 |
REPEATABLE READ | 可重复读 | ○(默认) | ○ | ○ | ✖ | 一个事务多次读取相同值,这个事务持续期间,禁止其他事务对这个字段变更 | |
SERIALIZABLE | 串行化 | ○ | ○ | ○ | ○ | ○ | 事务可从一个表中读取相同的行,这个事务持续期间,禁止其他事务对该表进行插入、更新、删除操作,性能低 |
4.3.3 命令行设置隔离级别
取消自动提交
set autocommit = false;
查看隔离级别(8.0版本后)
select @@transaction_isolation;
修改隔离级别
set global transaction isolation level read committed;
需要退出,重新登陆。
4.3.4 代码设置隔离级别
取消自动提交
conn.setAutoCommit(false);
查看隔离级别
conn.getTransactionIsolation()
修改隔离级别
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
数据库重启之后恢复默认。
5. DAO
DAO(Database access object):数据库访问对象。
5.1 BaseDao
// 封装通用增删改查(含事物)
public abstract class BaseDao {
// 通用增删改
public int update(Connection conn, String sql, Object... obj) {
PreparedStatement ps = null;
try {
ps = conn.prepareStatement(sql);
for (int i = 0; i < obj.length; i++) {
ps.setObject(i + 1, obj[i]);
}
return ps.executeUpdate(); // 返回影响几条数据
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, ps);
}
return 0;
}
// 通用查询一条(含事务)
public <T> T getInstance(Connection conn, Class<T> clazz, String sql, Object... obj) {
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql);
for (int i = 0; i < obj.length; i++) {
ps.setObject(i + 1, obj[i]);
}
rs = ps.executeQuery();
// 获取结果集的元数据
ResultSetMetaData rsmd = rs.getMetaData();
// 获取结果集列数
int columnCount = rsmd.getColumnCount();
while (rs.next()) {
T t = clazz.newInstance();
for (int i = 0; i < columnCount; i++) {
Object vaule = rs.getObject(i + 1);
String columnName = rsmd.getColumnName(i + 1);
// 获取列名
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t, vaule);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, ps, rs);
}
return null;
}
// 通用查询多条(含事务)
public <T> List<T> getList(Connection conn, Class<T> clazz, String sql, Object... obj) {
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = conn.prepareStatement(sql);
for (int i = 0; i < obj.length; i++) {
ps.setObject(i + 1, obj[i]);
}
rs = ps.executeQuery();
// 获取结果集的元数据
ResultSetMetaData rsmd = rs.getMetaData();
// 获取结果集列数
int columnCount = rsmd.getColumnCount();
List<T> list = new ArrayList<T>();
while (rs.next()) {
T t = clazz.newInstance();
for (int i = 0; i < columnCount; i++) {
Object vaule = rs.getObject(i + 1);
String columnName = rsmd.getColumnName(i + 1);
// 获取列名
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t, vaule);
}
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, ps, rs);
}
return null;
}
// 获取特殊值
public <T> T 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 (T)rs.getObject(1);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(null, ps, rs);
}
return null;
}
}
5.2 AccountDao
public interface AccountDao {
void insert(Connection conn,Account acc);
void deleteById(Connection conn,int id);
void update(Connection conn, int id, Account acc);
Account getAccountById(Connection conn, int id);
List<Account> getAll(Connection conn);
Long getCount(Connection conn);
BigDecimal getMaxBalance(Connection conn);
}
5.3 AccountDaoImpl
public class AccountDaoImpl extends BaseDao implements AccountDao {
@Override
public void insert(Connection conn, Account acc) {
String sql = "insert into account(name,balance) values(?,?)";
update(conn,sql,acc.getName(),acc.getBalance());
}
@Override
public void deleteById(Connection conn, int id) {
String sql = "delete from account where id = ?";
update(conn,sql,id);
}
@Override
public void update(Connection conn, int id, Account acc) {
String sql = "update account set name = ?,balance = ? where id = ?";
update(conn,sql,acc.getName(),acc.getBalance(),acc.getId());
}
@Override
public Account getAccountById(Connection conn, int id) {
String sql = "select * from account where id = ?";
return getInstance(conn,Account.class,sql,id);
}
@Override
public List<Account> getAll(Connection conn) {
String sql = "select * from account ";
return getList(conn,Account.class,sql);
}
@Override
public Long getCount(Connection conn) {
String sql = " select count(*) from account ";
return getValue(conn,sql);
}
@Override
public BigDecimal getMaxBalance(Connection conn) {
String sql = " select max(balance) from account ";
return getValue(conn,sql);
}
}
5.4 AccountDaoImplTest
public class AccountDaoImplTest {
private AccountDaoImpl dao = new AccountDaoImpl();
@Test
public void testInsert() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
Account acc = new Account(0 , "chenglong", new BigDecimal(500));
dao.insert(conn, acc);
System.out.println("添加成功!");
} catch (Exception e) {
e.printStackTrace();
} finally{
JDBCUtils.closeResource(conn, null);
}
}
@Test
public void testDeleteById() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
dao.deleteById(conn, 1004);
System.out.println("删除成功!");
} catch (Exception e) {
e.printStackTrace();
} finally{
JDBCUtils.closeResource(conn, null);
}
}
@Test
public void testUpdateConnectionIntAccount() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
Account acc = new Account(1003,"chengyu",new BigDecimal(1500));
dao.update(conn, 1003, acc);
System.out.println("修改成功!");
} catch (Exception e) {
e.printStackTrace();
} finally{
JDBCUtils.closeResource(conn, null);
}
}
@Test
public void testGetAccountById() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
Account acc = dao.getAccountById(conn, 1003);
System.out.println(acc);
} catch (Exception e) {
e.printStackTrace();
} finally{
JDBCUtils.closeResource(conn, null);
}
}
@Test
public void testGetAll() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
List<Account> list = dao.getAll(conn);
list.forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
} finally{
JDBCUtils.closeResource(conn, null);
}
}
@Test
public void testGetCount() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
Long count = dao.getCount(conn);
System.out.println("记录数为:" + count);
} catch (Exception e) {
e.printStackTrace();
} finally{
JDBCUtils.closeResource(conn, null);
}
}
@Test
public void testGetMaxBalance() {
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
BigDecimal maxBalance = dao.getMaxBalance(conn);
System.out.println("最大余额为:" + maxBalance);
} catch (Exception e) {
e.printStackTrace();
} finally{
JDBCUtils.closeResource(conn, null);
}
}
}
6. 数据库连接池
6.1 普通JDBC 问题
1)普通的数据库连接,每次都要将Connection 加载到内存,在验证用户名密码。执行完还需要将连接移除。数据库连接资源并没有得到很好的重复利用。
若同时有几百人甚至几千人在线,频繁的进行数据库连接操作,将占用很多系统资源,严重时甚至会造成服务器崩溃。
2)对于每次数据库连接,使用完后都得断开。否则,当程序出现异常而未能关闭,将会导致数据库系统中的内存泄露,最终将导致重启数据库。
3)不能控制被创建的连接对象数,系统资源会被毫无顾忌的分配出去,如果连接过多,超出符合,也可能导致内存泄漏,服务器崩溃。
6.2 数据库连接池基本思想
为数据库连接建立一个“缓冲池”,预先在缓冲池中放入一定数量的连接,当需要建立数据库连接的时候,只需从缓冲池中取出一个,使用完毕放回即可。
6.3 开源数据库连接池
6.3.1 C3P0
稳定性可以,速度相对慢。
导入下记jar 包:
c3p0-0.9.5.5.jar
mchange-commons-java-0.2.19.jar
mysql-connector-java-8.0.23.jar
6.3.1.1 方式一
@Test
public void getConnectionTest() throws Exception {
// 获取C3P0 连接池
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass( "com.mysql.jdbc.Driver" );
cpds.setJdbcUrl( "jdbc:mysql://localhost:3306/book" );
cpds.setUser("root");
cpds.setPassword("tiger");
// 初始连接数
cpds.setInitialPoolSize(10);
Connection conn = cpds.getConnection();
System.out.println(conn);
}
参数设定:
https://www.mchange.com/projects/c3p0/#configuration_properties
6.3.1.2 方式二
src 下创建c3p0-config.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<named-config name="helloc3p0">
<!-- 提供获取连接的基本信息 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/book</property>
<property name="user">root</property>
<property name="password">tiger</property>
<!-- 数据库连接池管理的基本信息 -->
<!-- 当连接数不够时,c3p0一次性向数据库服务器申请的连接数 -->
<property name="acquireIncrement">5</property>
<!-- 初始化连接数 -->
<property name="initialPoolSize">10</property>
<!-- 最小连接数 -->
<property name="minPoolSize">10</property>
<!-- 最大连接数 -->
<property name="maxPoolSize">100</property>
<!-- 最多维护的Statements 的个数 -->
<property name="maxStatements">50</property>
<!-- 每个连接中可以最多使用的Statements 的个数 -->
<property name="maxStatementsPerConnection">5</property>
</named-config>
</c3p0-config>
JDBCUtils.java:
// 连接池提供一个即可
private static ComboPooledDataSource cpds = new ComboPooledDataSource("helloc3p0");
public static Connection getConnection() throws SQLException {
return cpds.getConnection();
}
6.3.2 DBCP
tomcat 自带数据库连接池技术,速度快,不稳定。
导入下记jar 包:
mysql-connector-java-8.0.23.jar
commons-dbcp2-2.8.0.jar
commons-logging-1.2.jar
commons-pool2-2.9.0.jar
6.3.2.1 方式一
@Test
public void getConnectionTest() throws SQLException {
// 创建DBCP 的数据库连接池
BasicDataSource source = new BasicDataSource();
// 设置基本信息
source.setDriverClassName("com.mysql.jdbc.Driver");
source.setUrl("jdbc:mysql://localhost:3306/book");
source.setUsername("root");
source.setPassword("tiger");
// 连接池管理相关信息
source.setInitialSize(10);
Connection conn = source.getConnection();
System.out.println(conn);
}
}
6.3.2.1 方式二
src 下创建 dbcp.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/book
username=root
password=tiger
initialSize=10
@Test
public void getConnectionTest1() throws Exception {
Properties pros = new Properties();
// 方式1-类的加载器
// InputStream is = ClassLoader.getSystemClassLoader().getSystemResourceAsStream("dbcp.properties");
// 方式2-
FileInputStream is = new FileInputStream(new File("src/dbcp.properties"));
pros.load(is);
// 创建DBCP连接池
DataSource source = BasicDataSourceFactory.createDataSource(pros);
Connection conn = source.getConnection();
System.out.println(conn);
}
JDBCUtils.java:
// 声明数据库连接池
private static DataSource source;
static {
try {
Properties pros = new Properties();
FileInputStream is = new FileInputStream(new File("src/dbcp.properties"));
pros.load(is);
// 创建DBCP连接池
source = BasicDataSourceFactory.createDataSource(pros);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection2() throws Exception {
return source.getConnection();
}
6.3.3 Druid “德鲁伊”
阿里提供的数据库连接池。兼顾稳定与效率。
导入下记jar 包:
druid-1.1.22.jar
mysql-connector-java-8.0.23.jar
public class DruidTest {
@Test
public void getConnection() throws Exception {
Properties pros = new Properties();
FileInputStream is = new FileInputStream(new File("src/druid.properties"));
pros.load(is);
DataSource source = DruidDataSourceFactory.createDataSource(pros);
Connection conn = source.getConnection();
System.out.println(conn);
}
}
JDBCUtils.java:
// 获取Druid连接池
private static DataSource druidSource;
static {
try {
Properties pros = new Properties();
FileInputStream is = new FileInputStream(new File("src/druid.properties"));
pros.load(is);
druidSource = DruidDataSourceFactory.createDataSource(pros);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection3() throws Exception{
return druidSource.getConnection();
}
7. Apache-DBUtils CRUD
是Apache 组织提供的一个开源JDBC 工具类库,封装了增删改查操作。
导入下记jar 包:
commons-dbutils-1.7.jar
druid-1.1.22.jar
mysql-connector-java-8.0.23.jar
druid.properties
url=jdbc:mysql://localhost:3306/book
username=root
password=tiger
driverClassName=com.mysql.jdbc.Driver
initialSize=10
maxActive=10
JDBCUtils.java
public class JDBCUtils {
// 获取Druid连接池
private static DataSource source;
static {
try {
Properties pros = new Properties();
FileInputStream is = new FileInputStream(new File("src/druid.properties"));
pros.load(is);
source = DruidDataSourceFactory.createDataSource(pros);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws Exception{
return source.getConnection();
}
public static void closeResource(Connection conn, Statement ps) {
DbUtils.closeQuietly(conn);
DbUtils.closeQuietly(ps);
}
public static void closeResource(Connection conn, Statement ps,ResultSet rs) {
DbUtils.closeQuietly(conn);
DbUtils.closeQuietly(ps);
DbUtils.closeQuietly(rs);
}
}
QueryRunnerTest.java
public class QueryRunnerTest {
// 增删改
@Test
public void insertTest() throws Exception {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection();
String sql = "insert into account(name,balance) values(?,?)";
int insertCount = runner.update(conn, sql, "chenggong","1500");
System.out.println("影响了几条数据:" + insertCount);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, null);
}
}
// 查询一项目
@Test
public void queryItemTest() throws Exception {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection();
String sql = "select count(*) from account";
ScalarHandler handler = new ScalarHandler();
Long count = (Long) runner.query(conn, sql, handler);
System.out.println("条数为:" + count);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, null);
}
}
// 查询一项目
@Test
public void queryMaxItemTest() throws Exception {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection();
String sql = "select max(balance) from account";
ScalarHandler handler = new ScalarHandler();
BigDecimal maxBalance = (BigDecimal) runner.query(conn, sql, handler);
System.out.println("最大余额为:" + maxBalance);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, null);
}
}
// 查询一条 Bean
@Test
public void queryTest() {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection();
String sql = "select * from account where id = ?";
// BeanHandler:ResultSetHandler 接口的实现类,用于封装一条记录
BeanHandler<Account> handler = new BeanHandler<>(Account.class);
Account account = runner.query(conn, sql, handler, 1001);
System.out.println("查询结果为:" + account);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, null);
}
}
// 查询多条 BeanList
@Test
public void queryListTest() throws Exception {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection();
String sql = "select * from account where id > ?";
BeanListHandler<Account> handler = new BeanListHandler<>(Account.class);
List<Account> accList = runner.query(conn, sql, handler, 1001);
accList.forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, null);
}
}
// 查询一条Map
@Test
public void queryMapTest() throws Exception {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection();
String sql = "select * from account where id = ?";
MapHandler handler = new MapHandler();
Map<String,Object> map = runner.query(conn, sql, handler, 1001);
System.out.println(map);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, null);
}
}
// 查询一条MapList
@Test
public void queryMapListTest() throws Exception {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection();
String sql = "select * from account where id > ?";
MapListHandler handler = new MapListHandler();
List<Map<String, Object>> list = runner.query(conn, sql, handler, 1001);
list.forEach(System.out::println);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, null);
}
}
// 自定义ResultSetHandler 的实现类
@Test
public void queryDiyTest() throws Exception {
Connection conn = null;
try {
QueryRunner runner = new QueryRunner();
conn = JDBCUtils.getConnection();
String sql = "select * from account where id = ?";
ResultSetHandler<Account> handler = new ResultSetHandler<Account>() {
@Override
public Account handle(ResultSet rs) throws SQLException {
if(rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
BigDecimal balance = rs.getBigDecimal("balance");
return new Account(id,name,balance);
}
return null;
}
};
Account acc = runner.query(conn, sql, handler, 1001);
System.out.println(acc);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, null);
}
}
}
最后
以上就是从容草莓为你收集整理的JDBC1. 获取数据库连接2. 操作和访问数据库3. 批量操作4. 数据库事务5. DAO6. 数据库连接池7. Apache-DBUtils CRUD的全部内容,希望文章能够帮你解决JDBC1. 获取数据库连接2. 操作和访问数据库3. 批量操作4. 数据库事务5. DAO6. 数据库连接池7. Apache-DBUtils CRUD所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复