数据库事务,JDBC操作和数据类型


目录

1 数据库事务

1.1 事务特性

事务的特性:

  1. 原子性(atomicity):事务是数据库的逻辑工作单位,而且是必须是原子工作单位,对于其数据修改,要么全部执行,要么全部不执行。
  2. 一致性(consistency):事务在完成时,必须是所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。
    事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定
  3. 隔离性(isolation):一个事务的执行不能被其他事务所影响。
  4. 持久性(durability):一个事务一旦提交,事物的操作便永久性的保存在DB中。即使此时再执行回滚操作也不能撤消所做的更改

事务(Transaction):是并发控制的单元,是用户定义的一个操作序列。这些操作要么都做,要么都不做,是一个不可分割的工作单位。通过事务,能将逻辑相关的一组操作绑定在一起,以便服务器 保持数据的完整性。事务通常是以begin transaction开始,以commitrollback结束。Commint表示提交,即提交事务的所有操作。具体地说就是将事务中所有对数据的更新写回到磁盘上的物理数据库中去,事务正常结束。Rollback表示回滚,即在事务运行的过程中发生了某种故障,事务不能继续进行,系统将事务中对数据库的所有已完成的操作全部撤消,滚回到事务开始的状态。

  • 自动提交事务:每条单独的语句都是一个事务。每个语句后都隐含一个commit (默认)
  • 显式事务:以begin transaction显示开始,以commitrollback结束。
  • 隐式事务:当连接以隐式事务模式进行操作时,数据库引擎实例将在提交或回滚当前事务后自动启动新事务。无须描述事物的开始,只需提交或回滚每个事务。但每个事务仍以commitrollback显式结束。
    连接将隐性事务模式设置为打开之后,当数据库引擎实例首次执行下列任何语句时,都会自动启动一个隐式事务:alter table,insert,create,open ,delete,revoke ,drop,select, fetch ,truncate table,grant,update在发出commitrollback;语句之前,该事务将一直保持有效。在第一个事务被提交或回滚之后,下次当连接执行以上任何语句时,数据库引擎实例都将自动启动一个新事务。该实例将不断地生成隐性事务链,直到隐性事务模式关闭为止。

1.2 事务并发问题

并发问题可归纳为以下几类:

  1. 丢失更新:撤销一个事务时,把其他事务已提交的更新数据覆盖(A和B事务并发执行,A事务执行更新后,提交;B事务在A事务更新后,B事务结束前也做了对该行数据的更新操作,然后回滚,则两次更新操作都丢失了)
  2. 脏读:一个事务读到另一个事务未提交的更新数据(A和B事务并发执行,B事务执行更新后,A事务查询B事务没有提交的数据,B事务回滚,则A事务得到的数据不是数据库中的真实数据。也就是脏数据,即和数据库中不一致的数据)
  3. 不可重复读:一个事务读到另一个事务已提交的更新数据(A和B事务并发执行,A事务查询数据,然后B事务更新该数据,A再次查询该数据时,发现该数据变化了)
  4. 覆盖更新:这是不可重复读中的特例,一个事务覆盖另一个事务已提交的更新数据(即A事务更新数据,然后B事务更新该数据,A事务查询发现自己更新的数据变了)
  5. 虚读(幻读):一个事务读到另一个事务已提交的新插入的数据(A和B事务并发执行,A事务查询数据,B事务插入或者删除数据,A事务再次查询发现结果集中有以前没有的数据或者以前有的数据消失了)

1.3 四种事务隔离级别

