红丝剪不断,金线系万千。烬心凝血弦,情深化柔绵。
连星理不乱,丝线四象穿。星云汇金缕,引线刺心弦。
post
学习java mysql的关闭资源方法中,学习到try catch with resource 方法,这个方法需要要求所释放资源类实现了AutoCloseable接口,代码介绍如下:
try(将需要关闭的资源语句写在这里){
......
}catch(错误){
}
这个方法被常用取代try-catch finally,除了写作简洁外,还有一个就是它避免了错误覆盖,通过addSuppressed()方法将抑制的错误信息存储下来,后面通过getSuppressed()输出。
参考博客地址:深入理解 Java 中的 try with resources
事务
事务是数据库管理系统执行过程中的一个逻辑单位,由有限个数据库操作序列组成。
事务四个特性:
1.原子性(Atomicity):事务作为一个整体被执行,包含其中的全部数据操作,要么全部执行,要么全部不执行,即回滚到执行前状态。
2.一致性(Consistency):事务应确保数据库状态从一个一致状态转变为另一个一致状态。一致状态是数据库中的数据应与数据库的定义一致,换句话说就是满足完整性约束。
3.隔离性(Isolation):多个事务并发执行时,一个事务的执行不影响其他事务执行,换句话说,每个事务只有自己执行的效果,没含带其他事务,具有独立性,最后,不同事务先后提交并执行后,最终呈现出来的效果是串行的。
3.持久性(Durability):已被提交的事务对数据库的修改应该是永久改变的。
隔离性最简单的实现方式就是各个事务都串行执行了,如果前面的事务还没有执行完毕,后面的事务就都等待。但是这样的实现方式很明显并发效率不高,并不适合在实际环境中使用。为了解决上述问题,实现不同程度的并发控制,SQL的标准制定者提出了不同的隔离级别:未提交读、提交读、可重复读、序列化读。
1. 未提交读(READ UNCOMMITTED)
最低级的的隔离级别,可以读取未提交事务的数据。
2. 提交读(READ COMMITTED)
一个事务只能看见已经提交的事务所做的改变,大多数数据库系统的默认隔离级别(但不是MYSQL默认的)。
3. 可重复读(REPEATABLE READ)
事务A在读到一条数据之后,此时事务B对该数据进行了修改并提交,那么事务A再读该数据,读到的还是原来的内容,可以说上了一个修改锁。
4. 可串行化(SERIALIZABLE)
最高级别隔离,强制事务串行执行,时间消耗大,导致超市现象和锁竞争,一般不用该级别,而是采用乐观锁与悲观锁。
较低的隔离级别能够提高效率,但是却会导致很多问题,常见的4个问题如下:
1.脏读
脏读发生于未提交读级别,事务A读取事务B的修改的数据,但是事务B提交前回滚,即撤回操作,此时事务A读取的数据为无用数据,出现了脏读,脏读就是读取脏的数据(垃圾数据)。
2. 不可重复读问题(对于列说)
不可重复读问题是说事务两次读取的数据不一致,发生于并发事务中,事务A第一次读取数据,随后,事务B修改数据的某一列,此时提交后,数据修改,此时事务A又再次读取数据,此时读取数据的某一列不一致,从而产生不可重复读问题。
3. 幻读问题(对于行说)
幻读问题是说事务第一次读的数据与第二次读的数据数量不一致,从而产生幻觉的问题,事务A第一次读取数据,此时事务B增加或删减了一行数据后,此时事务A再次读取数据时产生了两者不一致的幻觉。
4. 丢失更新问题
丢失更新问题是说事务A的更新被事务B的修改覆盖,从而丢失了更新,丢失问题分为两种,第一种为回滚丢失更新,在事务A执行更新时,事务B执行更新并提交,但是事务A最后回滚撤销,进而回滚到事务开始状态,此时事务B的更新也会被覆盖,第二种为提交覆盖数据更新,事务A的提交修改了期间事务B的更新的数据,从而导致了事务B更新消失,因为丢失更新1比较严重,所以数据库本身不允许这一类丢失更新的发生。
对于不同隔离等级会出现的问题汇总如下:
隔离级别 | 脏读 | 不可重复读 | 幻读 | 丢失更新1 | 丢失更新2 |
---|---|---|---|---|---|
未提交读 | 发生 | 发生 | 发生 | 不发生 | 发生 |
提交读 | 不发生 | 发生 | 发生 | 不发生 | 发生 |
可重复读 | 不发生 | 不发生 | 发生 | 不发生 | 不发生 |
可串行化 | 不发生 | 不发生 | 不发生 | 不发生 | 不发生 |
前面说了乐观锁与悲观锁,但是这里要强调乐观锁与悲观锁是思想,不是具体实现,那些具体锁可以根据思想划分为乐观锁或悲观锁。乐观锁又叫乐观并发控制(OCC),悲观锁又叫悲观并发控制(PCC)。
乐观锁是认为外界对数据的操作一般是不会发生冲突的,因此在操作过程不会进行加锁,而是当提交的时候才会进行检测加锁,数据库的乐观锁,并不是利用数据库本身的锁去实现的,可能是利用某种实现逻辑去实现做到乐观锁的思想。
悲观锁是认为外界对数据的操作默认是会发生冲突的,所以在数据操作的整个过程都会处于加锁状态,保证同一时间只有一个线程可以访问到数据,通常利用数据库本身提供的锁机制去实现。
乐观锁使用逻辑进行上锁,一般基于CAS思想进行设计,CAS思想是通过对比旧期望值a(在操作前进行读取,并进行赋值)与需要进行最终修改时读取值b进行比较,如果一致,则表明在此期间没有人进行修改,可以进行操作,而如果不一致,说明在此期间已经有人进行了修改,不执行更新,基于此思想数据库有两种方式:
删除表
删除表有三种方式:
1.删除表
DROP TABLE 表名;
2.删除表数据,而不是删除表结构,不能使用where指定具体内容。
TRUNCATE TABLE 表名;
3.删除表数据,可与where搭配使用,删除指定内容。
DELETE FROM 表名 WHERE 或者DELETE FROM 表名。
不同点:
1.DROP 与 TRUNCATE 为数据库定义语言DDL,执行后自动提交,DELETE为数据库操作语言。
2.truncate和delete 只删除数据不删除表结构,truncate 删除后将重建索引(新插入数据后id从0开始记起),而 delete不会删除索引 (新插入的数据将在删除数据的索引后继续增加),drop语句将删除表的结构包括依赖的约束,触发器,索引等;
3.drop和truncate删除时不记录MySQL日志,不能回滚,delete删除会记录MySQL日志,可以回滚;
4.返回值:delete 操作后返回删除的记录数,而 truncate 返回的是0或者-1(成功则返回0,失败返回-1)
sql注入
sql注入是常见的攻击形式,通过注入sql语句,导致程序的安全以及数据库安全。
sql注入一般见效于拼接sql语句中,通过破坏输入规则,插入期望的sql语句,以此达到所要达到的目的。
两个例子:
1.查找sql注入,一般用于登录界面。
拼接sql语句:
String sql = "SELECT * FROM student where username ='"+username+"' AND password = '"+password+"';"
//AND 运行优先级高于 OR
注入sql语句:
username = "123";
password = "'or '1'='1";
SQL最终语句:
SELECT * FROM student where username ='123' AND password = '' or '1'='1';
因而可以将所有数据查出,造成登录成功。
2.在查询操作中插入删除操作。
拼接sql语句:
String sql = "SELECT * FROM student where username ='"+username+"' AND password = '"+password+"';"
注入sql语句:
username = "123";
password = "'; DELETE FROM student where '1'='1"
SQL最终语句:
SELECT * FROM student where username ='123' AND password =''; DELETE FROM student where '1'='1';
注:该操作是批处理操作,但是java 中mysql执行方法execute不支持此操作。
因此为了避免sql注入,我们需要使用参数化sql语句,即预编译好的sql语句,除此之外做好用户输入数据的验证与过滤操作,以及最小化用户权限。即使是参数化sql语句,我们也要避免拼接sql语句,否则依旧会有被sql注入的可能。
这里主要是学习参数化sql语句,其他的会在后面项目中学习。
参数化sql语句就是使用占位符?代替部分参数,通过prepareStatement(String sql),创建一个preparedStatement,随后使用不带参数的execute/executeUpdate/executeQuery命令执行操作,不过在这之前通过preparedStatement对象的setInt与setString方法对?占位参数赋值,其中第一个参数为int型,表示第几个占位符,后面则为要赋的值。下面展示一个例子:
Connection conn = DriverManager.getConnection(url,username,password);
String sql = "INSERT INTO student(name,id_card) VALUES(?,?);";
PreparedStatement pstm = conn.prepareStatemnet(sql);
pstm.setString(1,"1234");
pstm.setString(2,"1234");
pstm.executeUpdate();
ResultSetMetaData rsmd = rs.getMetaData();int columnCount = rsmd.getColumnCount();这个可以获取字段数目,当我们忘记原本的数据库字段数时使用。