连接池+事务

Posted myjavastudy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了连接池+事务相关的知识,希望对你有一定的参考价值。

 事务的四个特性:

  1、原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。

   2、一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。

   3、隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。

   4、持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。

 

 事务的4种隔离级别(mysql   默认为读未提交)

   1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据

   2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。

     3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

事务隔离级别 脏读 不可重复读 幻读
读未提交(read-uncommitted)
不可重复读(read-committed)
可重复读(repeatable-read)
串行化(serializable)

 

 

 

 

   丢失更新

  定义:事务T1读取了数据,并执行了一些操作,然后更新数据。事务T2也做相同的事,则T1和T2更新数据时可能会覆盖对方的更新,从而引起错误。

                          

                                                        《1》  共享锁             lock in share mode    (可能会产生死锁)

                            1    悲观锁“          《2》  排它锁  (我读的时候你不能读)     select  *  from  user    for    update                                 关键词    for   update  

  丢失跟新:

                            2     乐观锁           加时间戳 每次执行操作的时候去和时间戳比较

 

  乐观锁  用在丢失更新频率低的情况下       悲观锁用在丢失更新频率高的情况下。

 

连接池:

1  自定义连接池:

  创建连接池

package com.rl.lianjiechi;

import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.logging.Logger;

import javax.sql.DataSource;

import com.rl.JDBCUtil;



public class MyPool implements DataSource{
	int size;
	LinkedList<Connection> linkedList=new LinkedList<>();
	

