Java 中的数据存储技术
JDBC
JDBC体系结构
JDBC 的API
Java数据类型与MySQL数据类型对应关系
通过 JDBC 访问数据库的步骤
1.获取驱动
2.建立连接
连接数据库的URL
3.操作数据表
Statement 与 PreparedStatement
SQL注入攻击
- SQL注入攻击的主要原因在于SQL语句在编译的编译方式,通过Statement方式访问数据库,SQL语句是先通过字符串拼接,然后才是DBServer的编译器对SQL语句进行编译
SELECT user, password FROM user_table WHERE user='a' OR 1 = ' AND password = ' OR '1' = '1
- 为了防止防范 SQL 注入,只要用 PreparedStatement (从 Statement 扩展而来) 取代 Statement 就可以了
4. 获取SQL执行结果
5. 释放资源
public class JDBCUtils {
/**
*
* @Description 获取数据库的连接
*/
public static Connection getConnection() throws Exception {
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is); // 1.读取配置文件中的4个基本信息
String driverClass = pros.getProperty("driverClass"); // 2.获取 Driver 实现类对象:使用反射
Class.forName(driverClass);
String url = pros.getProperty("url"); // 3.提供要连接的数据库
String user = pros.getProperty("user");// 4.提供连接需要的用户名和密码
String password = pros.getProperty("password");
Connection conn = DriverManager.getConnection(url, user, password);// 5.获取连接
return conn;
}
/**
*
* @Description 关闭资源操作
*/
public static void closeResource(Connection conn,Statement ps,ResultSet rs){
try {
if(ps != null)
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn != null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(rs != null)
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public <T> T getInstance(Class<T> clazz,String sql, Object... args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) { // 为编译后的 Sql 传参
ps.setObject(i + 1, args[i]);
}
rs = ps.executeQuery();
// 获取结果集的元数据 :ResultSetMetaData
ResultSetMetaData rsmd = rs.getMetaData();
// 通过ResultSetMetaData获取结果集中的列数
int columnCount = rsmd.getColumnCount();
/**
* 如果返回多条记录
* while(rs.next()){
* //获取当前这条数据的各个字段值
* int id = resultSet.getInt(1);
* String name = resultSet.getString(2);
* String email = resultSet.getString(3);
* Date birth = resultSet.getDate(4);
* //方式一:
* System.out.println("id = " + id + ",name = " + name + ",email = " + email + ",birth = " + birth);
* //方式二:
* Object[] data = new Object[]{id,name,email,birth};
* //方式三:将数据封装为一个对象(推荐)
* Customer cst = new Customer(id, name, email, birth);
* System.out.println(cst);
* }
*/
if (rs.next()) { // next():判断结果集的下一条是否有数据,如果有数据返回true,并指针下移;如果返回false,指针不会下移
T t = clazz.newInstance();
// 处理结果集一行数据中的每一个列
for (int i = 0; i < columnCount; i++) {
// 获取列值
Object columValue = rs.getObject(i + 1);
// 获取每个列的列名
// String columnName = rsmd.getColumnName(i + 1);
String columnLabel = rsmd.getColumnLabel(i + 1);
// 给t对象指定的columnName属性,赋值为columValue:通过反射
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columValue);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResource(conn, ps, rs);
}
return null;
}
向数据表中插入、读取大数据:BLOB字段
// 插入大字段数据
Connection conn = JDBCUtils.getConnection();
String sql = "insert into customers(name,email,birth,photo)values(?,?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setObject(1,"袁浩");
ps.setObject(2, "yuan@qq.com");
ps.setObject(3,"1992-09-08");
FileInputStream is = new FileInputStream(new File("girl.jpg"));
ps.setBlob(4, is);
ps.execute();
JDBCUtils.closeResource(conn, ps,null);
// 查询大字段数据
Connection conn = null;
PreparedStatement ps = null;
InputStream is = null;
FileOutputStream fos = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
String sql = "select id,name,email,birth,photo from customers where id = ?";
ps = conn.prepareStatement(sql);
ps.setInt(1, 21);
rs = ps.executeQuery();
if(rs.next()){
/**
* 方式一
* int id = rs.getInt(1);
* String name = rs.getString(2);
* String email = rs.getString(3);
* Date birth = rs.getDate(4);
*/
//方式二
int id = rs.getInt("id");
String name = rs.getString("name");
String email = rs.getString("email");
Date birth = rs.getDate("birth");
Customer cust = new Customer(id, name, email, birth);
System.out.println(cust);
//将Blob类型的字段下载下来,以文件的方式保存在本地
Blob photo = rs.getBlob("photo");
is = photo.getBinaryStream();
fos = new FileOutputStream("zhangyuhao.jpg");
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer)) != -1){
fos.write(buffer, 0, len);
}
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
if(is != null)
is.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fos != null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
JDBCUtils.closeResource(conn, ps, rs);
}
批量插入数据
Connection conn = null;
PreparedStatement ps = null;
try {
long start = System.currentTimeMillis();
conn = JDBCUtils.getConnection();
conn.setAutoCommit(false);//设置不允许自动提交数据
String sql = "insert into goods(name)values(?)";
ps = conn.prepareStatement(sql);
for(int i = 1;i <= 1000000;i++){
ps.setObject(1, "name_" + i);
ps.addBatch();//1."攒"sql
if(i % 500 == 0){
ps.executeBatch();//2.执行batch
ps.clearBatch();//3.清空batch
}
}
//提交数据
conn.commit();
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));//20000:83065 -- 565
} catch (Exception e) { //1000000:16086 -- 5114
e.printStackTrace();
}finally{
JDBCUtils.closeResource(conn, ps,null);
}
数据库的事务
数据库事务的执行过程
COMMIT 和 ROLLBACK 的优点、数据完整性
COMMIT 、ROLLBACK 前
COMMIT 后
ROLLBACK 后
ACID
JDBC 事务处理
Connection conn = null;
try {
conn = JDBCUtils.getConnection();
System.out.println(conn.getAutoCommit());//true
//1.取消数据的自动提交
conn.setAutoCommit(false);
String sql1 = "update user_table set balance = balance - 100 where user = ?";
update(conn,sql1, "AA");
//模拟网络异常
System.out.println(10 / 0);
String sql2 = "update user_table set balance = balance + 100 where user = ?";
update(conn,sql2, "BB");
System.out.println("转账成功");
//2.提交数据
conn.commit();
} catch (Exception e) {
e.printStackTrace();
//3.回滚数据
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}finally{
// 关闭连接时,也会自动的提交数据
JDBCUtils.closeResource(conn, null,null);
}
事务的隔离级别
- 数据库的隔离级别即为事务的隔离级别
数据库连接池
- 常见的数据库连接池:C3P0→DBCP–Apache CommonPool→Druid→Hikari(流行时间排序)
- Druid的功能特点:可以对SQL性能进行监控,但是MySQL自身有慢日志可以查看
- Hikari是现在默认的数据库连接池
DateSource 类
- 通过数据库连接池得到的连接对象,使用
conn.close()
,并没有关闭数据库的物理连接,它仅仅把数据库连接释放,归还给了数据库连接池