zoukankan      html  css  js  c++  java
  • 数据库事务总结

    Java的数据库开发使用JDBC,它的开发步骤有:1.注册驱动;2.获得链接;3.执行sql;4.释放资源。

    数据库的事务,是指逻辑上不可分割的一组操作,要么全成功要么全失败。

    MYSQL的事务开启方式

    start transaction; --开启事务
    commit; --提交事务
    rollback; --事务回滚
    

    JDBC设计有事务保存点,可以使回滚操作到这个保存点。

    Savepoint savePoint = conn.setSavepoint();
    

    事务所具有的特性:1.原子性;2.一致性;3.隔离性;4.持久性。

      1.强调事务不可分割;

      2.事务执行的时候数据完整性保持一致;

      3.一个事务的执行,不应该受到另外一个事务的影响;

      4.事务执行完毕,数据成为持久化的存在。

    由于事务的隔离性,数据库事务产生三种读的问题:脏读,不可重复读,幻读

      脏读:一个事务读到了另一个事务未提交的数据。

          当事务隔离级别为read uncommitted的时候,一个事务执行到一半并未提交,另一个事务读到了数据,前一个事物回滚,这样第二个事务读到的就是不真实的数据。

      不可重复读:一个事务读取到了另一个事务已经提交的数据(一般执行的事update操作)

          当事务第一次读取的时候,此时它需要进行两次查询,同时另一个事务的更新处于两次查询之间;另一个事务更新完后再查询的时候数据是不相同的。此时他们的隔离级别都是read committed。

      幻读:一个事务读取到另外一个事务已经提交的数据(一般执行的事insert操作)

          MYSQL数据库的幻读是有概率性的,不好演示。

    数据库的四种隔离级别

    read uncommitted  未提交读,那么脏读、不可重复读、幻读都是有可能发生的
    read committed  已提交读,可避免脏读,不能避免不可重复读和幻读
    repeatable read  重复读,避免脏读、不可重复读,但是幻读还是有可能产生
    serializable  串行读,避免所有异常读的产生
    

      他们的安全性是递增的,效率性是递减的。

      MYSQL默认的是repeatable read;Orical中的是read committed。

    通过命令查看事务的隔离级别:
    * select @@tx_isolation;
    设置数据库隔离级别:
    * set session transaction isolation level 隔离级别;
    

    丢失更新:两个用户(或以上)对同一个数据对象操作引起的数据丢失。

        解决方案:1.悲观锁,假设丢失更新一定存在;sql后面加上for update;这是数据库的一种机制。

             2.乐观锁,假设丢失更新不一定发生。update时候存在版本,更新时候按版本号进行更新。

    数据库连接池:由于数据库的链接对象的创建是耗时的,如果使用预先创建好的链接对象并复用,则可节省服务器的内存并提高效率。

      连接池的链接的close方法设计的是把链接归还到连接池中,而不是销毁。

    开源数据库连接池:DBCP,C3P0,tomcat内置连接池(jndi技术)。

    ThreadLocal:一个线程包,可以把一些对象存放在被线程中,作为本线程的全局对象。它的原理是维护一个map集合,放入的k,v对象是本线程对象和需要保存的全局对象。

    class MyThreadLocal<T> {
    	private Map<Thread,T> map = new HashMap<Thread,T>();
    	public void set(T value) {
    		map.put(Thread.currentThread(), value);
    	}
    	
    	public void remove() {
    		map.remove(Thread.currentThread());
    	}
    	
    	public T get() {
    		return map.get(Thread.currentThread());
    	}
    }
    

    service层与事务:DAO层执行的是对数据源的单个操作,service层比较适合作为事务的存放层。在service层中调用DAO层的多个方法,并且使用同一个链接对象。

    实例:使用c3p0实现一个转账事务。service层-dao层-model层

       模型javabean

    package JDBCConectionPools;
    
    public class account {
    	private int id;
    	private String name;
    	private float money;
    	public account(){
    		
    	}
    	public account(int id, String name, float money) {
    		super();
    		this.id = id;
    		this.name = name;
    		this.money = money;
    	}
    	public int getId() {
    		return id;
    	}
    	public void setId(int id) {
    		this.id = id;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public float getMoney() {
    		return money;
    	}
    	public void setMoney(float money) {
    		this.money = money;
    	}
    	
    }
    

      dao层

    package JDBCConectionPools;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.ArrayList;
    import java.util.List;
    
    public class accountDAO {
    	public List<account> selectAllAccount(Connection conn){
    		String sql = "select * from account";
            Statement statement;
            ResultSet rs = null;
            List<account> list =new ArrayList<account>();
    		try {
    			statement = conn.createStatement();
    			rs = statement.executeQuery(sql);
    			while(rs.next()){
    				int id = rs.getInt("id");
    				String name = rs.getString("name");
    				float money = rs.getFloat("money");
    				account a = new account(id,name,money);
    				list.add(a);
    				System.out.println("id:"+id+" name:"+name+" money:"+money);
    			}
    		} catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}  
            return list;
    	}
    	//转出账户
    	public void decAccount(Connection conn,String name,float number){
    		String sql = "update account set money = money -? where name = ?";
    		PreparedStatement stmt = null;
    		ResultSet rs = null;
    		try {
    			stmt = conn.prepareStatement(sql);
    			stmt.setFloat(1, number);
    			stmt.setString(2, name);
    			stmt.executeUpdate();
    		} catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		
    	}
    	//转入账户
    	public void incAccount(Connection conn,String name,float number){
    		String sql = "update account set money = money + ? where name = ?";
    		PreparedStatement stmt = null;
    		ResultSet rs = null;
    		try {
    			stmt = conn.prepareStatement(sql);
    			stmt.setFloat(1, number);
    			stmt.setString(2, name);
    			stmt.executeUpdate();
    		} catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		
    	}
    	//单个搜索
    	public account selectUser(Connection conn,String name){
    		String sql = "select * from account where name = ?";
            PreparedStatement statement;
            ResultSet rs = null;
            account a = null;
            try {
    			statement = conn.prepareStatement(sql);
    			statement.setString(1, name);
    			rs = statement.executeQuery();
    			if(rs.next())
    			{
    				a = new account(rs.getInt("id"),rs.getString("name"),rs.getFloat("money"));
    			}
    		} catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		return a;
    	}
    }
    

     service层

    package JDBCConectionPools;
    
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.util.List;
    
    import org.junit.Test;
    
    
    public class accountSerivce {
    	//搜索事务
    	public List<account> selectAccountService(){
    		Connection conn = JDBCUtils.getConnection();
    		accountDAO dao = new accountDAO();
    
    		List<account> list = null;
    		try {
    			JDBCUtils.beginTransaction();
    			list = dao.selectAllAccount(conn);
    			JDBCUtils.commitTransaction();
    		} catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    			try {
    				JDBCUtils.rollbackTransaction();
    			} catch (SQLException e1) {
    				// TODO Auto-generated catch block
    				e1.printStackTrace();
    			}
    		}
    		return list;
    	}
    	//赚钱服务
    	public void transferAccounts(String from,String to,float number){
    		Connection conn = JDBCUtils.getConnection();
    		accountDAO dao = new accountDAO();
    		
    		try {
    			JDBCUtils.beginTransaction();
    			dao.decAccount(conn, from, number);
    			dao.incAccount(conn, to, number);
    			account account = dao.selectUser(conn, from);
    			if(account != null)
    				if(account.getMoney() < 0)
    				{
    					System.err.println("余额不足!无法转账!");
    					JDBCUtils.rollbackTransaction();
    				}
    				else {
    					JDBCUtils.commitTransaction();
    				}
    			else
    				JDBCUtils.rollbackTransaction();
    		} catch (SQLException e) {
    			e.printStackTrace();
    			try {
    				JDBCUtils.rollbackTransaction();
    			} catch (SQLException e1) {
    				e1.printStackTrace();
    			}
    		}
    	}
    	@Test
    	public void test(){
    		accountSerivce service = new accountSerivce();
    		service.transferAccounts("美美", "冠希", 10000);
    		List<account> list = service.selectAccountService();
    		for(int i =0;i<list.size();i++)
    			System.out.println(list.get(i).getId()+" "+list.get(i).getName()+" "+list.get(i).getMoney());
    	}
    	
    }
    

     工具类

    package JDBCConectionPools;
    
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    public class JDBCUtils {
    	private static ComboPooledDataSource cpds = null;
    	private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
    	static {
    		// 这里有个优点,写好配置文件,想换数据库,简单
    		cpds = new ComboPooledDataSource("mysql");// 这是mysql数据库
    	}
        public static Connection getConnection(){  
    		Connection conn = tl.get();
    		try {
    			if (conn == null) {
    				conn = cpds.getConnection();
    				tl.set(conn);
    			}
    		} catch (SQLException e1) {
    			// TODO Auto-generated catch block
    			e1.printStackTrace();
    		}
    		return conn;
           
        }
        //事务有关
    	public static void beginTransaction() throws SQLException{
    		Connection conn = tl.get();
    		if(conn == null){
    			conn = cpds.getConnection();
    			tl.set(conn);
    		}
    		conn.setAutoCommit(false);
    	}
    	public static void commitTransaction() throws SQLException{
    		Connection conn = tl.get();
    		if(conn == null){
    			conn = cpds.getConnection();
    			tl.set(conn);
    		}
    		conn.commit();
    		tl.remove();
    	}
    	
    	public static void rollbackTransaction() throws SQLException{
    		Connection conn = tl.get();
    		if(conn == null){
    			conn = cpds.getConnection();
    			tl.set(conn);
    		}
    		conn.rollback();
    		tl.remove();
    	}
    	// 释放资源的方法
    	public static void release(ResultSet rs, Statement stmt, Connection conn) {
    		if (rs != null) {
    			try {
    				rs.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    			rs = null;
    		}
    		if (stmt != null) {
    			try {
    				stmt.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    			stmt = null;
    		}
    		if (conn != null) {
    			try {
    				conn.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    			conn = null;
    		}
    	}
    
    	public static void release(ResultSet rs,Statement stmt) {
    		if (rs != null) {
    			try {
    				rs.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    			rs = null;
    		}
    		if (stmt != null) {
    			try {
    				stmt.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    			stmt = null;
    		}
    	}
    
    	public static void release(Statement stmt, Connection conn) {
    		if (stmt != null) {
    			try {
    				stmt.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    			stmt = null;
    		}
    		if (conn != null) {
    			try {
    				conn.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    			conn = null;
    		}
    	}
    		
    }
    

      配置文件c3p0-config.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <c3p0-config>  
        <!-- This is default config! -->  
        <default-config>  
            <property name="initialPoolSize">10</property>  
            <property name="maxIdleTime">30</property>  
            <property name="maxPoolSize">100</property>  
            <property name="minPoolSize">10</property>  
            <property name="maxStatements">200</property>  
        </default-config>  
      
        <!-- This is my config for mysql-->  
        <named-config name="mysql">  
            <property name="driverClass">com.mysql.jdbc.Driver</property>  
            <property name="jdbcUrl">jdbc:mysql://localhost:3306/MyData?characterEncoding=utf-8</property>  
            <property name="user">root</property>  
            <property name="password">qwert123</property>  
            <property name="initialPoolSize">10</property>  
            <property name="maxIdleTime">30</property>  
            <property name="maxPoolSize">100</property>  
            <property name="minPoolSize">10</property>  
            <property name="maxStatements">200</property>  
        </named-config>  
    </c3p0-config>

      遇到两个异常:1.查询适合使用statement不能设置?参数;2.使用中文查询的时候sql命令行可查,jdbc查不到,设置utf-8之后查到了。

      设计方式总结:1.工具包提供连接对象的创建与回收,并且使用ThreadLocal保存链接对象,在事务commit之后从线程中移除链接对象。

             2.service层中只执行dao层的操作,不进行jdbc编程。

             3.dao层不用拿到链接对象,用参数链接对象执行sql。

    •  数据库安全模式下无法delete

        输入sql

    SET SQL_SAFE_UPDATES = 0;
    

      

  • 相关阅读:
    docker私有仓库搭建及使用
    服务器ip迁移纪要
    Windows 下QT程序发布
    Prometheus监控软件部署方法
    android的listview控件,加了行内按钮事件导致行点击失效的问题
    惊奇!Android studio内部在调用Eclipse
    关于Android Stuido2.3和Eclipse4.4
    XCODE9.1的一些新问题
    osx12.6设置全屏
    IEEE754浮点数与字节数互转工具
  • 原文地址:https://www.cnblogs.com/chentingk/p/5864194.html
Copyright © 2011-2022 走看看