1、先插入再查询
BEGIN;
INSERT INTO video (CREATE_TIME, UPDATE_TIME, VERSION, VID, VIDEO_NAME, SPEED, RATE, HORIZONTAL, VERTICAL, SIZE, LIB_ID, ALARM_THRESHOLD, ONLINE, STATUS, REMARK)
VALUES(NOW(),NOW(),0,'vidtest','test.mp4',1,1,1,1,'1',1,0.1,20,20,'');
SELECT * FROM video WHERE VID = 'vidtest';
ROLLBACK;
在事务内部的SELECT语句可以查询到数据,但是回滚后,不能查询到数据
。
2、先删除再查询
BEGIN;
DELETE FROM video WHERE VID = 'vidtest';
SELECT * FROM video WHERE VID = 'vidtest';
ROLLBACK;
在事务内部的查询不能查询到数据,但是回滚后,可以查询到数据
。
二、原因说明:
在同一个事务中,数据需要保证一致性,也就是说,在当前的事务中,删除了就是看不到了,插入了就是多了一条数据,但这些操作都是在当前事务内部的。对于外界是隔离的。如果我们开启事务插入一条数据,还没有提交,这时新开一个事务,去查询数据,是发现不了未提交数据的。
三,基于mybatis进行测试
1、先插入再更新
//这两个操作是在同一个方法中 方法是通过Transactional注解实现事务的
@Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRED)
//插入一条数据
int i = videoDao.insert(videoPo);
//更新一条数据
int j = videoDao.updateVid(videoPo.getId(),vid);
控制台打印的sql语句
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5cfedd40]
JDBC Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@6624f1eb]]] will be managed by Spring
==> Preparing: INSERT INTO video (CREATE_TIME, UPDATE_TIME, VERSION, VID, VIDEO_NAME, SPEED, RATE, HORIZONTAL, VERTICAL, SIZE, LIB_ID, ALARM_THRESHOLD, ONLINE, STATUS, REMARK) VALUES (now(), now(), 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
==> Parameters: vid60c7d86a-111b-45ec-b25e-2100b6f9a588(String), 1.mp4(String), 1(Integer), 1(Integer), 50(Integer), 20(Integer), 30*30(String), 1(Long), 0.1000(BigDecimal), 10(Integer), 20(Integer), null
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5cfedd40]
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5cfedd40] from current transaction
==> Preparing: UPDATE video SET VID = ? WHERE ID = ?
==> Parameters: vid14(String), 14(Long)
<== Updates: 1
mybatis会将两条执行语句在同一个sqlSession中执行。
2、先删除再查询
//这两个操作是在同一个方法中 方法是通过Transactional注解实现事务的
@Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRED)
//删除一条数据
int i = videoDao.deleteVedioByVid(vid);
//查询被删除的数据
VideoPo videoPo = videoDao.getVideoByVid(vid);
控制台打印的sql语句
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3b1ca134]
JDBC Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@6624f1eb]]] will be managed by Spring
==> Preparing: DELETE FROM video WHERE VID = ?
==> Parameters: vidtest(String)
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3b1ca134]
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@55d43e72]
JDBC Connection [ProxyConnection[PooledConnection[com.mysql.jdbc.JDBC4Connection@1dfa23fe]]] will be managed by Spring
==> Preparing: SELECT * FROM video WHERE VID = ?
==> Parameters: vidtest(String)
<== Columns: ID, CREATE_TIME, UPDATE_TIME, VERSION, VID, VIDEO_NAME, SPEED, RATE, HORIZONTAL, VERTICAL, SIZE, LIB_ID, ALARM_THRESHOLD, ONLINE, STATUS, REMARK
<== Row: 12, 2018-10-19 15:05:15.0, 2018-10-19 15:05:15.0, 0, vidtest, test.mp4, 1, 1, 1, 1, 1, 1, 0.1000, 20, 20,
<== Total: 1
mybatis创建了两个sqlSession。
后来我又测试了先删除再更新的场景,发现更新失败。
结论:
mybatis会再插入或删除后,进行更新操作时使用同一个sqlSession,查询时新建sqlSession。
一,现象展示1、先插入再查询BEGIN;INSERT INTO video (CREATE_TIME, UPDATE_TIME, VERSION, VID, VIDEO_NAME, SPEED, RATE, HORIZONTAL, VERTICAL, SIZE, LIB_ID, ALARM_THRESHOLD, ONLINE, STATUS, REMARK) VALUES(NOW()...
数据库和操作系统一样,是一个多用户使用的共享资源。当多个用户并发地存取数据 时,在数据库中就会产生多个
事务
同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性。加锁是实现数据库并 发控制的一个非常重要的技术。在实际应用中经常会遇到的与锁相关的异常情况,当两个
事务
需要一组有冲突的锁,而不能将
事务
继续下去的话,就会出现死锁,严 重影响应用的正常执行。
在数据库中有两种基本的锁类型:排它锁(Exclusive Locks,即X锁)和共享锁(Share Locks,即S锁)。当数据对象被加上排它锁时,其他的
事务
不能对它读取和修改。加了共享锁的数据对象可以被其他
分别创建增加、
删除
、更新的触发器(Trigger)来达到两张表之间数据同步的目的。 1:数据同步增加: 如有两张表——A表和B表,创建触发器使当A表
插入
数据后B表也同步
插入
数据。其中B表
插入
数据的字段需要同A表中的字段相对应。 代码如下: CREATE TRIGGER 触发器名称 ON A表 AFTER INSERT AS BEGIN INSERT INTO B表(B表字段1,B表字段2,B表字段3) SELECT A表字段1,A表字段2,A表字段3 FROM INSERTED END 2.数据同步
删除
: 如有两张表——A表和B表,创建触发器使当A表
删除
数据后B表也同步
删除
数据。其中B表与
Object res = this.selectById(obj.getId());
res: null;
线上的一个坑,做了读写分离以后,有一个场景因为想方法复用,只传入一个ID就好,直接去库里查出一个对象再做后续处理,结果查不出来,
事务
隔离级别各种也都排查了,最后发现是读写分离的问题,所以换个思路去实现吧。
补充知识:
MySQL
INSERT
插入
条件判断:如果不存在则
插入
我们经常需要进行sql的批量
插入
,要求:该条记录不存在则
插入
,存在则不
插入
。如果使用一条IN
def fieldbyname(self, namestr):
self.env.cr.execute("SELECT "+namestr+" FROM dzjl_muoban WHERE id=%s",
[self.id])
c_id = self.env.cr.fetchone()
# print(c_id)
# print(namestr)
return c_id.
Mysql
事务
操作失败如何解决
事务
的原子性 :
事务
是数据库的逻辑工作单位,
事务
中包含的各操作要么都做,要么都不做 。
要实现
事务
的原子性,单单靠一条commit或是rollback命令还是不行的,因为例如commit命令它只是将一个
事务
中执行成功的DML语句提交给数据库里。如果要实现
事务
的原子性,则就需要commit和rollback命令配合上程序上的一个业务逻辑才能可以,具体业务逻辑代码如下示例代码:
程序中打开了
事务
进行
插入
,但是没有commit,表中的数据已经存在,就是回滚也不能
删除
插入
的数据
本表的Storage Engine 为myisam
首
先
要介绍几个概念:
1. MVCC(Multi-Vers
ion
Concurren):多版本并发控制,是
MySQL
的
事务
型存储引擎如InnoDB。
2. trx_id与roll_pointer:
MySQL
会给每个表加2个字段,trx_id是
事务
字段id,roll_pointer为回滚字段。
3.
事务
会有一个ID,只有更新(
删除
和
插入
是特性的更新)会生成
事务
ID。
4. 在
事务
中进行更新,会把原记录放到undo回滚日志里面,然后再
插入
一条新记录,这个新记录的roll_pointer指向刚刚放到undo回滚日志里面的那个记录。
5. 在
事务
中使用select不会生
近来稍有时间研究了下
MYSQL
中的
事务
操作,在很多场合下很是适用,譬如在注册的时候需要初始化很多张关联表的时候,问答回复的时候需要至少同时操作两张表,这些都会在某些时候只能成功更新一张表,而另外的SQL语句出现错误,正常的操作会导致初始化了一张表 ,其他的都木有能初始化,这个时候就会导致用户表里的用户信息已经执行
插入
,导致提示注册失败,但是用户已经注册了部分信息,这个时候需要程序员去数据库
删除
相应的数据是一个比较不好的事情。 因此这边考虑使用
事务
,
事务
可以进行模拟SQL操作,当所有的SQL都操作成功的时候才进行SQL操作,只要有一个操作失败就回滚当前
事务
的所有SQL操作,避免出现上面描述中出现
因此,正确的原子操作是真正被执行过的。是物理执行。
在当前
事务
中确实能看到
插入
的记录。最后只不过
删除
了。但是AUTO_INCREMENT不会应
删除
而改变值。
1、为什么auto_increament没有回滚?
因为innodb的auto_increament的计数器记录的当前值是保存在存内存中的,并不是存在于磁盘上,当
mysql
server处于运行的时候,这个计数值只会随着insert改增长,不会随着delete而减少。而当
mysql
server启动时,当我们需要去
查询
auto_increment计数值时,
mysql
便会自动执行:SELECT MAX(id) FROM 表名 FOR UPD
#
查询
是否存在记录
mycursor.execute("SELECT * FROM customers WHERE email = %s", ("john@example.com",))
result = mycursor.fetchone()
if result:
# 更新记录
sql = "UPDATE customers SET firstname = %s, lastname = %s WHERE email = %s"
mycursor.execute(sql, ("John", "Doe", "john@example.com"))
else:
#
插入
新纪录
sql = "REPLACE INTO customers (firstname, lastname, email) VALUES (%s, %s, %s)"
mycursor.execute(sql, data)
# 提交
事务
mydb.commit()
# 输出结果
print(mycursor.rowcount, "record(s) affected")
注意,REPLACE INTO语句只适用于有主键或唯一索引的表,因为它会根据主键或唯一索引来判断记录是否存在。如果表没有主键或唯一索引,可以考虑使用INSERT ... ON DUPLICATE KEY UPDATE语句。