数据库系统提供了四种事务隔离级别供用户选择:

  1. ISOLATION_DEFAULT(默认隔离级别 ):这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别,
    oracle默认的是:READ_COMMITTEDmysql默认的是:REPEATABLE_READ
  2. Read Uncommitted(读未提交数据):一个事务在执行过程中可以看到其他事务没有提交的新插入的记录,而且能看到其他事务没有提交的对已有记录的更新
    这种隔离级别会产生脏读,不可重复读和幻像读
  3. Read Commited(读已提交数据):一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,而且能看到其他事务已经提交的对已有记录的更新
    这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读
  4. Repeatable Read(可重复读):一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,但是不能看到其他其他事务对已有记录的更新
    这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读
  5. Serializable(串行化):一个事务在执行过程中完全看不到其他事务对数据库所做的更新(事务执行的时候不允许别的事务并发执行。事务串行化执行,事务只能一个接着一个地执行,而不能并发执行)
    除了防止脏读,不可重复读外,还避免了幻像读
丢失更新 脏读 非重复读 覆盖更新 幻像读
未提交读 Y Y Y Y Y
已提交读 N N Y Y Y
可重复读 N N N N Y
串行化 N N N N N

1.4 保存点提交和回滚操作

保存点(SavePoint),JDBC定义了SavePoint接口,提供在一个更细粒度的事务控制机制。当设置了一个保存点后,可以rollback到该保存点处的状态,而不是rollback整个事务。
Connection接口的setSavepointreleaseSavepoint方法可以设置和释放保存点。
JDBC规范虽然定义了事务的以上支持行为,但是各个JDBC驱动,数据库厂商对事务的支持程度可能各不相同。如果在程序中任意设置,可能得不到想要的效果。为此,JDBC提供了DatabaseMetaData接口,提供了一系列JDBC特性支持情况的获取方法。比如,通过DatabaseMetaData.supportsTransactionIsolationLevel方法可以判断对事务隔离级别的支持情况,通过DatabaseMetaData.supportsSavepoints方法可以判断对保存点的支持情况

rollbackcommit的作用都完成对数据库的一次操作,并且释放当前的一些资源, 对于commit之后的事务,是不能够再进行回滚 ,但是对于回滚之后的失误,在不同的情况下却可以选择提交,rollback()rollback(Savepoint)的区别也就在于此
这两方面方法会抛出SQLException,如果该事务为自动提交,即:
connection.setAutoCommint(true),或者是默认设置.

参看以下代码:

private String sql = "insert into t_transaction_test (name,value)  values(?,?)";
      pstm = conn.prepareStatement(sql);
        
        pstm.setString(1, "test9");
        pstm.setString(2, "test9");
        pstm.execute();
        Savepoint sp = conn.setSavepoint();
        
        pstm.setString(1, "test10");
        pstm.setString(2, "test10");
        pstm.execute();
        Savepoint sp2 = conn.setSavepoint();
  
        conn.rollback();
        conn.commit();
        System.out.println("Insert OK " + sp.getSavepointId());

在这段代码中,程序已经被rollback,因此接下来的commit()是没有任何意义的,但是如果将程序改为:

conn.rollback();
conn.commit(sp);

那么接下来的commit()将是有意义的,数据库中会记录第一条数据

2 JDBC操作

2.1 使用Batch批量处理数据库

当需要向数据库发送一批SQL语句执行时,应避免向数据库一条条的发送执行,而应采用JDBC的批处理机制,以提升执行效率

2.1.1 Statement批处理

使用Statement批处理

Statement.addBatch(sql) list 执行批处理SQL语句
executeBatch()方法:执行批处理命令
clearBatch()方法:清除批处理命令

Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JDBCManager.getConnection();
String sql1 = "insert into user(name,password,email,birthday) 
values('kkk','123','abc@sina.com','1978-08-08')";
String sql2 = "update user set pw='123456' where id=3";
st = conn.createStatement();
st.addBatch(sql1);  //把SQL语句加入到批命令中
st.addBatch(sql2);  //把SQL语句加入到批命令中
st.executeBatch();
} finally{
JDBCManager.DBclose(con,st,rs);
}

