关于JDBC的学习(2)
文章目录
Blob类型和批量操作
一定要注意环境:1、java驱动(Driver类) 2、数据库环境
1、Blob类型操作
首先,Blob数据类型是什么?
Blob(Binary large Object),顾名思义 二进制大对象,用来存储大量的二进制和文本数据的一种数据类型。这里以图片为例。
Mysql的4中Blob类型:
TinyBlob-最大255个字节 Blob-最大65个字节 MediumBlob-最大16M LongBlob-最大4G
Blob类型存储的操作流程是什么?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cJR8IT2P-1587998597227)(E:\网课学习笔记\学习总结-md文件\JDBC.asset\BLob流程.png)]
上图为读/写一个Blob的输入流。
上传Blob数据类型代码:
public void setBlobType() {
Connection conn = null;
PreparedStatement ps = null;
int flag = 0;
try {
//1、建立连接
conn = JDBCUtils.connectionMysql();
//2、预编译SQL语句
String sql = "insert into customers(name, email, birth, photo) values (?, ?, ?, ?)";
ps = conn.prepareStatement(sql);
//3、填充占位符
ps.setObject(1, "张三");
ps.setObject(2, "zhangsan@163.com");
ps.setObject(3, "1997-05-30");
//4、填充流,导入要保存的图片
InputStream in = new FileInputStream(new File("1.jpg"));
ps.setBlob(4, in);
// ps.setBinaryStream(4, in); 这是第二种方法
//5、提交执行
flag = ps.executeUpdate();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
//6、关闭资源
JDBCUtils.closeRes(conn, ps, null);
}
//7、检测是否成功
if (flag != 0) {
System.out.println("添加成功");
}else {
System.out.println("添加不成功");
}
}
读取Blob代码:(这里实现的是,读取MySQL数据库中的Blob类型的图片,并写入到自己磁盘)
public void getBlobType() {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
InputStream in = null;
FileOutputStream fos = null;
try {
//1、建立连接
conn = JDBCUtils.connectionMysql();
//2、预编译SQL语句
String sql = "select name, email, birth, photo from customers where id = ?";
ps = conn.prepareStatement(sql);
//3、填充占位符
ps.setObject(1, 24);
//4、提交操作,获得结果集
rs = ps.executeQuery();
if(rs.next()) {
String name = rs.getString(1);
String email = rs.getString(2);
Date birth = rs.getDate(3);
System.out.println(name + ":" + email + ":" + birth);
}
//5、获取流,将它保存到本地上
Blob blob = rs.getBlob(4);
in = blob.getBinaryStream();
//in = rs.getBinaryStream("photo");
byte[] bytes = new byte[1024];
fos = new FileOutputStream("zhangsan1.jpg");
int len = 0;
while ( (len = in.read(bytes))!= -1) {
fos.write(bytes, 0, len);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
JDBCUtils.closeRes(conn, ps, rs);
}
//6、释放资源
try {
if (in != null) {
in.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
关于JDBC使用PreparedStatement一个重要原因就是,Statement不支持传输Blob类型(MySQL支持)的数据。
总结:关于Blob,个人感觉就像是读写文件一样,在上传和下载过程中,区别就是存取的位置不一样。为了抓紧入门,没有去理解深度的原理。切记一定仔细参阅API文档,很重要。
2、批量操作
应用场景:当我们要大量的重复同一件事时(操作相同,数据不同),如成批插入和更新记录时,我们可以采用Java的批量更新机制。
这里扩展一下执行插入操作的一个过程。
数据库执行操作一般分为两个步骤:1、操作的执行 2、提交(commit)
只有数据库执行提交操作,才算真正的执行操作,并且不能回滚的。
Java的批量更新机制允许多条语句一次性提交数据库进行批量处理。
就像是运送东西一样,我原本只能一次运送一个,有了这个机制,一次就可以运送多个;
主要涉及三个代码:
// 我们这里需要一些循环操作,因为我们传入的SQL语句并不是一次性的,而是我们主动的去让它执行多少次语句。
addBatch(String sql); // sql 要批量执行SQL语句 我们这里需要一些循环操作,因为我们传入的SQL
executeBatch(); // 提交我们已经积累的SQL语句
//当我们提交完成后,需要将缓存的数据清空,在进行“攒”
clearBatch(); //清空缓存的数据
大部分使用的可能是直接从excel中去往数据库写文件。
数据库事务
1、事务的概念
事务是什么?
一组逻辑操作单元。事务其实就是指我们要执行的事情,这其中可能包括一个或多个操作。
事务的处理:保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方
式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久地保存下来;
要么数据库管理系统将放弃所作的所有修改,整个事务回滚(rollback)到最初状态。
打个比方,就像过河一样,我们要么到河的另一边,要么就要回到河的这一边。不能保持在停留在河中的一种状态。
2、事务的ACID属性
- 原子性(Atomicity):是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生
- 一致性(Consistent):事务必须使数据库从一个一致性状态变换到另一个一致性状态
- 隔离性(Isolation):事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能相互干扰。
- 持久性(Durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久行的,接下来的其他操作和数据库故障不应该对其有任何影响。
3、数据库的四种隔离级别
数据库的并发问题
- 脏读:事务读取到其他事务没有被提交的字段,如果其他事务取消提交,这读到的数据无效。
- 不可重复读:是指在同一事务中,事务读取数据之后,其他事务修改了数据,然后这个事务再次读取数据,两次读取不一致。
- 幻读:就是在一个事务读取的数据的时候,另个一事务向其中插入数据,然后这个事务再次读取的时候,就会多出好几个行。
数据库提供的隔离级别
隔离级别 | 描述 |
---|---|
READ UNCOMMITED (读未提交数据) | 允许事务读取未被其他事务提交的变更, 不能解决数据库的任意并发问题 |
READ COMMIT (读已提交数据) | 只能读取已经被其他事务提交的数据,可以避免脏读,不能解决不可重复读和幻杜问题 |
REPEATABLE READ (可重复读) | 确保事务可以多次从一个字段中读取相同的值,在本事务执行期间,禁止其他事务对该字段的更新操作。可以解决脏读和不可重复读,但是幻读问题无法解决。 |
SERIALIZABLE (串行化) | 确保事务可以从一个表中读取相同的行,这个事务持续期间,禁止其他事务对该表执行插入,更新,和删除操作,所有并发问题都可以避免,性能低下。 |
MySQL默认的是REPEATABLE READ,并且MySQL支持以上4中隔离级别
Oracle默认的是READ COMMITED 并且 Oracle只支持 READ COMMITED,SERIALIZABLE,
4、MySQL设置隔离级别方法
- 查询当前隔离级别
select @@tx_isolation
- 设置当前MySQL连接的隔离级别
set transaction isolation level read committed;
- 设置数据库系统全局的隔离级别
set global transaction isolation level read committed;
DAO概念
1、概念
DAO :Data Access Object 数据访问的类和接口,包括了对数据的插入,更新,删除等。数据访问:顾名思义就是与数据库打交道,中间不掺杂任何其他的业务,
2、功能实现
主要实现思想是:
- 创建一个BaseDao的类,里面实现通用的增删改查的方法。
- 创建一个针对于某个表的接口,定义该表所需要实现的抽象方法、
- 创建一个上面接口的实现类,并且继承BaseDao的类。重写接口中的所有方法,就可以使用了。
jdbc.properties
userName=javaweb
password=123456
url=jdbc:mysql://192.168.1.10:3306/jdbc
driverClassName=com.mysql.cj.jdbc.Driver
BaseDao.java
public abstract class BaseDao<T> {
/**
* @throws Exception
*
* @Title: connection_MySql
* @Description: TODO 这是建立连接的类
* @return void 返回类型
* @throws
*/
public static Connection connection_MySql() {
//1、加载配置文件
InputStream in = BaseDao.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
Connection conn = null;
try {
//2、读取配置文件信息
pros.load(in);
String className = pros.getProperty("className");
String url = pros.getProperty("url");
String user = pros.getProperty("user");
String password = pros.getProperty("password");
//3、注册Driver类信息
Class.forName(className);
//4、建立连接
conn = DriverManager.getConnection(url, user, password);
return conn;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
*
* @Title: closeRes
* @Description: TODO 关闭资源
* @param conn
* @param ps
* @param rs 参数
* @return void 返回类型
* @throws
*/
public static void closeRes(Connection conn, PreparedStatement ps, ResultSet rs) {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
*
* @Title: update
* @Description: TODO 执行增删改的通用方法
* @param conn
* @param sql
* @param args
* @return 参数
* @return int 返回类型
* @throws
*/
public static int update(Connection conn, String sql, Object...args) {
PreparedStatement ps = null;
try {
//1、预编译SQL语句
ps = conn.prepareStatement(sql);
//2、填充占位符
for (int i = 0; i < args.length; i ++) {
ps.setObject(i + 1, args[i]);
}
//3、提交语句获得返回值Long
int resultCount = ps.executeUpdate();
return resultCount;
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
/**
*
* @Title: query
* @Description: TODO 只能查询一条信息的查询方法
* @param <T>
* @param clazz
* @param conn
* @param sql
* @param args
* @return 参数
* @return T 返回类型
* @throws
*
*/
public static <T> T query(Class<T> clazz,Connection conn, String sql, Object...args) {
PreparedStatement ps = null;
ResultSet rs = null;
try {
//1、预编译SQL语句
ps = conn.prepareStatement(sql);
//2、填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
//3、提交查询语句并返回结果集
rs = ps.executeQuery();
//4、获得结果集的元数据
ResultSetMetaData rsmd = rs.getMetaData();
//5、获取列数和列名
int columnCount = rsmd.getColumnCount();
//6、利用反射动态的给javabean类赋值
if (rs.next()) {
T resultData = clazz.newInstance();
for (int i = 0; i < columnCount; i ++) {
String columnName = rsmd.getColumnLabel(i + 1);
Object getObjectData = rs.getObject(columnName);
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(resultData, getObjectData);
}
//7、返回javabean类
return resultData;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
closeRes(null, ps, rs);
}
return null;
}
/**
*
* @Title: queryAll
* @Description: TODO 查询所有结果的方法
* @param <T>
* @param clazz
* @param conn
* @param sql
* @param args
* @return
* @throws Exception 参数
* @return List<T> 返回类型
* @throws
*/
public static <T> List<T> queryAll(Class<T> clazz, Connection conn, String sql, Object...args) {
ArrayList<T> list = new ArrayList<T>();
PreparedStatement ps = null;
ResultSet rs = null;
try {
//1、预编译SQL语句
ps = conn.prepareStatement(sql);
//2、填充占位符
for (int i = 0; i < args.length; i ++) {
ps.setObject(i + 1, args[i]);
}
//3、提交语句并返回结果集
rs = ps.executeQuery();
//4、获得结果集的元数据
ResultSetMetaData rsmd = rs.getMetaData();
//5、获取列数和列名
int columnCount = rsmd.getColumnCount();
//6、处理每一条返回语句
while (rs.next()) {
T resultData = clazz.newInstance();
for(int i = 0; i < columnCount; i ++) {
String columnLabel = rsmd.getColumnLabel(i + 1);
Object getObjectData = rs.getObject(columnLabel);
//7、利用反射动态的给javabean类赋值
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(resultData, getObjectData);
}
list.add(resultData);
}
//8、返回结果的集合
return list;
} catch (Exception e) {
e.printStackTrace();
}finally {
closeRes(null, ps, rs);
}
return null;
}
public <E> E 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 (E) rs.getObject(1);
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
JDBCUtils.closeRes(null, ps, rs);
}
return null;
}
}
CustomersDao.java ---- 针对于Customer表的接口
public interface CustomersDao {
/**
*
* @Title: insert
* @Description: TODO 将cust对象添加到数据库中
* @param conn
* @param cuts 参数
* @return void 返回类型
* @throws
*/
public void insert(Connection conn, Customers cuts);
/**
*
* @Title: deleteById
* @Description: TODO 针对指定ID,删除表中的一条记录
* @param conn
* @param id 参数
* @return void 返回类型
* @throws
*/
public void deleteById(Connection conn, int id);
/**
*
* @Title: update
* @Description: TODO 针对内存中的cust对象,去修改数据表中的指定的记录
* @param conn
* @param cust 参数
* @return void 返回类型
* @throws
*/
public void update(Connection conn, Customers cust);
/**
*
* @Title: getCustomerById
* @Description: TODO 针对指定的id查询得到对应的Customer对象
* @param conn
* @param id 参数
* @return void 返回类型
* @throws
*/
public void getCustomerById(Connection conn, int id);
/**
*
* @Title: getAll
* @Description: TODO 查询表中的所有记录构成的集合
* @param conn 参数
* @return void 返回类型
* @throws
*/
public void getAll(Connection conn);
/**
*
* @Title: getCount
* @Description: TODO 返回数据表中的数据的条目数
* @param conn
* @return 参数
* @return Long 返回类型
* @throws
*/
public Long getCount(Connection conn);
/**
*
* @Title: getMaxBirth
* @Description: TODO 返回数据表中最大的生日
* @param conn
* @return 参数
* @return Date 返回类型
* @throws
*/
public Date getMaxBirth(Connection conn);
}
CustomersDaoImpl.java
public class CustomersDaoImpl extends BaseDao<Customers> implements CustomersDao {
@Override
public void insert(Connection conn, Customers cuts) {
String sql = "insert into customers(name, email, birth) values(?,?,?)";
int counts = update(conn, sql, cuts.getName(), cuts.getEmail(), cuts.getBirth());
if (counts != 0) {
System.out.println("插入成功");
}else {
System.out.println("插入失败");
}
}
@Override
public void deleteById(Connection conn, int id) {
String sql = "delete from customers where id = ?";
int counts = update(conn, sql, id);
if (counts != 0) {
System.out.println("删除成功");
}else {
System.out.println("删除失败");
}
}
@Override
public void update(Connection conn, Customers cust) {
String sql = "update customers set name = ?, email = ?, birth = ? where id = ?";
int counts = update(conn, sql, cust.getName(), cust.getEmail(), cust.getBirth(), cust.getId());
if (counts != 0) {
System.out.println("更新成功");
} else {
System.out.println("更新失败");
}
}
@Override
public void getCustomerById(Connection conn, int id) {
String sql = "select id, name, email, birth from customers where id = ?";
Customers result = query(Customers.class, conn, sql, id);
if (result != null) {
System.out.println(result);
} else {
System.out.println("查询为空");
}
}
@Override
public void getAll(Connection conn) {
String sql = "select id, name, email, birth from customers";
List<Customers> result = queryAll(Customers.class, conn, sql);
result.forEach(System.out :: println);
}
@Override
public Long getCount(Connection conn) {
String sql = "select count(*) from customers";
Long value = (long)getValue(conn, sql);
if (value != 0) {
System.out.println(value);
} else {
System.out.println("查询为空");
}
return value;
}
@Override
public Date getMaxBirth(Connection conn) {
String sql = "select Max(birth) from customers";
Date date = (Date)getValue(conn, sql);
System.out.println(date);
return date;
}
}
以上代码是自己根据提示手敲的,以后希望可静下心来仔细看看代码