| @@ -0,0 +1,37 @@ | |||
| # maven ignore | |||
| target/ | |||
| *.jar | |||
| !.mvn/wrapper/* | |||
| *.war | |||
| *.zip | |||
| *.tar | |||
| *.tar.gz | |||
| # eclipse ignore | |||
| .settings/ | |||
| .project | |||
| .classpath | |||
| # idea ignore | |||
| .idea/ | |||
| *.ipr | |||
| *.iml | |||
| *.iws | |||
| # temp ignore | |||
| *.log | |||
| *.cache | |||
| *.diff | |||
| *.patch | |||
| *.tmp | |||
| # system ignore | |||
| .DS_Store | |||
| Thumbs.db | |||
| *.orig | |||
| # flatten ignore | |||
| .flattened-pom.xml | |||
| # license check result | |||
| license-list | |||
| @@ -0,0 +1,478 @@ | |||
| # DBCP | |||
| ## 简介 | |||
| DBCP用于创建和管理连接,利用连接池的方式复用连接减少了资源开销。 | |||
| 连接池的参数可以采用`properties`文件来配置:配置包括驱动、链接、账号密码,连接池基本参数,事务相关参数,连接测试的参数以及内存回收参数等。 | |||
| DBCP对外交互主要是一个`BasicDataDource`,用于设置连接池参数和获取连接对象,作用有点类似于JDK的`DriverManager`。通过源码可以看到,`BasicDataSource`内部有一个`dataSource` 和`connectionPool`字段。 | |||
| `dataSource`用于从连接池中获取连接。 | |||
| `connectionPool`用于创建,存储和管理池中的连接,里面有一个`Map`对象和`LinkedBlockingDeque`对象,分别存储着所有连接和空闲连接,构成所谓的“池”。 | |||
| ## 使用例子 | |||
| ### 需求 | |||
| 使用DBCP连接池获取连接对象,对用户数据进行增删改查。 | |||
| ### 工程环境 | |||
| JDK:1.8.0_201 | |||
| maven:3.6.1 | |||
| IDE:Spring Tool Suites4 for Eclipse | |||
| mysql驱动:8.0.15 | |||
| mysql:5.7 | |||
| ### 主要步骤 | |||
| DBCP对外交互主要是一个`BasicDataDource`,用于设置连接池参数和获取连接对象。 | |||
| 1. 通过`BasicDataSourceFactory.createDataSource(properties)`设置连接池参数,并获得`BasicDataDource`对象; | |||
| 2. 获取连接对象:调用`BasicDataDource`对象的`getConnection()`方法获取`Connection`对象。 | |||
| ### 创建表 | |||
| ```sql | |||
| CREATE DATABASE `demo`CHARACTER SET utf8 COLLATE utf8_bin; | |||
| User `demo`; | |||
| CREATE TABLE `user` ( | |||
| `id` tinyint(3) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户id', | |||
| `name` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '用户名', | |||
| `age` int(10) unsigned DEFAULT NULL COMMENT '用户年龄', | |||
| `gmt_create` datetime DEFAULT NULL COMMENT '记录创建时间', | |||
| `gmt_modified` datetime DEFAULT NULL COMMENT '记录最后修改时间', | |||
| PRIMARY KEY (`id`), | |||
| UNIQUE KEY `uk_name` (`name`) | |||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; | |||
| ``` | |||
| ### 创建项目 | |||
| 项目类型Maven Project,打包方式jar | |||
| ### 引入依赖 | |||
| ```xml | |||
| <!-- junit --> | |||
| <dependency> | |||
| <groupId>junit</groupId> | |||
| <artifactId>junit</artifactId> | |||
| <version>4.12</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <!-- dbcp --> | |||
| <dependency> | |||
| <groupId>org.apache.commons</groupId> | |||
| <artifactId>commons-dbcp2</artifactId> | |||
| <version>2.6.0</version> | |||
| </dependency> | |||
| <!-- log4j --> | |||
| <dependency> | |||
| <groupId>log4j</groupId> | |||
| <artifactId>log4j</artifactId> | |||
| <version>1.2.17</version> | |||
| </dependency> | |||
| <!-- mysql驱动的jar包 --> | |||
| <dependency> | |||
| <groupId>mysql</groupId> | |||
| <artifactId>mysql-connector-java</artifactId> | |||
| <version>8.0.15</version> | |||
| </dependency> | |||
| ``` | |||
| ### 编写jdbc.prperties | |||
| 路径:resources目录下 | |||
| ```properties | |||
| #数据库配置 | |||
| driverClassName=com.mysql.cj.jdbc.Driver | |||
| url=jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true | |||
| username=root | |||
| password=root | |||
| ``` | |||
| ### 编写JDBCUtil用于获得连接对象 | |||
| 这里设置工具类的目的是避免多个线程使用同一个连接对象,并提供了释放资源的方法(注意,考虑到重用性,这里并不会关闭连接)。 | |||
| 路径:`cn.zzs.jdbc` | |||
| ```java | |||
| /** | |||
| * @ClassName: JDBCUtil | |||
| * @Description: 用于获取数据库连接对象的工具类 | |||
| * @author: zzs | |||
| * @date: 2019年8月31日 下午9:05:08 | |||
| */ | |||
| public class JDBCUtil { | |||
| private static DataSource dataSource; | |||
| private static ThreadLocal<Connection> tl = new ThreadLocal<>(); | |||
| private static Object obj = new Object(); | |||
| static { | |||
| init(); | |||
| } | |||
| /** | |||
| * | |||
| * @Title: getConnection | |||
| * @Description: 获取数据库连接对象的方法,线程安全 | |||
| * @author: zzs | |||
| * @date: 2019年8月31日 下午9:22:29 | |||
| * @return: Connection | |||
| */ | |||
| public static Connection getConnection(){ | |||
| //从当前线程中获取连接对象 | |||
| Connection connection = tl.get(); | |||
| //判断为空的话,创建连接并绑定到当前线程 | |||
| if(connection == null) { | |||
| synchronized (obj) { | |||
| if(tl.get() == null) { | |||
| connection = createConnection(); | |||
| tl.set(connection); | |||
| } | |||
| } | |||
| } | |||
| return connection; | |||
| } | |||
| /** | |||
| * | |||
| * @Title: release | |||
| * @Description: 释放资源 | |||
| * @author: zzs | |||
| * @date: 2019年8月31日 下午9:39:24 | |||
| * @param conn | |||
| * @param statement | |||
| * @return: void | |||
| */ | |||
| public static void release(Connection conn,Statement statement,ResultSet resultSet) { | |||
| if(resultSet!=null) { | |||
| try { | |||
| resultSet.close(); | |||
| } catch (SQLException e) { | |||
| System.err.println("关闭ResultSet对象异常"); | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| if(statement != null) { | |||
| try { | |||
| statement.close(); | |||
| } catch (SQLException e) { | |||
| System.err.println("关闭Statement对象异常"); | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| //注意:这里不关闭连接 | |||
| if(conn!=null) { | |||
| try { | |||
| //如果连接失效的话,从当前线程的绑定中删除 | |||
| if(!conn.isValid(3)) { | |||
| tl.remove(); | |||
| } | |||
| } catch (SQLException e) { | |||
| System.err.println("校验连接有效性"); | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * | |||
| * @Title: createConnection | |||
| * @Description: 创建数据库连接 | |||
| * @author: zzs | |||
| * @date: 2019年8月31日 下午9:27:03 | |||
| * @return: Connection | |||
| */ | |||
| private static Connection createConnection(){ | |||
| Connection conn = null; | |||
| //获得连接 | |||
| try { | |||
| conn = dataSource.getConnection(); | |||
| } catch (SQLException e) { | |||
| System.err.println("从数据源获取连接失败"); | |||
| e.printStackTrace(); | |||
| } | |||
| return conn; | |||
| } | |||
| /** | |||
| * @Title: init | |||
| * @Description: 根据指定配置文件创建数据源对象 | |||
| * @author: zzs | |||
| * @date: 2019年9月1日 上午10:53:05 | |||
| * @return: void | |||
| */ | |||
| private static void init() { | |||
| //导入配置文件 | |||
| Properties properties = new Properties(); | |||
| InputStream in = JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties"); | |||
| try { | |||
| properties.load(in); | |||
| //根据配置文件内容获得数据源对象 | |||
| dataSource = BasicDataSourceFactory.createDataSource(properties); | |||
| } catch (IOException e) { | |||
| System.err.println("导入配置文件出错"); | |||
| e.printStackTrace(); | |||
| } catch (Exception e) { | |||
| System.err.println("根据指定配置文件创建数据源出错"); | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| } | |||
| ``` | |||
| ### 编写测试类 | |||
| 路径:test目录下的`cn.zzs.jdbc` | |||
| #### 添加用户 | |||
| 注意:这里引入了事务 | |||
| ```java | |||
| /** | |||
| * 测试添加用户 | |||
| * @throws SQLException | |||
| */ | |||
| @Test | |||
| public void saveUser() throws Exception { | |||
| //创建sql | |||
| String sql = "insert into user values(null,?,?,?,?)"; | |||
| //获得连接 | |||
| Connection connection = JDBCUtil.getConnection(); | |||
| PreparedStatement statement = null; | |||
| try { | |||
| //设置非自动提交 | |||
| connection.setAutoCommit(false); | |||
| //获得Statement对象 | |||
| statement = connection.prepareStatement(sql); | |||
| //设置参数 | |||
| statement.setString(1, "zzs001"); | |||
| statement.setInt(2, 18); | |||
| statement.setDate(3, new Date(System.currentTimeMillis())); | |||
| statement.setDate(4, new Date(System.currentTimeMillis())); | |||
| //执行 | |||
| statement.executeUpdate(); | |||
| //提交事务 | |||
| connection.commit(); | |||
| } catch (Exception e) { | |||
| System.out.println("异常导致操作回滚"); | |||
| connection.rollback(); | |||
| e.printStackTrace(); | |||
| } finally { | |||
| //释放资源 | |||
| JDBCUtil.release(connection, statement,null); | |||
| } | |||
| } | |||
| ``` | |||
| #### 更新用户 | |||
| ```java | |||
| /** | |||
| * 测试更新用户 | |||
| */ | |||
| @Test | |||
| public void updateUser() throws Exception { | |||
| //创建sql | |||
| String sql = "update user set age = ?,gmt_modified = ? where name = ?"; | |||
| //获得连接 | |||
| Connection connection = JDBCUtil.getConnection(); | |||
| PreparedStatement statement = null; | |||
| try { | |||
| //设置非自动提交 | |||
| connection.setAutoCommit(false); | |||
| //获得Statement对象 | |||
| statement = connection.prepareStatement(sql); | |||
| //设置参数 | |||
| statement.setInt(1, 19); | |||
| statement.setDate(2, new Date(System.currentTimeMillis())); | |||
| statement.setString(3, "zzs001"); | |||
| //执行 | |||
| statement.executeUpdate(); | |||
| //提交事务 | |||
| connection.commit(); | |||
| } catch (Exception e) { | |||
| System.out.println("异常导致操作回滚"); | |||
| connection.rollback(); | |||
| e.printStackTrace(); | |||
| } finally { | |||
| //释放资源 | |||
| JDBCUtil.release(connection, statement,null); | |||
| } | |||
| } | |||
| ``` | |||
| #### 查询用户 | |||
| ```java | |||
| /** | |||
| * 测试查找用户 | |||
| */ | |||
| @Test | |||
| public void findUser() throws Exception { | |||
| //创建sql | |||
| String sql = "select * from user where name = ?"; | |||
| //获得连接 | |||
| Connection connection = JDBCUtil.getConnection(); | |||
| PreparedStatement statement = null; | |||
| ResultSet resultSet = null; | |||
| try { | |||
| //获得Statement对象 | |||
| statement = connection.prepareStatement(sql); | |||
| //设置参数 | |||
| statement.setString(1, "zzs001"); | |||
| //执行 | |||
| resultSet = statement.executeQuery(); | |||
| //遍历结果集 | |||
| while (resultSet.next()) { | |||
| String name = resultSet.getString(2); | |||
| int age = resultSet.getInt(3); | |||
| System.out.println("用户名:" + name + ",年龄:" + age); | |||
| } | |||
| } finally { | |||
| //释放资源 | |||
| JDBCUtil.release(connection, statement,resultSet); | |||
| } | |||
| } | |||
| ``` | |||
| #### 删除用户 | |||
| ```java | |||
| /** | |||
| * 测试删除用户 | |||
| */ | |||
| @Test | |||
| public void deleteUser() throws Exception { | |||
| //创建sql | |||
| String sql = "delete from user where name = ?"; | |||
| //获得连接 | |||
| Connection connection = JDBCUtil.getConnection(); | |||
| PreparedStatement statement = null; | |||
| try { | |||
| //设置非自动提交 | |||
| connection.setAutoCommit(false); | |||
| //获得Statement对象 | |||
| statement = connection.prepareStatement(sql); | |||
| //设置参数 | |||
| statement.setString(1, "zzs001"); | |||
| //执行 | |||
| statement.executeUpdate(); | |||
| //提交事务 | |||
| connection.commit(); | |||
| } catch (Exception e) { | |||
| System.out.println("异常导致操作回滚"); | |||
| connection.rollback(); | |||
| e.printStackTrace(); | |||
| } finally { | |||
| //释放资源 | |||
| JDBCUtil.release(connection, statement,null); | |||
| } | |||
| } | |||
| ``` | |||
| ### dbcp配置文件详解 | |||
| #### 数据库基本配置 | |||
| ```properties | |||
| driverClassName=com.mysql.cj.jdbc.Driver | |||
| url=jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true | |||
| username=root | |||
| password=root | |||
| ``` | |||
| #### 连接数据相关参数 | |||
| ```properties | |||
| #-------------连接数据相关参数-------------------------------- | |||
| #初始化连接:连接池启动时创建的初始化连接数量 | |||
| #默认为0 | |||
| initialSize=0 | |||
| #最大活动连接 | |||
| #连接池在同一时间能够分配的最大活动连接的数量, 如果设置为非正数则表示不限制 | |||
| #默认为8 | |||
| maxActive=8 | |||
| #最大空闲连接 | |||
| #连接池中容许保持空闲状态的最大连接数量,超过的空闲连接将被释放,如果设置为负数表示不限制 | |||
| #默认为8 | |||
| maxIdle=8 | |||
| #最小空闲连接 | |||
| #连接池中容许保持空闲状态的最小连接数量,低于这个数量将创建新的连接,如果设置为0则不创建 | |||
| #默认为0 | |||
| minIdle=0 | |||
| #最大等待时间 | |||
| #当没有可用连接时,连接池等待连接被归还的最大时间(以毫秒计数),超过时间则抛出异常,如果设置为-1表示无限等待 | |||
| #默认无限 | |||
| maxWait=-1 | |||
| ``` | |||
| #### 事务相关的属性 | |||
| ```properties | |||
| #-------------事务相关的属性-------------------------------- | |||
| #连接池创建的连接的默认的auto-commit状态 | |||
| #默认为true | |||
| defaultAutoCommit=false | |||
| #连接池创建的连接的默认的read-only状态. 如果没有设置则setReadOnly方法将不会被调用. (某些驱动不支持只读模式,比如:Informix) | |||
| #默认值由驱动决定 | |||
| #defaultReadOnly=false | |||
| #连接池创建的连接的默认的TransactionIsolation状态 | |||
| #可用值为下列之一:NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE | |||
| #默认值由驱动决定 | |||
| defaultTransactionIsolation=REPEATABLE_READ | |||
| #连接池创建的连接的默认的catalog | |||
| #defaultCatalog | |||
| ``` | |||
| #### 连接检查情况 | |||
| ```properties | |||
| #-------------连接检查情况-------------------------------- | |||
| #SQL查询,用来验证从连接池取出的连接,在将连接返回给调用者之前.如果指定,则查询必须是一个SQL SELECT并且必须返回至少一行记录 | |||
| validationQuery= select 1 | |||
| #指明是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个. | |||
| #注意: 设置为true后如果要生效,validationQuery参数必须设置为非空字符串 | |||
| #默认为true | |||
| testOnBorrow=true | |||
| #指明是否在归还到池中前进行检验 | |||
| #注意: 设置为true后如果要生效,validationQuery参数必须设置为非空字符串 | |||
| #默认为false | |||
| testOnReturn=false | |||
| #是否开启空闲资源监测。 | |||
| #注意: 设置为true后如果要生效,validationQuery参数必须设置为非空字符串 | |||
| #默认为false | |||
| testWhileIdle= true | |||
| #空闲资源的检测周期(单位为毫秒)。默认-1:不检测。建议设置,周期自行选择。timeBetweenEvictionRunsMillis=30000 | |||
| #做空闲资源检测时,每次的采样数。默认3。 | |||
| #可根据自身应用连接数进行微调,如果设置为-1,就是对所有连接做空闲监测。 | |||
| numTestsPerEvictionRun=3 | |||
| #资源池中资源最小空闲时间(单位为毫秒),达到此值后空闲资源将被移除。 | |||
| #默认值1000*60*30 = 30分钟。建议默认,或根据自身业务选择。 | |||
| minEvictableIdleTimeMillis=1800000 | |||
| ``` | |||
| #### 缓存语句 | |||
| ```properties | |||
| #-------------缓存语句-------------------------------- | |||
| #开启池的prepared statement 池功能 | |||
| #注意: 确认连接还有剩余资源可以留给其他statement | |||
| #默认为false | |||
| poolPreparedStatements=false | |||
| #statement池能够同时分配的打开的statements的最大数量, 如果设置为0表示不限制 | |||
| #默认为0 | |||
| maxOpenPreparedStatements=0 | |||
| ``` | |||
| #### 连接泄漏回收参数 | |||
| ```properties | |||
| #-------------连接泄漏回收参数-------------------------------- | |||
| #标记是否删除泄露的连接,如果他们超过了removeAbandonedTimout的限制. | |||
| #如果设置为true, 连接被认为是被泄露并且可以被删除,如果空闲时间超过removeAbandonedTimeout. | |||
| #设置为true可以为写法糟糕的没有关闭连接的程序修复数据库连接. | |||
| #默认为false | |||
| removeAbandoned=false | |||
| #泄露的连接可以被删除的超时值, 单位秒 | |||
| #默认为300 | |||
| removeAbandonedTimeout=300 | |||
| #标记当Statement或连接被泄露时是否打印程序的stack traces日志。 | |||
| #被泄露的Statements和连接的日志添加在每个连接打开或者生成新的Statement,因为需要生成stack trace。 | |||
| #默认为false | |||
| logAbandoned=false | |||
| #如果开启"removeAbandoned",那么连接在被认为泄露时可能被池回收. | |||
| #这个机制在(getNumIdle() < 2) and (getNumActive() > getMaxActive() - 3)时被触发. | |||
| #举例当maxActive=20, 活动连接为18,空闲连接为1时可以触发"removeAbandoned". | |||
| #但是活动连接只有在没有被使用的时间超过"removeAbandonedTimeout"时才被删除,默认300秒.在resultset中游历不被计算为被使用. | |||
| ``` | |||
| #### 其他 | |||
| ```properties | |||
| #-------------其他-------------------------------- | |||
| #控制PoolGuard是否容许获取底层连接 | |||
| #默认为false | |||
| accessToUnderlyingConnectionAllowed=false | |||
| #如果容许则可以使用下面的方式来获取底层物理连接: | |||
| # Connection conn = ds.getConnection(); | |||
| # Connection dconn = ((DelegatingConnection) conn).getInnermostDelegate(); | |||
| # ... | |||
| # conn.close(); | |||
| ``` | |||
| > 学习使我快乐!! | |||
| @@ -0,0 +1,38 @@ | |||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | |||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <groupId>cn.zzs.dbcp</groupId> | |||
| <artifactId>DBCP-demo</artifactId> | |||
| <packaging>jar</packaging> | |||
| <version>1.0.0</version> | |||
| <name>DBCP-demo</name> | |||
| <url>http://maven.apache.org</url> | |||
| <dependencies> | |||
| <!-- junit --> | |||
| <dependency> | |||
| <groupId>junit</groupId> | |||
| <artifactId>junit</artifactId> | |||
| <version>4.12</version> | |||
| <scope>test</scope> | |||
| </dependency> | |||
| <!-- dbcp --> | |||
| <dependency> | |||
| <groupId>org.apache.commons</groupId> | |||
| <artifactId>commons-dbcp2</artifactId> | |||
| <version>2.6.0</version> | |||
| </dependency> | |||
| <!-- log4j --> | |||
| <dependency> | |||
| <groupId>log4j</groupId> | |||
| <artifactId>log4j</artifactId> | |||
| <version>1.2.17</version> | |||
| </dependency> | |||
| <!-- mysql驱动的jar包 --> | |||
| <dependency> | |||
| <groupId>mysql</groupId> | |||
| <artifactId>mysql-connector-java</artifactId> | |||
| <version>8.0.15</version> | |||
| </dependency> | |||
| </dependencies> | |||
| </project> | |||
| @@ -0,0 +1,136 @@ | |||
| package cn.zzs.dbcp; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| import java.sql.Connection; | |||
| import java.sql.ResultSet; | |||
| import java.sql.SQLException; | |||
| import java.sql.Statement; | |||
| import java.util.Properties; | |||
| import javax.sql.DataSource; | |||
| import org.apache.commons.dbcp2.BasicDataSourceFactory; | |||
| /** | |||
| * @ClassName: JDBCUtil | |||
| * @Description: 用于获取数据库连接对象的工具类 | |||
| * @author: zzs | |||
| * @date: 2019年8月31日 下午9:05:08 | |||
| */ | |||
| public class JDBCUtil { | |||
| private static DataSource dataSource; | |||
| private static ThreadLocal<Connection> tl = new ThreadLocal<>(); | |||
| private static Object obj = new Object(); | |||
| static { | |||
| init(); | |||
| } | |||
| /** | |||
| * | |||
| * @Title: getConnection | |||
| * @Description: 获取数据库连接对象的方法,线程安全 | |||
| * @author: zzs | |||
| * @date: 2019年8月31日 下午9:22:29 | |||
| * @return: Connection | |||
| */ | |||
| public static Connection getConnection(){ | |||
| //从当前线程中获取连接对象 | |||
| Connection connection = tl.get(); | |||
| //判断为空的话,创建连接并绑定到当前线程 | |||
| if(connection == null) { | |||
| synchronized (obj) { | |||
| if(tl.get() == null) { | |||
| connection = createConnection(); | |||
| tl.set(connection); | |||
| } | |||
| } | |||
| } | |||
| return connection; | |||
| } | |||
| /** | |||
| * | |||
| * @Title: release | |||
| * @Description: 释放资源 | |||
| * @author: zzs | |||
| * @date: 2019年8月31日 下午9:39:24 | |||
| * @param conn | |||
| * @param statement | |||
| * @return: void | |||
| */ | |||
| public static void release(Connection conn,Statement statement,ResultSet resultSet) { | |||
| if(resultSet!=null) { | |||
| try { | |||
| resultSet.close(); | |||
| } catch (SQLException e) { | |||
| System.err.println("关闭ResultSet对象异常"); | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| if(statement != null) { | |||
| try { | |||
| statement.close(); | |||
| } catch (SQLException e) { | |||
| System.err.println("关闭Statement对象异常"); | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| //注意:这里不关闭连接 | |||
| if(conn!=null) { | |||
| try { | |||
| //如果连接失效的话,从当前线程的绑定中删除 | |||
| if(!conn.isValid(3)) { | |||
| tl.remove(); | |||
| } | |||
| } catch (SQLException e) { | |||
| System.err.println("校验连接有效性"); | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| } | |||
| /** | |||
| * | |||
| * @Title: createConnection | |||
| * @Description: 创建数据库连接 | |||
| * @author: zzs | |||
| * @date: 2019年8月31日 下午9:27:03 | |||
| * @return: Connection | |||
| */ | |||
| private static Connection createConnection(){ | |||
| Connection conn = null; | |||
| //获得连接 | |||
| try { | |||
| conn = dataSource.getConnection(); | |||
| } catch (SQLException e) { | |||
| System.err.println("从数据源获取连接失败"); | |||
| e.printStackTrace(); | |||
| } | |||
| return conn; | |||
| } | |||
| /** | |||
| * @Title: init | |||
| * @Description: 根据指定配置文件创建数据源对象 | |||
| * @author: zzs | |||
| * @date: 2019年9月1日 上午10:53:05 | |||
| * @return: void | |||
| */ | |||
| private static void init() { | |||
| //导入配置文件 | |||
| Properties properties = new Properties(); | |||
| InputStream in = JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties"); | |||
| try { | |||
| properties.load(in); | |||
| //根据配置文件内容获得数据源对象 | |||
| dataSource = BasicDataSourceFactory.createDataSource(properties); | |||
| } catch (IOException e) { | |||
| System.err.println("导入配置文件出错"); | |||
| e.printStackTrace(); | |||
| } catch (Exception e) { | |||
| System.err.println("根据指定配置文件创建数据源出错"); | |||
| e.printStackTrace(); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,100 @@ | |||
| #数据库基本配置 | |||
| driverClassName=com.mysql.cj.jdbc.Driver | |||
| url=jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true | |||
| username=root | |||
| password=root | |||
| #-------------连接数据相关参数-------------------------------- | |||
| #初始化连接:连接池启动时创建的初始化连接数量 | |||
| #默认为0 | |||
| initialSize=0 | |||
| #最大活动连接 | |||
| #连接池在同一时间能够分配的最大活动连接的数量, 如果设置为非正数则表示不限制 | |||
| #默认为8 | |||
| maxActive=8 | |||
| #最大空闲连接 | |||
| #连接池中容许保持空闲状态的最大连接数量,超过的空闲连接将被释放,如果设置为负数表示不限制 | |||
| #默认为8 | |||
| maxIdle=8 | |||
| #最小空闲连接 | |||
| #连接池中容许保持空闲状态的最小连接数量,低于这个数量将创建新的连接,如果设置为0则不创建 | |||
| #默认为0 | |||
| minIdle=0 | |||
| #最大等待时间 | |||
| #当没有可用连接时,连接池等待连接被归还的最大时间(以毫秒计数),超过时间则抛出异常,如果设置为-1表示无限等待 | |||
| #默认无限 | |||
| maxWait=-1 | |||
| #-------------事务相关的属性-------------------------------- | |||
| #连接池创建的连接的默认的auto-commit状态 | |||
| #默认为true | |||
| defaultAutoCommit=false | |||
| #连接池创建的连接的默认的read-only状态. 如果没有设置则setReadOnly方法将不会被调用. (某些驱动不支持只读模式,比如:Informix) | |||
| #默认值由驱动决定 | |||
| #defaultReadOnly=false | |||
| #连接池创建的连接的默认的TransactionIsolation状态 | |||
| #可用值为下列之一:NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE | |||
| #默认值由驱动决定 | |||
| defaultTransactionIsolation=REPEATABLE_READ | |||
| #连接池创建的连接的默认的catalog | |||
| #defaultCatalog | |||
| #-------------连接检查情况-------------------------------- | |||
| #SQL查询,用来验证从连接池取出的连接,在将连接返回给调用者之前.如果指定,则查询必须是一个SQL SELECT并且必须返回至少一行记录 | |||
| validationQuery= select 1 | |||
| #指明是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个. | |||
| #注意: 设置为true后如果要生效,validationQuery参数必须设置为非空字符串 | |||
| #默认为true | |||
| testOnBorrow=true | |||
| #指明是否在归还到池中前进行检验 | |||
| #注意: 设置为true后如果要生效,validationQuery参数必须设置为非空字符串 | |||
| #默认为false | |||
| testOnReturn=false | |||
| #是否开启空闲资源监测。 | |||
| #注意: 设置为true后如果要生效,validationQuery参数必须设置为非空字符串 | |||
| #默认为false | |||
| testWhileIdle= true | |||
| #空闲资源的检测周期(单位为毫秒)。默认-1:不检测。建议设置,周期自行选择。timeBetweenEvictionRunsMillis=30000 | |||
| #做空闲资源检测时,每次的采样数。默认3。 | |||
| #可根据自身应用连接数进行微调,如果设置为-1,就是对所有连接做空闲监测。 | |||
| numTestsPerEvictionRun=3 | |||
| #资源池中资源最小空闲时间(单位为毫秒),达到此值后空闲资源将被移除。 | |||
| #默认值1000*60*30 = 30分钟。建议默认,或根据自身业务选择。 | |||
| minEvictableIdleTimeMillis=1800000 | |||
| #-------------缓存语句-------------------------------- | |||
| #开启池的prepared statement 池功能 | |||
| #注意: 确认连接还有剩余资源可以留给其他statement | |||
| #默认为false | |||
| poolPreparedStatements=false | |||
| #statement池能够同时分配的打开的statements的最大数量, 如果设置为0表示不限制 | |||
| #默认为0 | |||
| maxOpenPreparedStatements=0 | |||
| #-------------连接泄漏回收参数-------------------------------- | |||
| #标记是否删除泄露的连接,如果他们超过了removeAbandonedTimout的限制. | |||
| #如果设置为true, 连接被认为是被泄露并且可以被删除,如果空闲时间超过removeAbandonedTimeout. | |||
| #设置为true可以为写法糟糕的没有关闭连接的程序修复数据库连接. | |||
| #默认为false | |||
| removeAbandoned=false | |||
| #泄露的连接可以被删除的超时值, 单位秒 | |||
| #默认为300 | |||
| removeAbandonedTimeout=300 | |||
| #标记当Statement或连接被泄露时是否打印程序的stack traces日志。 | |||
| #被泄露的Statements和连接的日志添加在每个连接打开或者生成新的Statement,因为需要生成stack trace。 | |||
| #默认为false | |||
| logAbandoned=false | |||
| #如果开启"removeAbandoned",那么连接在被认为泄露时可能被池回收. | |||
| #这个机制在(getNumIdle() < 2) and (getNumActive() > getMaxActive() - 3)时被触发. | |||
| #举例当maxActive=20, 活动连接为18,空闲连接为1时可以触发"removeAbandoned". | |||
| #但是活动连接只有在没有被使用的时间超过"removeAbandonedTimeout"时才被删除,默认300秒.在resultset中游历不被计算为被使用. | |||
| #-------------其他-------------------------------- | |||
| #控制PoolGuard是否容许获取底层连接 | |||
| #默认为false | |||
| accessToUnderlyingConnectionAllowed=false | |||
| #如果容许则可以使用下面的方式来获取底层物理连接: | |||
| # Connection conn = ds.getConnection(); | |||
| # Connection dconn = ((DelegatingConnection) conn).getInnermostDelegate(); | |||
| # ... | |||
| # conn.close(); | |||
| @@ -0,0 +1,183 @@ | |||
| #OFF,systemOut,logFile,logDailyFile,logRollingFile,logMail,logDB,ALL | |||
| #①配置根Logger | |||
| log4j.rootLogger=debug,systemOut | |||
| #OFF,systemOut,logFile,logDailyFile,logRollingFile,logMail,logDB,ALL | |||
| #②配置其他Logger | |||
| #log4j.logger.myLogger=debug,systemOut | |||
| #log4j.additivity.mobileLogger=false | |||
| #输出到控制台 | |||
| log4j.appender.systemOut= org.apache.log4j.ConsoleAppender | |||
| log4j.appender.systemOut.layout= org.apache.log4j.PatternLayout | |||
| log4j.appender.systemOut.layout.ConversionPattern=[%p][Thread:%t]: %m%n | |||
| log4j.appender.systemOut.Threshold= debug | |||
| log4j.appender.systemOut.ImmediateFlush= TRUE | |||
| log4j.appender.systemOut.Target= System.out | |||
| #输出到文件 | |||
| log4j.appender.logFile= org.apache.log4j.FileAppender | |||
| log4j.appender.logFile.layout= org.apache.log4j.PatternLayout | |||
| log4j.appender.logFile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread:%t][Class:%c Method: %M]%n%p: %m%n | |||
| log4j.appender.logFile.Threshold= DEBUG | |||
| log4j.appender.logFile.ImmediateFlush= TRUE | |||
| log4j.appender.logFile.Append= TRUE | |||
| log4j.appender.logFile.File= logs/file_log.log | |||
| log4j.appender.logFile.Encoding= utf-8 | |||
| #按DatePattern输出到文件 | |||
| log4j.appender.logDailyFile= org.apache.log4j.DailyRollingFileAppender | |||
| log4j.appender.logDailyFile.layout= org.apache.log4j.PatternLayout | |||
| log4j.appender.logDailyFile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread:%t][Class:%c Method: %M]%n%p: %m%n | |||
| #[%-5p][%-22d{yyyy/MM/dd HH:mm:ssS}][%l]%n%m%n | |||
| log4j.appender.logDailyFile.Threshold= warn | |||
| log4j.appender.logDailyFile.ImmediateFlush= TRUE | |||
| log4j.appender.logDailyFile.Append= TRUE | |||
| log4j.appender.logDailyFile.File= logs/daily_log | |||
| log4j.appender.logDailyFile.DatePattern= '_'yyyy-MM-dd-HH-mm'.log' | |||
| log4j.appender.logDailyFile.Encoding= utf-8 | |||
| #设定文件大小输出到文件 | |||
| log4j.appender.logRollingFile= org.apache.log4j.RollingFileAppender | |||
| log4j.appender.logRollingFile.layout= org.apache.log4j.PatternLayout | |||
| log4j.appender.logRollingFile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread:%t][Class:%c Method: %M]%n%p: %m%n | |||
| log4j.appender.logRollingFile.Threshold= DEBUG | |||
| log4j.appender.logRollingFile.ImmediateFlush= TRUE | |||
| log4j.appender.logRollingFile.Append= TRUE | |||
| log4j.appender.logRollingFile.File=logs/rolling_log.log | |||
| log4j.appender.logRollingFile.MaxFileSize= 1mb | |||
| log4j.appender.logRollingFile.MaxBackupIndex= 10 | |||
| log4j.appender.logRollingFile.Encoding= utf-8 | |||
| #用Email发送日志 | |||
| log4j.appender.logMail= org.apache.log4j.net.SMTPAppender | |||
| log4j.appender.logMail.layout= org.apache.log4j.HTMLLayout | |||
| log4j.appender.logMail.layout.LocationInfo= TRUE | |||
| log4j.appender.logMail.layout.Title= My Mail LogFile | |||
| log4j.appender.logMail.Threshold= DEBUG | |||
| log4j.appender.logMail.SMTPDebug= FALSE | |||
| log4j.appender.logMail.SMTPHost= SMTP.163.com | |||
| log4j.appender.logMail.From= zzs@163.com | |||
| log4j.appender.logMail.To= zzs@gmail.com | |||
| #log4j.appender.logMail.Cc= xly3000@gmail.com | |||
| #log4j.appender.logMail.Bcc= xly3000@gmail.com | |||
| log4j.appender.logMail.SMTPUsername= zzs | |||
| log4j.appender.logMail.SMTPPassword= zzs | |||
| log4j.appender.logMail.Subject= Log4j Log Messages | |||
| #log4j.appender.logMail.BufferSize= 1024 | |||
| #log4j.appender.logMail.SMTPAuth= TRUE | |||
| #将日志登录到MySQL数据库 | |||
| log4j.appender.logDB= org.apache.log4j.jdbc.JDBCAppender | |||
| log4j.appender.logDB.layout= org.apache.log4j.PatternLayout | |||
| log4j.appender.logDB.Driver= com.mysql.cj.jdbc.Driver | |||
| log4j.appender.logDB.URL= jdbc:mysql://127.0.0.1:3306/crm_logs?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=true | |||
| log4j.appender.logDB.User= root | |||
| log4j.appender.logDB.Password= root | |||
| log4j.appender.logDB.Sql= INSERT INTO T_log4j(project_name,create_date,level,category,file_name,thread_name,line,all_category,message)values('Struts2','%d{yyyy-MM-ddHH:mm:ss}','%p','%c','%F','%t','%L','%l','%m') | |||
| ################################################################################ | |||
| #①配置根Logger,其语法为: | |||
| # | |||
| #log4j.rootLogger =[level],appenderName,appenderName2,... | |||
| #level是日志记录的优先级,分为OFF,TRACE,DEBUG,INFO,WARN,ERROR,FATAL,ALL | |||
| ##Log4j建议只使用四个级别,优先级从低到高分别是DEBUG,INFO,WARN,ERROR | |||
| #通过在这里定义的级别,您可以控制到应用程序中相应级别的日志信息的开关 | |||
| #比如在这里定义了INFO级别,则应用程序中所有DEBUG级别的日志信息将不被打印出来 | |||
| #appenderName就是指定日志信息输出到哪个地方。可同时指定多个输出目的 | |||
| ################################################################################ | |||
| ################################################################################ | |||
| #②配置日志信息输出目的地Appender,其语法为: | |||
| # | |||
| #log4j.appender.appenderName =fully.qualified.name.of.appender.class | |||
| #log4j.appender.appenderName.optionN =valueN | |||
| # | |||
| #Log4j提供的appender有以下几种: | |||
| #1)org.apache.log4j.ConsoleAppender(输出到控制台) | |||
| #2)org.apache.log4j.FileAppender(输出到文件) | |||
| #3)org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件) | |||
| #4)org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件) | |||
| #5)org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方) | |||
| # | |||
| #1)ConsoleAppender选项属性 | |||
| # -Threshold = DEBUG:指定日志消息的输出最低层次 | |||
| # -ImmediateFlush = TRUE:默认值是true,所有的消息都会被立即输出 | |||
| # -Target = System.err:默认值System.out,输出到控制台(err为红色,out为黑色) | |||
| # | |||
| #2)FileAppender选项属性 | |||
| # -Threshold = INFO:指定日志消息的输出最低层次 | |||
| # -ImmediateFlush = TRUE:默认值是true,所有的消息都会被立即输出 | |||
| # -File = C:\log4j.log:指定消息输出到C:\log4j.log文件 | |||
| # -Append = FALSE:默认值true,将消息追加到指定文件中,false指将消息覆盖指定的文件内容 | |||
| # -Encoding = UTF-8:可以指定文件编码格式 | |||
| # | |||
| #3)DailyRollingFileAppender选项属性 | |||
| #-Threshold = WARN:指定日志消息的输出最低层次 | |||
| #-ImmediateFlush = TRUE:默认值是true,所有的消息都会被立即输出 | |||
| # -File =C:\log4j.log:指定消息输出到C:\log4j.log文件 | |||
| # -Append= FALSE:默认值true,将消息追加到指定文件中,false指将消息覆盖指定的文件内容 | |||
| #-DatePattern='.'yyyy-ww:每周滚动一次文件,即每周产生一个新的文件。还可以按用以下参数: | |||
| # '.'yyyy-MM:每月 | |||
| # '.'yyyy-ww:每周 | |||
| # '.'yyyy-MM-dd:每天 | |||
| # '.'yyyy-MM-dd-a:每天两次 | |||
| # '.'yyyy-MM-dd-HH:每小时 | |||
| # '.'yyyy-MM-dd-HH-mm:每分钟 | |||
| #-Encoding = UTF-8:可以指定文件编码格式 | |||
| # | |||
| #4)RollingFileAppender选项属性 | |||
| #-Threshold = ERROR:指定日志消息的输出最低层次 | |||
| #-ImmediateFlush = TRUE:默认值是true,所有的消息都会被立即输出 | |||
| # -File =C:/log4j.log:指定消息输出到C:/log4j.log文件 | |||
| # -Append= FALSE:默认值true,将消息追加到指定文件中,false指将消息覆盖指定的文件内容 | |||
| #-MaxFileSize = 100KB:后缀可以是KB,MB,GB.在日志文件到达该大小时,将会自动滚动.如:log4j.log.1 | |||
| #-MaxBackupIndex = 2:指定可以产生的滚动文件的最大数 | |||
| #-Encoding = UTF-8:可以指定文件编码格式 | |||
| ################################################################################ | |||
| ################################################################################ | |||
| #③配置日志信息的格式(布局),其语法为: | |||
| # | |||
| #log4j.appender.appenderName.layout=fully.qualified.name.of.layout.class | |||
| #log4j.appender.appenderName.layout.optionN= valueN | |||
| # | |||
| #Log4j提供的layout有以下几种: | |||
| #5)org.apache.log4j.HTMLLayout(以HTML表格形式布局) | |||
| #6)org.apache.log4j.PatternLayout(可以灵活地指定布局模式) | |||
| #7)org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串) | |||
| #8)org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息) | |||
| #9)org.apache.log4j.xml.XMLLayout(以XML形式布局) | |||
| # | |||
| #5)HTMLLayout选项属性 | |||
| #-LocationInfo = TRUE:默认值false,输出java文件名称和行号 | |||
| #-Title=Struts Log Message:默认值 Log4JLog Messages | |||
| # | |||
| #6)PatternLayout选项属性 | |||
| #-ConversionPattern = %m%n:格式化指定的消息(参数意思下面有) | |||
| # | |||
| #9)XMLLayout选项属性 | |||
| #-LocationInfo = TRUE:默认值false,输出java文件名称和行号 | |||
| # | |||
| #Log4J采用类似C语言中的printf函数的打印格式格式化日志信息,打印参数如下: | |||
| #%m 输出代码中指定的消息 | |||
| #%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL | |||
| #%r 输出自应用启动到输出该log信息耗费的毫秒数 | |||
| #%c 输出所属的类目,通常就是所在类的全名 | |||
| #%t 输出产生该日志事件的线程名 | |||
| #%n 输出一个回车换行符,Windows平台为“\r\n”,Unix平台为“\n” | |||
| #%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式 | |||
| # 如:%d{yyyy年MM月dd日HH:mm:ss,SSS},输出类似:2012年01月05日 22:10:28,921 | |||
| #%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数 | |||
| # 如:Testlog.main(TestLog.java:10) | |||
| #%F 输出日志消息产生时所在的文件名称 | |||
| #%L 输出代码中的行号 | |||
| #%x 输出和当前线程相关联的NDC(嵌套诊断环境),像javaservlets多客户多线程的应用中 | |||
| #%% 输出一个"%"字符 | |||
| # | |||
| # 可以在%与模式字符之间加上修饰符来控制其最小宽度、最大宽度、和文本的对齐方式。如: | |||
| # %5c: 输出category名称,最小宽度是5,category<5,默认的情况下右对齐 | |||
| # %-5c:输出category名称,最小宽度是5,category<5,"-"号指定左对齐,会有空格 | |||
| # %.5c:输出category名称,最大宽度是5,category>5,就会将左边多出的字符截掉,<5不会有空格 | |||
| # %20.30c:category名称<20补空格,并且右对齐,>30字符,就从左边交远销出的字符截掉 | |||
| ################################################################################ | |||
| ################################################################################ | |||
| #④指定特定包的输出特定的级别 | |||
| #log4j.logger.org.springframework=DEBUG | |||
| ################################################################################ | |||
| @@ -0,0 +1,146 @@ | |||
| package cn.zzs.dbcp; | |||
| import java.sql.Connection; | |||
| import java.sql.Date; | |||
| import java.sql.PreparedStatement; | |||
| import java.sql.ResultSet; | |||
| import java.sql.SQLException; | |||
| import org.junit.Test; | |||
| /** | |||
| * @ClassName: DBCPTest | |||
| * @Description: 测试DBCP | |||
| * @author: zzs | |||
| * @date: 2019年8月31日 下午9:39:54 | |||
| */ | |||
| public class DBCPTest { | |||
| /** | |||
| * 测试添加用户 | |||
| * @throws SQLException | |||
| */ | |||
| @Test | |||
| public void saveUser() throws Exception { | |||
| //创建sql | |||
| String sql = "insert into user values(null,?,?,?,?)"; | |||
| //获得连接 | |||
| Connection connection = JDBCUtil.getConnection(); | |||
| PreparedStatement statement = null; | |||
| try { | |||
| //设置非自动提交 | |||
| connection.setAutoCommit(false); | |||
| //获得Statement对象 | |||
| statement = connection.prepareStatement(sql); | |||
| //设置参数 | |||
| statement.setString(1, "zzs001"); | |||
| statement.setInt(2, 18); | |||
| statement.setDate(3, new Date(System.currentTimeMillis())); | |||
| statement.setDate(4, new Date(System.currentTimeMillis())); | |||
| //执行 | |||
| statement.executeUpdate(); | |||
| //提交事务 | |||
| connection.commit(); | |||
| } catch (Exception e) { | |||
| System.out.println("异常导致操作回滚"); | |||
| connection.rollback(); | |||
| e.printStackTrace(); | |||
| } finally { | |||
| //释放资源 | |||
| JDBCUtil.release(connection, statement,null); | |||
| } | |||
| } | |||
| /** | |||
| * 测试更新用户 | |||
| */ | |||
| @Test | |||
| public void updateUser() throws Exception { | |||
| //创建sql | |||
| String sql = "update user set age = ?,gmt_modified = ? where name = ?"; | |||
| //获得连接 | |||
| Connection connection = JDBCUtil.getConnection(); | |||
| PreparedStatement statement = null; | |||
| try { | |||
| //设置非自动提交 | |||
| connection.setAutoCommit(false); | |||
| //获得Statement对象 | |||
| statement = connection.prepareStatement(sql); | |||
| //设置参数 | |||
| statement.setInt(1, 19); | |||
| statement.setDate(2, new Date(System.currentTimeMillis())); | |||
| statement.setString(3, "zzs001"); | |||
| //执行 | |||
| statement.executeUpdate(); | |||
| //提交事务 | |||
| connection.commit(); | |||
| } catch (Exception e) { | |||
| System.out.println("异常导致操作回滚"); | |||
| connection.rollback(); | |||
| e.printStackTrace(); | |||
| } finally { | |||
| //释放资源 | |||
| JDBCUtil.release(connection, statement,null); | |||
| } | |||
| } | |||
| /** | |||
| * 测试查找用户 | |||
| */ | |||
| @Test | |||
| public void findUser() throws Exception { | |||
| //创建sql | |||
| String sql = "select * from user where name = ?"; | |||
| //获得连接 | |||
| Connection connection = JDBCUtil.getConnection(); | |||
| PreparedStatement statement = null; | |||
| ResultSet resultSet = null; | |||
| try { | |||
| //获得Statement对象 | |||
| statement = connection.prepareStatement(sql); | |||
| //设置参数 | |||
| statement.setString(1, "zzs001"); | |||
| //执行 | |||
| resultSet = statement.executeQuery(); | |||
| //遍历结果集 | |||
| while (resultSet.next()) { | |||
| String name = resultSet.getString(2); | |||
| int age = resultSet.getInt(3); | |||
| System.out.println("用户名:" + name + ",年龄:" + age); | |||
| } | |||
| } finally { | |||
| //释放资源 | |||
| JDBCUtil.release(connection, statement,resultSet); | |||
| } | |||
| } | |||
| /** | |||
| * 测试删除用户 | |||
| */ | |||
| @Test | |||
| public void deleteUser() throws Exception { | |||
| //创建sql | |||
| String sql = "delete from user where name = ?"; | |||
| //获得连接 | |||
| Connection connection = JDBCUtil.getConnection(); | |||
| PreparedStatement statement = null; | |||
| try { | |||
| //设置非自动提交 | |||
| connection.setAutoCommit(false); | |||
| //获得Statement对象 | |||
| statement = connection.prepareStatement(sql); | |||
| //设置参数 | |||
| statement.setString(1, "zzs001"); | |||
| //执行 | |||
| statement.executeUpdate(); | |||
| //提交事务 | |||
| connection.commit(); | |||
| } catch (Exception e) { | |||
| System.out.println("异常导致操作回滚"); | |||
| connection.rollback(); | |||
| e.printStackTrace(); | |||
| } finally { | |||
| //释放资源 | |||
| JDBCUtil.release(connection, statement,null); | |||
| } | |||
| } | |||
| } | |||