上一篇文章我们总结了获取数据库连接以及操作数据表的一些知识点,本篇将继续上次的文章给大家分享!
1. 上一篇文章我们可以对数据表进行增删改查的操作了,对与一些小项目的部分功能我们也足以胜任。但现在有一个需求是一个人下了一个订单,并将这个订单的下单时间等信息插入了订单表,并且其主键是自动生成主键值,当我们想要找到该用户买了哪些商品(商品表)时,则需要用订单 ID 去获取商品列表,此时就需要获得数据库自动生成的主键值(不针对于不自动生成主键的数据库,如 Oracle)。
取得数据库自动生成的主键值,使用重载的 prepareStatement(sql, flag) 方法使其生成自动生成的主键值,使用 getGennratedKeys() 获得自动生成的主键值的结果集,代码如下(具体方法可以在 API 中进行进一步的了解):
![](https://i-blog.csdnimg.cn/blog_migrate/cba0752d5757330d4a7c9d54c8a0b81c.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6fc8e6e5378402a585ef938b88be1719.gif)
@Test
public void testGetKey() {
Connection connection;
PreparedStatement preparedStatement;
connection = JDBCTools.getConnection();
String sql = "INSERT INTO book (book_name, isbn, price, stock) VALUES (?, ?, ?, ?)";
try {
// 设置其可以返回自动生成的主键值
preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
preparedStatement.setString(1, "R");
preparedStatement.setString(2, "10011");
preparedStatement.setInt(3, 220);
preparedStatement.setInt(4, 10);
preparedStatement.executeUpdate();
// 获取自动生成的主键值,需要调用方法 getGeneratedKeys(),返回一个结果集,这个方法
ResultSet resultSet = preparedStatement.getGeneratedKeys();
if (resultSet.next()) {
System.out.println(resultSet.getObject(1));
}
} catch (SQLException e) {
e.printStackTrace();
}
}
2. 处理 Blob 类型数据,如插入图片,读取图片的操作,往数据库中插入 BLOB 类型的数据必须使用 prepareStatement,因为插入 BLOB 类型的数据使用的 sql 语句无法拼写出来;下面是插入 Blob 型数据
![](https://i-blog.csdnimg.cn/blog_migrate/a16b478e60ff65b4b2fa1405a71f151a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6b0ecd3cdb354a377cef4ccbeb5b5a54.gif)
@Test
public void testBlob() {
Connection connection;
PreparedStatement preparedStatement;
connection = JDBCTools.getConnection();
String sql = "INSERT INTO pic_blob (pic) VALUES ( ?)";
try {
preparedStatement = connection.prepareStatement(sql);
File file = new File("C:\\Users\\lenovo\\Desktop\\key.png");
InputStream inputStream = null;
try {
inputStream = new FileInputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// 使用输入流填充参数
preparedStatement.setBlob(1, inputStream);
preparedStatement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
插入 Blob 类型数据就肯定可以读取 Blbo 型数据,理解了上面的方法后就大概知道了读取需要使用 OutputStream,你可以先自己尝试一下如何去写,看有什么困难在看下面的代码:
![](https://i-blog.csdnimg.cn/blog_migrate/ded2d16deecb5a4f0990dba6db5c281c.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/bbeb3d492ef85d71aefeeb47a703bcbb.gif)
@Test
public void testBlobGet() {
Connection connection;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
connection = JDBCTools.getConnection();
String sql = "SELECT pic FROM pic_blob WHERE id=?";
try {
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, 1);
resultSet = preparedStatement.executeQuery();
// 和 IO 那块一样,使用字节数组加快读取速度
byte[] buffer = new byte[1024];
int len;
while (resultSet.next()) {
try {
Blob pic = resultSet.getBlob(1);
// 获取输入流
InputStream inputStream = pic.getBinaryStream();
// 将图片存到一个位置,通过输出流
OutputStream outputStream = new FileOutputStream("C:\\Users\\lenovo\\Desktop\\key2.png");
while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}
outputStream.close();
inputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCTools.releaseConnection(connection, preparedStatement, resultSet);
}
}
3. 数据库事务处理。简单的说几个对数据库的操作要么全都执行,要么全都不执行,需要保持一致性,Mysql 数据库默认的执行一个操作便会自动提交,为了处理事务我们必须设置数据库不是自动提交的,还有若每个对数据表的操作都是自己单独的连接,那么就无法保证事务,所以我们需要更改以前写的工具类,使其使用一个数据库连接,然后等所有操作结束之后我们再手动提交操作,若发生异常便进行回滚,具体代码如下:
![](https://i-blog.csdnimg.cn/blog_migrate/843bba37d1d53fc6db3de86847ff979e.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/d14c6e3cb8a2014182deab2a2437b68f.gif)
@Test
public void testTransaction1() {
Connection connection = null;
try {
// 为下面的 update 方法传入一个统一的连接
connection = JDBCTools.getConnection();
// 关闭自动提交
connection.setAutoCommit(false);
String sql1 = "UPDATE book SET price = price - 500 WHERE id = 1";
update(connection, sql1);
// 制造异常检查事务的正确性
// int i = 2 / 0;
String sql2 = "UPDATE book SET price = price + 500 WhERE id = 2";
update(connection, sql2);
// 执行完毕,手动提交
connection.commit();
} catch (SQLException e) {
try {
// 如果发生异常将其进行回滚
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
}
// 更改后的 update 方法使用的 connection 不再是在其内部每次调用方法获取新的连接,而是传入一个连接使其统一
public void update(Connection connection, String sql, Object... args) {
PreparedStatement preparedStatement = null;
try {
preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(i + 1, args[i]);
}
preparedStatement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCTools.releaseConnection(null, preparedStatement, null);
}
}
如果数据库中同时运行多个事务,若没有设置合适隔离级别将会出现各种并发问题:脏读,不可重复读,幻读等问题。将数据库隔离级别设置的越高数据的一致性就越高但并发性也就越差。
Oracle 数据库支持两种事务隔离级别,READ_COMMITED(读已提交),SERIALIZABLE(串行化),其默认的隔离级别是 READ_COMMITED
MySQL 数据库支持四种事务隔离级别,默认的隔离级别是 REPEATBLE READ(读未提交)。大多数情况下我们设置为 READ_COMMITED 最为合适,我们可以使用 connection.setTransactionIsolation(); 来设置数据库的隔离级别
4. 我们在这之前所使用的数据库连接是每次从数据库中获取一个使用完毕就将其放入数据库,这样的操作比较浪费,所以也就有了数据库连接池,其基本思想: 为数据库连接建立一个缓冲池,预先放入一定数量的连接,需要时从缓冲池中取出一个,使用完毕放入,当使用的时候没有多余的连接时需要等待。这样就节省了很多的时间,下面是使用 c3p0 连接池获取数据库连接的代码:
![](https://i-blog.csdnimg.cn/blog_migrate/07e4636ba8de1cc693cf12e713af1d26.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/bd2e421d3b4c59dbae935af3f1313944.gif)
@Test
public void testC3p0() {
// 在使用之前需要导入对应的 jar 包文件
ComboPooledDataSource dataSource = new ComboPooledDataSource();
try {
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql:///sh_db");
dataSource.setUser("root");
dataSource.setPassword("zy961029");
System.out.println(dataSource.getConnection());
} catch (PropertyVetoException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
和之前的一样,我们也需要将上面的方法加以配置文件的处理(程序中的 helloc3p0 是配置文件中所配置 <name-config name="helloC3p0"> )
![](https://i-blog.csdnimg.cn/blog_migrate/645a469fb63371bcaf964661c8b7da5e.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/09b39ffc8754d274b5cb5b80f789cff5.gif)
@Test
public void testC3p0Config() {
// 注意 jar 包的版本问题,0.9.2 之前的不支持下面的写法
ComboPooledDataSource dataSource = new ComboPooledDataSource("helloC3p0");
try {
Connection connection = dataSource.getConnection();
System.out.println(connection);
} catch (SQLException e) {
e.printStackTrace();
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/a631cebb411d10b43deb7ff4b51b64b9.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/5f51a165a36620f1e469ad0c718620a3.gif)
<?xml version="1.0" encoding="UTF-8" ?>
<c3p0-config>
<named-config name="helloC3p0">
<property name="user">root</property>
<property name="password">zy961029</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///sh_db</property>
<property name="maxStatements">1</property>
<property name="maxStatementsPerConnection">5</property>
</named-config>
</c3p0-config>
5. 使用 DBUtils,commons-dbutils 是 Apache 组织提供的一个开源 JDBC 工具类,它是对 JDBC 的简单封装,学习成本低,并且简化了代码,不影响代码的性能
使用 DBUTils 进行简单的增删改查功能(可以在包内的 API 中进行对相应的类进行了解 )
QueryRunner 类简化了 SQL 查询,与 ResultSetHandler 组合可以完成大部分的数据操作,减少编码量
![](https://i-blog.csdnimg.cn/blog_migrate/6c317bad4529ac9ac48c1f7ca700285f.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0cca9aa715e1a7bcc8ef6ea9b71b7a02.gif)
package com.jdbc.dao.my.dbutils;
import com.jdbc.dao.my.first.test.JDBCTools;
import com.jdbc.dao.my.first.test.SH_DB;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.*;
import org.junit.Test;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Created by shkstart on 2017/11/02.
*/
public class DBUtilsTest {
// C3p0 获取数据库连接
@Test
public void testGetConnection() {
// 更新工具类中获取数据库连接的方法,使用 c3p0 数据库连接
Connection connection = JDBCTools.getConnection();
System.out.println(connection);
}
// 使用 QueryRunner 删除数据 --> DBUtils
@Test
public void testUpdate() {
QueryRunner queryRunner = new QueryRunner();
Connection connection;
connection = JDBCTools.getConnection();
// String sql = "DELETE FROM book WHERE id=?";
String sql = "INSERT INTO book (book_name, isbn, price, stock) values(?, ?, ?, ?)";
try {
queryRunner.update(connection, sql, "C#", "1008", 120, 40);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCTools.releaseConnection(connection, null, null);
}
}
// 将查询到的所有数据打印出来,
@Test
public void resultSetHandler() {
QueryRunner queryRunner = new QueryRunner();
Connection connection;
final List<SH_DB> sh_dbs = new ArrayList<SH_DB>();
connection = JDBCTools.getConnection();
String sql = "SELECT book_name bookName, isbn, price, stock FROM book";
try {
queryRunner.query(connection, sql, new ResultSetHandler<Object>() {
@Override
public Object handle(ResultSet resultSet) throws SQLException {
while (resultSet.next()) {
String bookName = resultSet.getString(1);
String isbn = resultSet.getString(2);
int price = resultSet.getInt(3);
int stock = resultSet.getInt(4);
SH_DB sh_db = new SH_DB(bookName, isbn, price, stock);
sh_dbs.add(sh_db);
}
return sh_dbs;
}
});
System.out.println(sh_dbs);
} catch (SQLException e) {
e.printStackTrace();
}
}
// 把结果集的第一条记录转为创建 BeanHandler 对象时传入的对象
@Test
public void testBeanHandler() {
Connection connection;
QueryRunner queryRunner = new QueryRunner();
connection = JDBCTools.getConnection();
String sql = "SELECT * FROM BOOK";
try {
SH_DB sh_db = queryRunner.query(connection, sql, new BeanHandler<SH_DB>(SH_DB.class));
System.out.println(sh_db);
} catch (SQLException e) {
e.printStackTrace();
}
}
// 返回查询结果的 list 结果集,其中查询语句必须使用表对应 bean 的列的别名
// BeanHandler 也一样
@Test
public void testBeanListHandler() {
Connection connection = JDBCTools.getConnection();
String sql = "SELECT book_name bookName, isbn, price, stock FROM book";
QueryRunner queryRunner = new QueryRunner();
try {
List<SH_DB> sh_dbs = (List<SH_DB>) queryRunner.query(connection, sql, new BeanListHandler(SH_DB.class));
System.out.println(sh_dbs);
} catch (SQLException e) {
e.printStackTrace();
}
}
// 将查询结果的第一行存放进一个 map 中,键位 列名,而不是列的别名, 值为 所对应的值
@Test
public void testMapHandler() {
Connection connection = JDBCTools.getConnection();
QueryRunner queryRunner = new QueryRunner();
String sql = "SELECT book_name bookName, isbn, price, stock from book " +
"where id = ?";
try {
Map<String, Object> map = queryRunner.query(connection, sql, new MapHandler(), 5);
System.out.println(map);
for (Map.Entry<String, Object> entry : map.entrySet()) {
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
} catch (SQLException e) {
e.printStackTrace();
}
}
// 将查询结果的存入一个 List,其 list 的元素为一个 map,对应一列
@Test
public void testListHandler() {
Connection connection = JDBCTools.getConnection();
QueryRunner queryRunner = new QueryRunner();
String sql = "SELECT book_name bookName, isbn, price, stock from book";
try {
List<Map<String, Object>> listMap = queryRunner.query(connection, sql, new MapListHandler());
System.out.println(listMap);
} catch (SQLException e) {
e.printStackTrace();
}
}
// 将查询的一个结果放入 object 返回,比如返回记录数,返回某一列的值,如果 sql 语句是返回多条记录的
// 那么 ScalarHandler 将返回的是第一列的值
@Test
public void testScalarHandler() {
Connection connection = JDBCTools.getConnection();
QueryRunner queryRunner = new QueryRunner();
String sql = "SELECT COUNT(*) FROM book";
try {
Object object = queryRunner.query(connection, sql, new ScalarHandler());
System.out.println(object);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
上一篇加上本篇就是上一周所学习的东西,太多,总结起来也挺费力的如果有什么错误还望指出,谢谢!!!