连接池+事务
事务的四个特性:
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 自定义连接池:
创建连接池
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | package com.rl.lianjiechi; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.LinkedList; import java.util.Properties; 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<>(); // 获取properties的配置文件,并以流的方式存储 InputStream inputStream = MyPool. class .getClassLoader() .getResourceAsStream( "jdbc.properties" ); // 创建properties的属性处理对象 Properties properties= new Properties(); public MyPool( int size) throws IOException, ClassNotFoundException { super (); this .size = size; for ( int i= 0 ;i<size;i++){ try { properties.load(inputStream); Class.forName(properties.getProperty( "driverClassName" )); final Connection conn = DriverManager.getConnection( properties.getProperty( "url" ), properties.getProperty( "user" ), properties.getProperty( "pass" )); //System.out.println( properties.getProperty("url")); linkedList.add(conn); } 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 ; } } |
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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); } } |
创建初始化时的连接所用的工具类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | 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常见的几种数据源
DBCP | c3p0 | Druid | |
用户名 | username | user | username |
密码 | password | password | password |
URL | url | jdbcUrl | jdbcUrl |
驱动类名 | driverClassName | driverClass | driverClassName |
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放在类路径下。
- <?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/jdbc?useUnicode=true&characterEncoding=UTF8</property>
- <property name="user">root</property>
- <property name="password"></property>
- <!-- 初始化连接池中的连接数,取值应在minPoolSize与maxPoolSize之间,默认为3-->
- <property name="initialPoolSize">10</property>
- <!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。默认值: 0 -->
- <property name="maxIdleTime">30</property>
- <!--连接池中保留的最大连接数。默认值: 15 -->
- <property name="maxPoolSize">100</property>
- <!-- 连接池中保留的最小连接数,默认为:3-->
- <property name="minPoolSize">10</property>
- <!--c3p0全局的PreparedStatements缓存的大小。如果maxStatements与maxStatementsPerConnection均为0,则缓存不生效,只要有一个不为0,则语句的缓存就能生效。如果默认值: 0-->
- <property name="maxStatements">200</property>
- <!-- 当连接池连接耗尽时,客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限期等待。单位毫秒。默认: 0 -->
- <property name="checkoutTimeout" value="3000"/>
- <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。默认值: 3 -->
- <property name="acquireIncrement" value="2"/>
- <!--定义在从数据库获取新连接失败后重复尝试的次数。默认值: 30 ;小于等于0表示无限次-->
- <property name="acquireRetryAttempts" value="0"/>
- <!--重新尝试的时间间隔,默认为:1000毫秒-->
- <property name="acquireRetryDelay" value="1000" />
- <!--关闭连接时,是否提交未提交的事务,默认为false,即关闭连接,回滚未提交的事务 -->
- <property name="autoCommitOnClose">false</property>
- <!--c3p0将建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数那么属性preferredTestQuery将被忽略。你不能在这张Test表上进行任何操作,它将只供c3p0测试使用。默认值: null -->
- <property name="automaticTestTable">Test</property>
- <!--如果为false,则获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常,但是数据源仍有效保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。默认: false-->
- <property name="breakAfterAcquireFailure">false</property>
- <!--每60秒检查所有连接池中的空闲连接。默认值: 0,不检查 -->
- <property name="idleConnectionTestPeriod">60</property>
- <!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。默认值: 0 -->
- <property name="maxStatementsPerConnection"></property>
- </named-config>
- <!-- This is my config for oracle -->
- <named-config name="oracle">
- <property name="driverClass">oracle.jdbc.driver.OracleDriver</property>
- <property name="jdbcUrl">jdbc:oracle:thin:@localhost:1521:orcl</property>
- <property name="user">scott</property>
- <property name="password">liang</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>
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 大模型 Token 究竟是啥:图解大模型Token
· 35岁程序员的中年求职记:四次碰壁后的深度反思
· 继承的思维:从思维模式到架构设计的深度解析
· 如何在 .NET 中 使用 ANTLR4
· 后端思维之高并发处理方案
· BotSharp + MCP 三步实现智能体开发
· BotSharp 5.0 MCP:迈向更开放的AI Agent框架
· 分享 3 款基于 .NET 开源且免费的远程桌面工具
· 在线聊天系统中的多窗口数据同步技术解密
· 5. RabbitMQ 消息队列中 Exchanges(交换机) 的详细说明