JDBC学习1
JDBC简介
JDBC:Java DataBase Connectivity,是SUN推出的操作数据库的规范。
JDBC和数据库驱动:规范和实现的关系。
JDBC:JDK中。java.sql.;javax.sql.;
驱动:去数据库厂商的网站下载
JDBC开发步骤
- 搭建开发环境:把数据库驱动的jar包加入到构建路径中。
- 注册驱动
- 获取与数据库的链接
- 创建代表SQL语句的对象
- 执行SQL语句(如果是查询语句:返回结果集。)
- 遍历结果集里的内容
- 释放资源
代码:
package com.jyh.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import com.jyh.domain.User;
public class JDBCConnect {
public static void main(String[] args) throws Exception{
//1.注册驱动
//DriverManager.registerDriver(new Driver());
//2.获取链接
//Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/database1", "root", "ying1995520***");
//3.创建代表sql语句的对象
Statement st = conn.createStatement();
//4.执行SQL语句(如果是查询语句则返回结果集)
ResultSet rs = st.executeQuery("select * from user");
//5.遍历返回的结果集里面的内容
List<User> list = new ArrayList<>();
while(rs.next()){
User user = new User();
user.setId(rs.getString("id"));
user.setName(rs.getString("name"));
user.setPassword(rs.getString("password"));
user.setEmail(rs.getString("email"));
user.setBirthday(rs.getDate("birthday"));
list.add(user);
}
System.out.println(list.size());
//6.释放资源
rs.close();
st.close();
conn.close();
}
}
JDBC规范中常用的接口和类
DriverManager
作用:
1. 注册驱动
1. 方式一:DriverManager.registerDriver(new com.mysql.jdbc.Driver());
缺点:
1. 严重依赖具体的数据库驱动
2. 导致数据驱动注册2遍
2. 方式二:(推荐)
2. 获取与数据库的链接
1. 方式一:
Connection conn = DriverManager.getConnection(“jdbc:mysql://localhost:3306/day15”,
“root”, “sorry”);
String url:JDBC与数据库厂商的协议。具体参考数据库的文档。
Oracle:jdbc:oracle:thin:@localhost:1521:orcl 瘦客户端链接方式
jdbc:oracle:client:@localhost:1521:orcl 客户端链接方式,高效
2. 方式二:
Connection conn = DriverManager.getConnection(“jdbc:mysql:///day15?user=root&password=sorry”);
Connection
作用:所有与数据库的交互都是基于链接的。
Statement
作用:代表SQL语句对象
常用方法:
- ResultSet executeQuery(String sql):执行查询,返回结果集。
- int executeUpdate(String sql):执行DML语句。返回的是SQL语句影响到的记录条数。
- boolean execute(String sql):执行任何的SQL语句。返回值不代表成功与否。如果执行的语句有结果集,返回true,否则返回false。
ResultSet
作用:封装了结果集
rs.getString("id")
根据列名获取值
rs.getString(1)
根据列好获取值,从1开始
数据库类型和Java类型的对应关系:
SQL类型 | JDBC对应方法 | 返回类型 |
---|---|---|
BIT(1) bit(n) | getBolean() getBytes() | Boolean byte[] |
TINYINT | getByte() | Byte |
SMALLINT | getShort() | Short |
Int | getInt() | Int |
BIGINT | getLong() | Long |
CHAR,VARCHAR,LONGVARCHAR | getString() | String |
Text(clob) blob | getClob() getBlob() | Clob Blob |
DATE | getDate() | java.sql.Date |
TIME | getTime() | java.sql.Time |
TIMESTAMP | getTimestamp() | java.sql.Timestamp |
常用的方法:
boolean next():向下移动。返回有没有记录
boolean provious():向上移动。返回有没有记录
boolean absolute(int row):定位。返回有没有记录。看第2条,写2.
void beforeFirst():移动到第一条记录的前面。默认位置
void afterLast():移动到最后一条记录的后面。
抽取JDBC的工具类
package com.jyh.jdbc;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JdbcUtil {
private static String driverClass;
private static String url;
private static String user;
private static String password;
static{
try {
//获取配置文件路径
String path = JdbcUtil.class.getClassLoader().getResource("jdbcConfig.properties").getPath();
//读取配置文件
InputStream is = new FileInputStream(path);
//将配置文件中的内容加载进Properties中
Properties ps = new Properties();
ps.load(is);
is.close();
//获取配置属性值
driverClass = ps.getProperty("driverClass");
url = ps.getProperty("url");
user = ps.getProperty("user");
password = ps.getProperty("password");
//注册数据库驱动
Class.forName(driverClass);
} catch (Exception e) {
throw new ExceptionInInitializerError();
}
}
//获取链接
public static Connection getConnection() throws Exception{
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
}
//关闭链接
public static void release(ResultSet rs,Statement st,Connection conn){
if(st != null){
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
st = null;
}
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
}
PreparedStatement接口
原则:能用PreparedStatement就不要使用Statement
优点:
- 参数使用占位符(?)替代
- 预编SQL语句,执行效率高
- 不存在SQL注入问题
批处理
//如果批处理执行的语句不相同,最好使用Statement
public void test1(){
Connection conn = null;
Statement stmt = null;
try{
conn = JdbcUtil.getConnection();
stmt = conn.createStatement();//内部有一个List,就是sql语句的缓存
String sql1 = "insert into t1 (id,name) values(1,'aa1')";
String sql2 = "insert into t1 (id,name) values(2,'aa2')";
String sql3 = "delete from t1 where id=1";
stmt.addBatch(sql1);
stmt.addBatch(sql2);
stmt.addBatch(sql3);
int [] ii = stmt.executeBatch();//批处理.每条语句影响的行数
for(int i:ii)
System.out.println(i);
}catch(Exception e){
e.printStackTrace();
}finally{
JdbcUtil.release(null, stmt, conn);
}
}
//很多时候:语句相同,只是参数不同
//PreparedStatement
//插入10条记录
@Test
public void test2(){
Connection conn = null;
PreparedStatement stmt = null;
try{
conn = JdbcUtil.getConnection();
stmt = conn.prepareStatement("insert into t1 (id,name) values(?,?)");
for(int i=0;i<10;i++){
stmt.setInt(1, i+1);
stmt.setString(2, "aa"+(i+1));
stmt.addBatch();//向缓存中加的参数
}
int [] ii = stmt.executeBatch();//批处理.每条语句影响的行数
for(int i:ii)
System.out.println(i);
}catch(Exception e){
e.printStackTrace();
}finally{
JdbcUtil.release(null, stmt, conn);
}
}
LOB(CLOB、BLOB)的存取
- LOB:Large Object。
CLOB:character Large Object。大文本。
BLOB:Binary Large Object。图片、视频、声音等。 - MySQL支持的LOB的类型。可变长度类型。
TINYBLOB, TINYTEXT 256B
BLOB, TEXT 64K
MEDIUMBLOB, MEDIUMTEXT 16M
LONGBLOB, LONGTEXT 4G
package com.jyh.lob;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import org.junit.Test;
import com.jyh.jdbc.JdbcUtil;
public class LobTest {
/**
* create table t1(
* id int primary key,
* content longtext
* )
* @throws Exception
*/
//数据库文本写入
@Test
public void testTextWrite() throws Exception{
Connection conn = JdbcUtil.getConnection();
PreparedStatement st = conn.prepareStatement("insert into t1(id,content) values(?,?)");
st.setInt(1, 1);
File file = new File("src/clob.txt");
Reader reader = new FileReader(file);
//setCharacterStream(int, Reader, long)MySQL没实现,因为MySQL最大存储小于long类型
st.setCharacterStream(2, reader, (int)file.length());
st.executeUpdate();
JdbcUtil.release(null, st, conn);
}
//数据库文本读取
@Test
public void testTextRead() throws Exception{
Connection conn = JdbcUtil.getConnection();
PreparedStatement st = conn.prepareStatement("select content from t1 where id = ?");
st.setInt(1, 1);
ResultSet rs = st.executeQuery();
if(rs.next()){
Reader reader = rs.getCharacterStream("content");
Writer wr = new FileWriter("d:/tt.txt");
char[] c = new char[1024];
int len = -1;
while((len = reader.read(c)) != -1){
wr.write(c, 0, len);
}
wr.flush();
reader.close();
wr.close();
}
JdbcUtil.release(rs, st, conn);
}
/**
* create table t2(
* id int primary key,
* content longblob
* )
* @throws Exception
*/
//二进制数据写入(图片、音频、视频等)
@Test
public void testBinaryWrite() throws Exception{
Connection conn = JdbcUtil.getConnection();
PreparedStatement st = conn.prepareStatement("insert into t2(id,content) values(?,?)");
st.setInt(1, 1);
InputStream in = new FileInputStream("src/1044490.jpg");
st.setBinaryStream(2, in , in.available());
st.executeUpdate();
JdbcUtil.release(null, st, conn);
}
//二进制数据读取(图片、音频、视频等)
@Test
public void testBinaryRead() throws Exception{
Connection conn = JdbcUtil.getConnection();
PreparedStatement st = conn.prepareStatement("select content from t2 where id = ?");
st.setInt(1, 1);
ResultSet rs = st.executeQuery();
if(rs.next()){
InputStream in = rs.getBinaryStream("content");
OutputStream out = new FileOutputStream("d:/1.jpg");
byte[] b = new byte[1024];
int len = -1;
while((len = in.read(b)) != -1){
out.write(b, 0, len);
}
out.flush();
in.close();
out.close();
}
JdbcUtil.release(rs, st, conn);
}
}
事务的入门
数据库的事务控制
start transaction:开启事务
rollback:回滚事务
commit:提交事务
JDBC控制事务
package com.jyh.transaction;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Savepoint;
import org.junit.Test;
import com.jyh.jdbc.JdbcUtil;
public class TransactionDemo {
@SuppressWarnings("resource")
@Test
public void demo1(){
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
Savepoint sp = null;
try{
conn = JdbcUtil.getConnection();
conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);//在事务之前设置事务的隔离级别
conn.setAutoCommit(false);//事务起始点
st = conn.prepareStatement("update account set salary=salary+100 where id = 1");
st.executeUpdate();
st = conn.prepareStatement("update account set salary=salary-100 where id = 2");
st.executeUpdate();
sp = conn.setSavepoint();//回滚点
st = conn.prepareStatement("update account set salary=salary+100 where id = 1");
st.executeUpdate();
@SuppressWarnings("unused")
int i = 1/0;
st = conn.prepareStatement("update account set salary=salary-100 where id = 3");
st.executeUpdate();
}catch(Exception e){
if(conn != null){
try {
conn.rollback(sp);//回滚到回滚点
} catch (SQLException e1) {
e1.printStackTrace();
}
}
}finally{
if(conn != null){
try {
conn.commit();//提交事务
} catch (SQLException e) {
e.printStackTrace();
}
}
JdbcUtil.release(rs, st, conn);
}
}
}
事务的特性
事务的特性
- 原子性:指处于同一个事务中的多条语句是不可分割的,要么全部成功,要么全部失败。
- 一致性:事务必须使数据库从一个一致性状态变换到另外一个一致性状态,相当于守恒。比如,转账,转账前两个账户余额之和为2k,转账之后也应该是2K。
- 隔离性:指多线程环境下,一个线程中的事务不能被其他线程中的事务打扰
- 持久性:事务一旦提交,就应该被永久保存起来。
事务隔离性专题
如果不考虑事务的隔离性,会出现以下问题:
1. 脏读:指一个线程中的事务读取到了另外一个线程中未提交的数据。
2. 不可重复读:指一个线程中的事务读取到了另外一个线程中提交的update的数据。
3. 虚读:指一个线程中的事务读取到了另外一个线程中提交的insert的数据。
隔离级别:
1:READ UNCOMMITTED:脏读、不可重复读、虚读都有可能发生。
2:READ COMMITTED:防止脏读的发生,不可重复读、虚读都有可能发生。
4:REPEATABLE READ:防止脏读、不可重复读的发生,虚读有可能发生。
8:SERIALIZABLE:防止脏读、不可重复读、虚读的发生。
级别越高,数据越安全,但性能越低。
- 脏读:事务提交之前发生。
a事务还没提交,b事务开始,然后获取的数据是a事务还没提交的数据。
READ COMMITTED就是防止脏读发生 - 不可重复读:事务提交之后发生。
a拿着工资卡去消费(a事务开启),系统读取到卡里确实有5000元,而此时他的老婆也正好在网上转账,把a工资卡的5000元转到另一账户(b事务开启),并在a之前提交了事务(b事务提交),当a扣款时,系统检查到a的工资卡已经没有钱,扣款失败,a十分纳闷,明明卡里有钱,为何……
总结就是a事务开始—>b事务开始—>b事务结束—>a事务结束
导致a得到的数据前后不一致
REPEATABLE READ解决不可重复读,相当于在操作某条数据的时候就已经锁定了该条数据,使得别人无法修改。 - 虚读:事务提交之后发生。
虚读其实跟不可重复读差不多,也是前后数据不一致,不过不是某一条,而是当前表格数据发生变化,也就是增加了一条数据。a事务开始读的时候是5条,然后b添加了一条就关闭了事务,a这时候还没关闭,再读取的时候就多了一条。
SERIALIZABLE是最高级别,防止了脏读,不可重复读和虚读,在我理解就相当于锁定了整张表,在某一个事务操作该表的时候,其它任何事务都不能对该表进行操作。
MySQL控制事务的隔离级别:
查看当前事务的隔离级别:SELECT @@tx_isolation;
更改事务的隔离级别:(注意:一定要在开启事务之前设定)
SET transaction isolation level 四个级别之一(单词);
JDBC中如何控制事务的隔离级别:
前提:在开启事务之前设置隔离级别。
javaconn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);//在事务之前设置事务的隔离级别