采用Statement.addBatch(sql)方式实现批处理:
优点:可以向数据库发送多条不同的SQL语句。
缺点:SQL语句没有预编译。
当向数据库发送多条语句相同,但仅参数不同的SQL语句时,需重复写上很多条SQL语句。例如:

Insert into user(name,password) values(‘aa’,’111’);
Insert into user(name,password) values(‘bb’,’222’);
Insert into user(name,password) values(‘cc’,’333’);
Insert into user(name,password) values(‘dd’,’444’);

2.1.2 PreparedStatement批处理

PreparedStatement批处理

PreparedStatement.addBatch();

conn = JDBCManager.getConnection();//获取工具;
String sql = "insert into user(name,password,email,birthday) values(?,?,?,?)";
st = conn.prepareStatement(sql);//预处理sql语句;
for(int i=0;i<50000;i++){
st.setString(1, "aaa" + i);
st.setString(2, "123" + i);
st.setString(3, "aaa" + i + "@sina.com");
st.setDate(4,new Date(1980, 10, 10));
 
st.addBatch();//将一组参数添加到此 PreparedStatement 对象的批处理命令中。
if(i%1000==0){
st.executeBatch();
st.clearBatch();清空此 Statement 对象的当前 SQL 命令列表。 
}
}

st.executeBatch();将一批命令提交给数据库来执行,如果全部命令执行成功,则返回更新计数组成的数组。返回数组的 int 元素的排序对应于批中的命令,批中的命令根据被添加到批中的顺序排序

采用PreparedStatement.addBatch()实现批处理
优点:发送的是预编译后的SQL语句,执行效率高。
缺点:只能应用在SQL语句相同,但参数不同的批处理中。因此此种形式的批处理经常用于在同一个表中批量插入数据,或批量更新表的数据。

2.2 获得数据库自动生成的主键

Connection con=null;
PreparedStatement ps=null;
con = JDBCManager.getConnection();
String sql="insert into users(name,password) values(?,?)";
try {
ps = con.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);//获取返回的主键;
ps.setString(1, "qq");
ps.setString(2, "123");
ps.executeUpdate();
ResultSet rs=ps.getGeneratedKeys();//返回一个结果集,保存着产生的key的结果集,
while(rs.next()){
 System.out.println(rs.getObject(1));//结果集只有一个值;
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
JDBCManager.DBclose(con, ps, null);
}

2.3 JDBC调用存储过程

点击了解MySQL存储过程创建
点击了解Oracle存储过程创建

JDBC调用存数过程(创建好存储过程体):
当值是输入函数时:

Connection con=null;
       CallableStatement cs=null;
       con=JDBCManager.getConnection();
       try {
cs=con.prepareCall("{call pd(?,?)}");//存储过程语句;
cs.setString(1, "yy");
cs.setString(2, "msn");
cs.execute();//执行
System.out.println("执行成功");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
JDBCManager.DBclose(con, cs, null);
}

当输入和输出同时:

Connection con=null;
      CallableStatement cs=null;
      con=JDBCManager.getConnection();
      try {
cs=con.prepareCall("{call pcall(?,?)}");
cs.setInt(1, 10);
cs.registerOutParameter(2,Types.CHAR);//获取一下注册类型;
cs.execute();//执行
System.out.println(cs.getString(2));//获取第二个String类型的参数值;
cs.execute();
System.out.println("执行成功");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
JDBCManager.DBclose(con, cs, null);
}

3 Oracle和MySQL的数据类型

OracleMySQL的数据类型

