zoukankan      html  css  js  c++  java
  • 【本地事物】

    数据库事物的四大特性

    本篇讲述事物的特性(ACID),及基于QueryRunner编写本地事物工具类。

    1. 原子性(A)

    所谓的原子性就是说,在整个事务的操作过程中,要么全部成功,要么全部失败,不会处于中间状态。在开启事务的过程中,对数据库进行数据操作,如果在过程中发生错误,那么所有的操作都会被回滚,不会对数据库有任何影响。

    2.一致性(C)

    一致性是说事务必须使数据库从一个状态变换到另一个状态时,始终保持一致性。举个转向的例子,假设用户A有100元,用户B有200元,如果在一个事务里用户A向用户B转账50元,那么不管发生了什么,并发多也好,少也好,只要数据应用到了数据库(也就是说事务执行成功了),那么A用户账户剩50元,B账户剩250元。这就是事务的一致性。

    3. 隔离性(I)

    隔离性是指在多个用户并发的操作数据库时,比如同时操作一张表,数据库为每一个用户开启一个事务,事务与事务之间不会互相影响,在操作过程中不会感知到其他事务。

    4. 持久性(D)

    持久性是指在事务进行commit后,在事务内操作的所有数据流程都会应用到数据库中,即使发生了故障,也不会有影响。

    本地事物工具类

    使用到QueryRunner对数据库连信息进行封装。使用注解标识事务,使用ThreadLocal保存变量。叫做线程本地变量,也叫作线程本地存储,方式不一样,其实意思差不多。在一个线程内可以随时取出该变量进行操作。对service进行代理,在业务执行前开启事务,执行完毕关闭事务,执行出错回滚事务等。看代码。

    - 创建注解

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    
    /** 
     * 对事物进行管理 注解
     * @author zfl
     *
     */
    @Retention(RetentionPolicy.RUNTIME) //将注解保存在运行期间
    @Target(value=ElementType.METHOD)  //运用到方法上面
    public @interface Tran {
    }
    
    

    - 创建事物管理器

    负责开启事务,提交事务,开启事务,获取数据源等

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.sql.Connection;
    import java.sql.SQLException;
    
    import javax.sql.DataSource;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    /**
     * 对事物进行管理
     * @author zfl
     *
     */
    public class TransactionManager {
    
    	private static DataSource dataSource = new ComboPooledDataSource();
    	
    	private static ThreadLocal<Connection> conn_local = new ThreadLocal<Connection>();
    
    	private static ThreadLocal<Boolean> flag_local = new ThreadLocal<Boolean>(){  //标志是否开启过事务,默认为false
    		protected Boolean initialValue() {return false;};
    	};
    
    	private static ThreadLocal<Connection> realConn_local = new ThreadLocal<Connection>(); //存放真正的链接,用于关闭真正的链接
    	public static void start() throws SQLException{
    		flag_local.set(true);
    		final Connection connection = dataSource.getConnection();
    		connection.setAutoCommit(false);  //开启事务  //设置自动提交为false
    		realConn_local.set(connection);   //存放真正的链接
    		Connection proxyConnection = (Connection) Proxy.newProxyInstance(connection.getClass().getClassLoader(), connection.getClass().getInterfaces(), 
    					new InvocationHandler() {
    						public Object invoke(Object proxy, Method method, Object[] args)
    								throws Throwable {
    							if("close".equals(method.getName())){  //针对close方法进行处理
    								return null;  //不关闭链接
    							} else{
    								return method.invoke(connection, args);
    							}
    						}
    					});    //代理连接的close方法做了修改,不真正的关闭
    		//保存代理链接,不管理close方法,但是最后链接还是要关闭的,要不然太浪费资源
    		conn_local.set(proxyConnection);
    	}
    
    	
    	public static void commit() throws SQLException{  //提交
    		conn_local.get().commit(); 
    	}
    	
    	public static void rollback() throws SQLException{  //回滚
    		conn_local.get().rollback();
    	}
    
    
    	/*
    	 * 为了解决在事务中获取链接或者在普通获取链接中的通用性考虑时,使用以下方法解决。
    	 * 解决DataSource中的getConnection方法。判断当前是否开启了事务。
    	 * 本没有问题,但是由于QueryRunner中的链接是在执行过后会自动关闭,所以又要对Connection中的close方法进行处理
    	 */
    	public static DataSource getDataSource(){
    		
    		if(flag_local.get()){  //如果为true代表开启过事务,返回能返回相同链接的数据源
    			return (DataSource) Proxy.newProxyInstance(dataSource.getClass().getClassLoader(), dataSource.getClass().getInterfaces(), 
    					 new InvocationHandler() {
    						public Object invoke(Object proxy, Method method, Object[] args)
    								throws Throwable {
    							if("getConnection".equals(method.getName())){
    								return conn_local.get();
    							} else{
    								return method.invoke(dataSource, args); //对不是getConnection的方法,不进行处理
    							}
    						}
    					});
    		} else{
    			return dataSource;
    		}
    	}
    	
    	/**
    		释放连接
    	*/
    	public static void release(){
    		try {
    			realConn_local.get().close(); //关闭链接
    		} catch (SQLException e) {
    			e.printStackTrace();
    			throw new RuntimeException(e);
    		}
    		realConn_local.remove();
    		conn_local.remove();
    		flag_local.remove();
    	}
    }
    

    - 业务层

    使用工厂管理对象的创建,生成service对象时,来判断方法上面是否加了事务注解。加了就添加事务,否则不添加事务。

    加载对象属性配置文件

    private static Properties pro = new Properties();
    	static{
    		InputStream inStream=null;
    		try {
    			inStream = BeanFactory.class.getClassLoader().getResourceAsStream("instance.properties");
    			pro.load(inStream);
    		} catch (Exception e) {
    			e.printStackTrace();
    			throw new RuntimeException(e);
    		}
    	}
    

    创建service代理对象

    //获取service
    	public static <T extends Service> T getService(Class<T> clazz){
    		String simpleName = clazz.getSimpleName();
    		String className = pro.getProperty(simpleName);
    		final T service;
    		try {
    			service = (T) Class.forName(className).newInstance();
    		} catch (Exception e) {
    			e.printStackTrace();
    			throw new RuntimeException(e);
    		}
    		//添加事务  生成service代理
    		@SuppressWarnings("all")
    		T proxyService = (T) Proxy.newProxyInstance(service.getClass().getClassLoader(), service.getClass().getInterfaces(), 
    				new InvocationHandler() {
    					public Object invoke(Object proxy, Method method, Object[] args)
    							throws Throwable {
    						if(method.isAnnotationPresent(Tran.class)){ //如果有注解,添加事务
    							Object obj = null;
    							try {
    								TransactionManager.start();
    								obj = method.invoke(service, args);
    								TransactionManager.commit();
    							} catch(InvocationTargetException e){  //底层代码抛出的异常
    								TransactionManager.rollback();
    								throw new RuntimeException(e.getTargetException());  //转换成底层异常抛出
    							} catch (Exception e) {
    								TransactionManager.rollback();
    								throw new RuntimeException(e);
    							} finally{
    								TransactionManager.release();
    							}
    							return obj;  //返回数据
    						} else{
    							return method.invoke(service, args); //普通方法,则不添加事务
    						}
    							
    					}
    				});
    		return proxyService;
    	}
    

    - 实际应用

    /**
    	 * 查询所有的菜品
    	 * @throws SQLException 
    	 */
    public void add(Food food) throws SQLException {
    		QueryRunner qr = new QueryRunner(TransactionManager.getDataSource()); //获取datasouce数据源
    		String sql = "insert into food(foodName,foodType_id,price,mprice,description,img)" +
    				" values(?,?,?,?,?,?)";
    		Object[]params ={food.getFoodName(),food.getFoodType().getId(),food.getPrice(),food.getMprice(),food.getDescription(),food.getImg()};
    		qr.update(sql, params);
    	}
    
    充满鲜花的世界到底在哪里
  • 相关阅读:
    【计算机网络】宽带、基带传输
    【操作系统】多道程序的理解
    【操作系统】操作系统的理解
    NLP学习常用的网页链接
    linux下常用FTP命令 1. 连接ftp服务器[转]
    shell运行java/Jar 脚本
    jsp验证码
    用javascript实现的验证码
    eclipse设置高亮显示的颜色
    oracle 导出
  • 原文地址:https://www.cnblogs.com/aliases/p/10500128.html
Copyright © 2011-2022 走看看