	public MyPool(int size) {
		super();
		this.size = size;
		for(int i=0;i<size;i++){
			try {
				linkedList.add(JDBCUtil.getConnection());
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	@Override
	public PrintWriter getLogWriter() throws SQLException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void setLogWriter(PrintWriter out) throws SQLException {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void setLoginTimeout(int seconds) throws SQLException {
		// TODO Auto-generated method stub
		
	}

	@Override
	public int getLoginTimeout() throws SQLException {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public Logger getParentLogger() throws SQLFeatureNotSupportedException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public <T> T unwrap(Class<T> iface) throws SQLException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean isWrapperFor(Class<?> iface) throws SQLException {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public Connection getConnection() throws SQLException {
		Object obj=null;
		if(linkedList.size()>0){
			Connection conn = linkedList.removeFirst();  
			obj = Proxy.newProxyInstance(conn.getClass().getClassLoader(), new Class[]{com.mysql.jdbc.Connection.class}, new InvocationHandler() {
				
				@Override
				public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws Throwable {
					// TODO Auto-generated method stub
					if(!method.getName().equals("close")){
						return method.invoke(conn, args);
					}
					else{
						linkedList.add(conn);
						return null;
					}
			
				
				}});
			return (Connection) obj;
		
		}
		return null;
	}
		

		

	@Override
	public Connection getConnection(String username, String password) throws SQLException {
		// TODO Auto-generated method stub
		return null;
	}

}

  测试类:

package com.rl.lianjiechi;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import com.domain.login.entity.User;
import com.mysql.jdbc.PreparedStatement;

public class Test {
	public static void main(String[] args) throws SQLException {
		Connection conn=null;
		PreparedStatement pst=null;
		MyPool myPool=new MyPool(10);
		conn=myPool.getConnection();
		String sql="select * from user";
		pst=(PreparedStatement) conn.prepareStatement(sql);
	
		boolean f=pst.execute();
		System.out.println(f);
	}
}

  创建初始化时的连接所用的工具类:

package com.rl;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public final class JDBCUtil {
    private static String driver="com.mysql.jdbc.Driver";
    private static String url="jdbc:mysql://localhost:3306/mytest";
    private static String user="root";
    private static String password="123456";
     
    private JDBCUtil(){}
     
    static {
        /**
         * 驱动注册
         */
        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            throw new ExceptionInInitializerError(e);
        }
         
    }
     
    /**
     * 获取 Connetion
     * @return
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException{
        return DriverManager.getConnection(url, user, password);
    }
     
    /**
     * 释放资源
     * @param conn
     * @param st
     * @param rs
     */
    public static void colseResource(Connection conn,Statement st,ResultSet rs) {
        closeResultSet(rs);
        closeStatement(st);
        closeConnection(conn);
    }
     
    /**
     * 释放连接 Connection
     * @param conn
     */
    public static void closeConnection(Connection conn) {
        if(conn !=null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        //等待垃圾回收
        conn = null;
    }
     
    /**
     * 释放语句执行者 Statement
     * @param st
     */
    public static void closeStatement(Statement st) {
        if(st !=null) {
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        //等待垃圾回收
        st = null;
    }
     
    /**
     * 释放结果集 ResultSet
     * @param rs
     */
    public static void closeResultSet(ResultSet rs) {
        if(rs !=null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        //等待垃圾回收
        rs = null;
    }
}

  

2常见的几种数据源

  传递给JDBC驱动的用于连接数据库的用户名、密码、URL以及驱动类名。
  DBCP c3p0 Druid
用户名 username user username
密码 password password password
URL url jdbcUrl jdbcUrl
驱动类名 driverClassName driverClass driverClassName
注:在Druid连接池的配置中,driverClassName可配可不配,如果不配置会根据url自动识别dbType(数据库类型),然后选择相应的driverClassName。
  关键配置:为了发挥数据库连接池的作用,在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数来设定的。无论这些数 据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池 请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。
  最小连接数:
  是数据库一直保持的数据库连接数,所以如果应用程序对数据库连接的使用量不大,将有大量的数据库资源被浪费。
  初始化连接数:
  连接池启动时创建的初始化数据库连接数量。
  最大连接数:
  是连接池能申请的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求被加入到等待队列中。
  最大等待时间:
  当没有可用连接时,连接池等待连接被归还的最大时间,超过时间则抛出异常,可设置参数为0或者负数使得无限等待(根据不同连接池配置)。
  DBCP c3p0 Druid
最小连接数 minIdle(0) minPoolSize(3) minIdle(0)
初始化连接数 initialSize(0) initialPoolSize(3) initialSize(0)
最大连接数 maxTotal(8) maxPoolSize(15) maxActive(8)
最大等待时间 maxWaitMillis(毫秒) maxIdleTime(0秒) maxWait(毫秒)
 

 c3p0配置如下:  

该文件应命名为c3p0-config.xml放在类路径下。

 

  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <c3p0-config>    
  3.     <!-- This is default config! -->    
  4.     <default-config>    
  5.         <property name="initialPoolSize">10</property>    
  6.         <property name="maxIdleTime">30</property>    
  7.         <property name="maxPoolSize">100</property>    
  8.         <property name="minPoolSize">10</property>    
  9.         <property name="maxStatements">200</property>    
  10.     </default-config>    
  11.     
  12.     <!-- This is my config for mysql-->    
  13.     <named-config name="mysql">    
  14.         <property name="driverClass">com.mysql.jdbc.Driver</property>    
  15.         <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=UTF8</property>    
  16.         <property name="user">root</property>    
  17.         <property name="password"></property>    
  18.          <!-- 初始化连接池中的连接数,取值应在minPoolSize与maxPoolSize之间,默认为3-->  
  19.         <property name="initialPoolSize">10</property>  
  20.         <!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。默认值: 0 -->    
  21.         <property name="maxIdleTime">30</property>    
  22.         <!--连接池中保留的最大连接数。默认值: 15 -->  
  23.         <property name="maxPoolSize">100</property>   
  24.         <!-- 连接池中保留的最小连接数,默认为:3-->   
  25.         <property name="minPoolSize">10</property>   
  26.         <!--c3p0全局的PreparedStatements缓存的大小。如果maxStatements与maxStatementsPerConnection均为0,则缓存不生效,只要有一个不为0,则语句的缓存就能生效。如果默认值: 0-->   
  27.         <property name="maxStatements">200</property>    
  28.         <!-- 当连接池连接耗尽时,客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限期等待。单位毫秒。默认: 0 -->     
  29.         <property name="checkoutTimeout" value="3000"/>   
  30.         <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。默认值: 3 -->     
  31.         <property name="acquireIncrement" value="2"/>   
  32.         <!--定义在从数据库获取新连接失败后重复尝试的次数。默认值: 30 ;小于等于0表示无限次-->     
  33.         <property name="acquireRetryAttempts" value="0"/>    
  34.         <!--重新尝试的时间间隔,默认为:1000毫秒-->     
  35.         <property name="acquireRetryDelay" value="1000" />   
  36.         <!--关闭连接时,是否提交未提交的事务,默认为false,即关闭连接,回滚未提交的事务 -->     
  37.         <property name="autoCommitOnClose">false</property>    
  38.         <!--c3p0将建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数那么属性preferredTestQuery将被忽略。你不能在这张Test表上进行任何操作,它将只供c3p0测试使用。默认值: null -->     
  39.         <property name="automaticTestTable">Test</property>   
  40.          <!--如果为false,则获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常,但是数据源仍有效保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。默认: false-->     
  41.         <property name="breakAfterAcquireFailure">false</property>  
  42.         <!--每60秒检查所有连接池中的空闲连接。默认值: 0,不检查 -->     
  43.         <property name="idleConnectionTestPeriod">60</property>      
  44.         <!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。默认值: 0 -->     
  45.         <property name="maxStatementsPerConnection"></property>   
  46.     </named-config>    
  47.         
  48.         
  49.     <!-- This is my config for oracle -->    
  50.     <named-config name="oracle">    
  51.         <property name="driverClass">oracle.jdbc.driver.OracleDriver</property>    
  52.         <property name="jdbcUrl">jdbc:oracle:thin:@localhost:1521:orcl</property>    
  53.         <property name="user">scott</property>    
  54.         <property name="password">liang</property>    
  55.         <property name="initialPoolSize">10</property>    
  56.         <property name="maxIdleTime">30</property>    
  57.         <property name="maxPoolSize">100</property>    
  58.         <property name="minPoolSize">10</property>    
  59.         <property name="maxStatements">200</property>    
  60.     </named-config>    
  61. </c3p0-config>  

以上是关于连接池+事务的主要内容,如果未能解决你的问题,请参考以下文章

Java 微服务 乐优网络商城 day02 源代码 SpringBoot 实战开发 整合JDBC和事务(数据库连接池)

JavaWeb之事务&数据库连接池

在 Tomcat7 中使用 JDBC 连接池进行事务处理

Mybatis框架第二篇

Java 微服务 day02 源代码 SpringBoot 实战开发 整合JDBC和事务(数据库连接池)

连接SQLServer时,因启用连接池导致孤立事务的原因分析和解决办法