数据类型 Oracle MySQL
NUMBER(p,s) 存储数值数据类型,如浮点型、整数型、分数、双精度等。其中p为精度,表示数字的总位数(1 <= p <=38) ; s为范围,表示小数点右边的位数,它在-84至127之间。默认38位
TINYINT 微整型,1字节
SMALLINT 小整型,2字节
MEDIUMINT 中整型,3字节
INT或INTEGER 整数数据类型 整型,4字节
BIGINT 大整型,8字节
FLOAT 浮点数数据类型 单精度浮点数值,4字节
DOUBLE 双精度浮点数值,8字节
DECIMAL 对DECIMAL(M,D) ,如果M>D,为M+2否则为D+2
CHAR(n) n=1to2000字节,定长字符串,n字节长,如果不指定长度,缺省为1个字节长(一个汉字为2字节) 定长字符串,0-255字节
NCHAR(n) NLS(national language support , 国际语言支持)的数据类型仅可以存储由数据库 NLS 字符集定义的 Unicode 字符集。Oracle最多可以存储2000Byte NLS(national language support , 国际语言支持)的数据类型仅可以存储由数据库 NLS 字符集定义的 Unicode 字符集。
VARCHAR(n) 最多可以以可变长度来存储4000B,因此不需要空格来作补充 变长字符串,0-255字节
NVARCHAR(n) n=1to4000字节,最多可以以可变长度来存储4000B,因此不需要空格来作补充 NLS(national language support , 国际语言支持)的数据类型仅可以存储由数据库 NLS 字符集定义的 Unicode 字符集
VARCHAR2(n) VARCHAR2 比 VARCHAR 更适合使用,由于兼容性的原因,所以仍然在 Oracle 数据库中保留着 VARCHAR
NVARCHAR2 NLS 的数据类型与 VARCHAR2 数据类型等价。这个数据类型最多可存储4000B
CLOB 存储大量的单字节字符数据和多字节字符数据。存储的最大容量为4G
NCLOB 存储可变长度的Unicode字符集字符数据,存储的最大容量为4G
BLOB 存储较大的二进制对象。例:图形,视频,音频等 二进制形式的长文本数据,0-65 535字节
BFILE bfile:文件定位器;指向位于服务器文件系统是二进制文件(存储一个文件路径)
LONG 存储可变长度的字符数据,最多存储2GB
RAW 存储基于字节的数据。最多存储2000个字节,使用需指定大小。raw数据类型可以建立索引
LONG RAW 存储可变长度的二进制数据。最多能存储2GB。不能索引
TINYBLOB 不超过 255 个字符的二进制字符串,0-255字节
TINYTEXT 短文本字符串,0-255字节
TEXT 长文本数据,0-65 535字节
MEDIUMBLOB 二进制形式的中等长度文本数据
MEDIUMTEXT 中等长度文本数据,0-16 777 215字节
LOGNGBLOB 二进制形式的极大文本数据,0-4 294 967 295字节
LONGTEXT 极大文本数据,0-4 294 967 295字节
DATE 存储表的日期和时间数据,使用7个字节固定长度,每个字节分别存储世纪,年,月,日,小时,分和秒;值从公元前4712年1月1日到公元9999年12月31日。 Oracle中的sysdate函数功能是返回当前的日期和时间 日期值,YYYY-MM-DD
TIMESTAMP 存储日期的年,月,日以及时间的小时,分和秒值。其中秒值精确到小数点后6位,同时包含时区信息。 Oracle中的systimestamp函数功能是返回当前日期,时间和时区
TIME 时间值或持续时间,HH:MM:SS
YEAR 年份值,YYYY
DATETIME 混合日期和时间,YYYY-MM-DD HH:MM:SS
TIMESTAMP 混合日期和时间值,时间戳 混合日期和时间值,时间戳,YYYYMMDD HHMMSS
ENUM 一个 ENUM 类型最多可以包含 65536 个元素,其中一个元素被 MySQL 保留,用来存储错误信息,这个错误值用索引 0 或者一个空字符串表示
SET 容器类型,一个 SET 类型最多可以包含 64 项元素,且不可能包含两个相同的元素

原创文章,作者:,如若转载,请注明出处:https://blog.ytso.com/tech/database/277334.html

(0)
上一篇 2022年7月27日 17:20
下一篇 2022年7月27日 17:21

相关推荐

发表回复

登录后才能评论