JDBC的学习(02)

关于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设置隔离级别方法
  1. 查询当前隔离级别
select @@tx_isolation
  1. 设置当前MySQL连接的隔离级别
set transaction isolation level read committed;
  1. 设置数据库系统全局的隔离级别
set global transaction isolation level read committed;
DAO概念
1、概念

DAO :Data Access Object 数据访问的类和接口,包括了对数据的插入,更新,删除等。数据访问:顾名思义就是与数据库打交道,中间不掺杂任何其他的业务,

2、功能实现

主要实现思想是:

  1. 创建一个BaseDao的类,里面实现通用的增删改查的方法。
  2. 创建一个针对于某个表的接口,定义该表所需要实现的抽象方法、
  3. 创建一个上面接口的实现类,并且继承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;
		
	}

}

以上代码是自己根据提示手敲的,以后希望可静下心来仔细看看代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java-gang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值