美文网首页
Spirng oracle 中for update悲观锁及事务

Spirng oracle 中for update悲观锁及事务

作者: TryCatch菌 | 来源:发表于2018-10-05 01:07 被阅读0次

    在实际工作中,经常为用到对某些敏感数据操作进行加锁,常用的锁有悲观锁和乐观锁,这里备忘个数据库层面的悲观锁
    for update语句,在数据库中执行select .....for update ,大家会发现会对数据库中的表或某些行数据进行锁表,在mysql中,如果查询条件带有主键,会锁行数据,如果没有,会锁表,在oracle中是对查询范围内的行数据进行锁定。
    测试代码

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.lovo.mapper.UserMapper">
    
       <resultMap type="UserBean" id="user">
            <id property="id" column="id" />
            <result property="name" column="u_name" javaType="String"/>
            <result property="sex" column="u_sex" javaType="String"/>
            <result property="age" column="u_age" javaType="java.lang.Long"/>
        </resultMap>
    
        <insert id="batchAddUser" parameterType="java.util.List">
            insert into t_user (u_id, u_name,u_sex,u_age)
            (select
            seq_t_user.nextval,temp.* from (
            <foreach collection="list" item="user" separator="union all">
                select
                #{user.name,jdbcType = VARCHAR} u_name,
                #{user.sex ,jdbcType = VARCHAR} u_sex,
                #{user.age,jdbcType = NUMERIC} u_age
                from dual
            </foreach>
            ) temp
            )
    
        </insert>
        
        <select id ="selectUserByName" resultMap="user"  parameterType="UserBean">
            select t.u_id,t.u_name,t.u_sex,t.u_age from t_user t where t.u_name = #{name} for update
            
        </select>
        
        <update id="updateUserByName"  parameterType="java.util.Map">
            update t_user t set t.u_age = #{age} where t.u_name =#{name}
        </update>
    
    </mapper>
    
    
        @Override
        public List<UserBean> selectUserByName(String name) {
            // 此方法加悲观锁 for update
            return userMapper.selectUserByName(name);
        }
    
        @Override
        public void updateUserByName(Map<String, String> date) {
            userMapper.updateUserByName(date);
            
        }
    
    @Service("userService")
    public class UserServiceImpl implements IUserService {
    
        @Autowired
        @Qualifier(value="userDao")
        private UserDao userDao;
        
        @Override
        public void updateUserByName(Map<String, String> date) {
            //悲观锁查询,模拟先查询,锁表然后再更新
            List<UserBean> userList = userDao.selectUserByName(date.get("name"));
            System.out.println(userList);
            userDao.updateUserByName(date);
    
        }
    
    }
    
    @RunWith(SpringJUnit4ClassRunner.class) 
    @ContextConfiguration(locations = "classpath*:applicationContext.xml")  
    public class TransactionTest {
    
        @Autowired
        @Qualifier(value="userService")
        private IUserService userSerive;
        
        @Test
        public void test()
        {
            Map<String, String> date = new HashMap<String, String>();
            date.put("name", "张三");
            date.put("age", "66");
            
            userSerive.updateUserByName(date);
        }
    }
    

    这里我们模拟一下,在代码中打一个断点

    image.png

    代码运行完查询语句后会对数据进行锁定

    /*查看被锁的表 */
    select b.owner,b.object_name,a.session_id,a.locked_mode from v$locked_object a,dba_objects b where b.object_id = a.object_id;
    
    image.png

    发现表有锁定,手工对数据进行修改,发现被查询出的数据锁定,未被查询的数据未锁定,为行级锁

    image.png

    通过断点后锁解除,所以说for update的锁的作用范围为一个会话事物周期内,当执行commit或rollback释放锁定数据。

    同样此种用法也适用用编程式事务

    首先配置下编程事物

        <!-- 配置transactionTemplate 需指定数据源<property name="transactionManager" ref="txManage"></property> -->  
        <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">  
            <property name="transactionManager" ref="txManage"></property>  
            <!--定义事务隔离级别,-1表示使用数据库默认级别-->  
            <property name="readOnly" value="false"></property>  
            <property name="isolationLevelName" value="ISOLATION_DEFAULT"></property>  
            <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"></property>  
        </bean>  
    
    @Repository("userDao")
    public class UserDaoImpl implements UserDao {
        
        @Resource
        private UserMapper userMapper;
        
        @Resource  
        private TransactionTemplate transactionTemplate;  
        
        @Override
        public void updateUserByNameWithTransaction(final Map<String, String> date) {
            //测试编程事物
            transactionTemplate.execute(new TransactionCallbackWithoutResult() {
                
                @Override
                protected void doInTransactionWithoutResult(TransactionStatus status) {
                    try 
                    {
                        System.out.println(userMapper.selectUserByName(date.get("name")));
                        userMapper.updateUserByName(date);
                    } catch (Exception e) 
                    {
                        //异常事务回滚  
                       status.setRollbackOnly();  
                       e.printStackTrace();    
                    }
                    
                }
            });
            userMapper.updateUserByName(date);
        }
    
    }
    
    
    image.png

    在代码中打上断点,测试发现在54行断点数据并未commit,在64断点行数据已经commit
    有一点需要注意:配置式的事务后,感觉方法的注解事物和编程事物不生效了
    初步的预计是,配置的事务管理拦截器优先级较高,使注解式的事务和编程事物没有得到处理。暂时记录这个问题,以后研究 。

    相关文章

      网友评论

          本文标题:Spirng oracle 中for update悲观锁及事务

          本文链接:https://www.haomeiwen.com/subject/kvpvoftx.html