1、JDBC-大数据(LOB)的存取:
a.首先在数据新建个表,可以用下面的命令
CREATE TABLE IF NOT EXISTS t_blob
(
id INT PRIMARY KEY,
image BLOB
)
b.创建一个数据库的工具类
写个dbcfg.properties在src目录下:
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/day16
user=root
password=root
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ResourceBundle;
//专门用于数据库的工具类
public class JdbcUtils {
private static String driverClass = "" ;
private static String url = "" ;
private static String user = "" ;
private static String password = "";
static{
//我们将数据库的连接数据写在dfcfg.properties 文件中,方便配置
ResourceBundle rb = ResourceBundle.getBundle("dbcfg") ;
driverClass = rb.getString("driverClass") ;
url = rb.getString("url") ;
user = rb.getString("user") ;
password = rb.getString("password") ;
try {
Class.forName(driverClass) ;
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection(){
try {
return DriverManager.getConnection(url, user, password) ;
} catch (SQLException e) {
e.printStackTrace();
}
return null ;
}
public static void release(ResultSet rs ,Statement stmt,Connection conn){
if(rs != null){
try {
rs.close() ;
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stmt != null){
try {
stmt.close() ;
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close() ;
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
c.我们写个测试类来测试存取功能:(存储图片)
public class BlobTest {
@Test
public void testSave() throws Exception{
Connection conn = JdbcUtils.getConnection() ;//获得链接对象
//创建预处理命令对象
PreparedStatement pstmt = conn.prepareStatement("insert into t1 values(?,?)") ;
pstmt.setInt(1, 1) ;//指定?的值
File file = new File("src/test.png") ;
InputStream in = new FileInputStream(file) ;
pstmt.setBinaryStream(2,in, (int)file.length()) ;//我们使用二进制存储,mysql里面需要强转下int
//执行sql语句
pstmt.executeUpdate() ;
//释放资源
JdbcUtils.release(null, pstmt, conn) ;
}
@Test
public void testQuery() throws Exception{
//获得链接对象
Connection conn = JdbcUtils.getConnection() ;
//创建预处理命令对象
PreparedStatement pstmt = conn.prepareStatement("select * from t1") ;
//执行sql语句
ResultSet rs = pstmt.executeQuery() ;
if(rs.next()){
int id = rs.getInt("id") ;
InputStream is = rs.getBinaryStream("image") ;
//需要再建一个文件
File f = new File("src/out.png") ;
OutputStream os = new FileOutputStream(f) ;
byte[] bs = new byte[1024] ;
int b = 0 ;
while((b = is.read(bs)) != -1){
os.write(bs,0,b) ;
}
is.close() ;
os.close() ;
}
JdbcUtils.release(rs, pstmt, conn) ;
}
}
注意:如果我们是存一个大的文本,就使用:
File file = new File("src/test.txt") ;
FileReader fr = new FileReader(file) ;
pstmt.setCharacterStream(2,fr,(int)file.length()) ;
因为我们存的大数据会比较慢,一般我们都是存文件的地址。
2、JDBC-批处理_一次性执行多条数据:
一次执行多条sql语句
a.通过Statement命令对象演示一个插入3条记录(速度快些)
Statement stmt = conn.createStatement();
String sql1 = "insert into t3 values(1,'name1',20)";
String sql2 = "insert into t3 values(2,'name2',23)";
String sql3 = "insert into t3 values(3,'name3',24)";
// 将三条sql语句一起放入stmt对象中,一起发送到服务执行,在DBMS中会编译成一个逻辑执行单元,所以速度会更快一些
stmt.addBatch(sql1);
stmt.addBatch(sql2);
stmt.addBatch(sql3);
stmt.executeBatch();// 执行sql语句,返回的是一个数组
JdbcUtils.release(null, stmt, conn);
b.通过PreparedStatement对象插入三条数据
PreparedStatement pstmt = conn.prepareStatement("insert into t3 values(?,?,?)");
// 指定?的值
pstmt.setInt(1, 4);
pstmt.setString(2, "name1");
pstmt.setInt(3, 18);
pstmt.addBatch();
// 指定?的值
pstmt.setInt(1, 5);
pstmt.setString(2, "name2");
pstmt.setInt(3, 17);
pstmt.addBatch();
// 指定?的值
pstmt.setInt(1, 6);
pstmt.setString(2, "name3");
pstmt.setInt(3, 18);
pstmt.addBatch();
pstmt.executeBatch();// 执行sql语句
JdbcUtils.release(null, pstmt, conn);// 释放资源
如果要插入大量的数据呢,1000条
for (int i = 0; i < 1000; i++) {
// 指定?的值
pstmt.setInt(1, i);
pstmt.setString(2, "name" + i);
pstmt.setInt(3, i);
pstmt.addBatch();//这个是将多条数据放到一起执行
if(i%200 == 0){//200条一起执行
pstmt.executeBatch() ;//执行sql语句
pstmt.clearBatch() ;//一定要清空缓存
}
}
//为了防止缓存中还有sql没有执行,在for外面应当再次 执行sql语句
pstmt.executeBatch();
1000条数据插入大概花了1分钟左右
3、JDBC-存储过程:
类似方法,存储过程形参可以有默认值,可以返回多个值
#sql语句,定义一个无参的存储过程
delimiter $$ //定义一个结束标识符
create procedure pro1()
begin
select * from mytable ;
end ;
$$
delimiter ;//修改回来
java调用方法:
/演示JDBC调用存储过程,使用的是CallableStatement
public class ProcedureTest {
//执行不带返回值的存储过程没有任何意义
@Test
public void testPro1() throws Exception {
Connection conn = JdbcUtils.getConnection();// 获得链接对象
// 创建执行存储过程的命令对象
CallableStatement cstmt = conn.prepareCall("{call pro2(?)}") ;
cstmt.setInt(1, 1) ;//指定?的值
cstmt.execute() ;//执行存储过程
JdbcUtils.release(null, cstmt, conn);// 释放资源
}
@Test
public void testPro2() throws Exception {
Connection conn = JdbcUtils.getConnection();// 获得链接对象
// 创建执行存储过程的命令对象
CallableStatement cstmt = conn.prepareCall("{call pro2(?,?)}") ;
cstmt.setInt(1, 1) ;//指定?的值
cstmt.registerOutParameter(2, Types.VARCHAR) ;//指定第二个?是输出参数
cstmt.execute() ;//执行存储过程
String name = cstmt.getString(2) ;//获得返回值
JdbcUtils.release(null, cstmt, conn);// 释放资源
}
}
好处就是java代码里面不用写sql语句,我们后面数据库变了,sql语句也会变,我们写只需要修改存储过程就行
不用修改代码了。还有一种方式就是mybatis,sql语句写在xml文件中。
4、JDBC-事务执行:
一组命令,要么全部成功,要么全部失败。
sql语句:
开始事务 start transaction;
提交事务:commit;
回滚:rollback;
例如:
//采用事务处理转账失败的情况
@Test
public void testTransaction1() throws Exception {
Connection conn = JdbcUtils.getConnection();// 获得链接对象
PreparedStatement pstmt = null ;
PreparedStatement pstmt1 = null ;
conn.setAutoCommit(false) ;//让数据库不要自动提交事务
try{
pstmt = conn.prepareStatement("update bank set money = money -? where id = ?") ;
pstmt.setFloat(1, 2000) ;
pstmt.setInt(2, 1) ;
int n = pstmt.executeUpdate() ;
System.out.println(10/2);
pstmt1 = conn.prepareStatement("update bank set money = money + ? where id = ?") ;
pstmt1.setFloat(1, 2000) ;
pstmt1.setInt(2, 2) ;
int n1 = pstmt1.executeUpdate() ;
conn.commit() ;//手动进行提交,体现一致性
}catch(Exception e){
conn.rollback() ;//让事务进行回滚
}
// 释放资源
JdbcUtils.release(null, pstmt, conn);
JdbcUtils.release(null, pstmt1, conn);
}
注意:一定要在一个连接里面提交事务。
5、事务的隔离级别:
不考虑事务的隔离级别会导致什么问题?
1、脏读:指一个事务到读到另外一个事务中未提交的数据。
2、不可重复读:指一个事务读到另外一个事务执行update的语句的结果(已提交)
3、虚读:指一个事务读到另外一个事务执行的insert的语句的结果(已提交)
MySQL中支持的事务隔离级别:
READ UNCOMMITTED:脏读、不可重复读、虚读有可能发生。
READ COMMITTED:防止脏读的发生;不可重复读、虚读有可能发生。
REPEATABLE READ:防止脏读、不可重复读的发生;虚读有可能发生。(MySQL中默认)
SERIALIZABLE:防止脏读、不可重复读、虚读。
随着级别的增高,数据越安全,但效率越低。
查看当前的事务隔离级别:select @@tx_isolation;//查询默认的级别是 REPEATABLE-READ
更改当前的事务隔离级别:set transaction isolation level 四个级别;
6、锁机制
共享锁:一个事务开启时,使用了共享锁,其他事务也可以设置共享锁。读数据是没有问题的。
select * from table1 lock in share mode;//给table1加共享锁,作用不大
排它锁:一个事务开启时,使用了排他锁,其他事务只有等待
update insert 等语句会自动加排他锁
select * from table2 for update;
例如:
开启一个事务, start transaction;
加个排他锁,select * from table2 for update;
在另外一个cmd中,开启一个事务, start start transaction;
此时你在这个cmd中执行insert,会等待之前cmd中的事务提交才响应。