三、使用PreparedStatement实现CRUD操作
JDBCUtils类,包含
- 获取连接方法
getConnection()
- 关闭连接方法
closeConnection(Connection conn, PreparedStatement ps, ResultSet rs)
publicclassJDBCUtils { /*** 获取连接* @return* @throws Exception*/publicstaticConnectiongetConnection() throwsException { Connectionconn=null; InputStreamis=JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties"); Propertiespros=newProperties(); pros.load(is); Stringuser=pros.getProperty("user"); Stringpassword=pros.getProperty("password"); Stringurl=pros.getProperty("url"); StringdriverClass=pros.getProperty("driverClass"); Class.forName(driverClass); conn=DriverManager.getConnection(url, user, password); returnconn; } /*** 关闭连接* @param con* @param ps*/publicstaticvoidcloseConnection(Connectioncon, PreparedStatementps) { try { if (con!=null) con.close(); } catch (SQLExceptione1) { e1.printStackTrace(); } try { if (ps!=null) ps.close(); } catch (SQLExceptione) { e.printStackTrace(); } } /*** 关闭连接* @param conn* @param ps* @param rs*/publicstaticvoidcloseConnection(Connectionconn, PreparedStatementps, ResultSetrs) { try { if (conn!=null) conn.close(); } catch (SQLExceptione) { e.printStackTrace(); } try { if (ps!=null) ps.close(); } catch (SQLExceptione) { e.printStackTrace(); } try { if (rs!=null) rs.close(); } catch (SQLExceptione) { e.printStackTrace(); } } }
1.增、删、改操作
// 针对于不同的表,通用的增、删、改操作publicstaticvoidupdate(Stringsql, Object... orgs) { Connectioncon=null; PreparedStatementps=null; try { // 1.获取数据库的连接con=JDBCUtils.getConnection(); // 2.获取PreparedStatement的实例 (或:预编译sql语句)ps=con.prepareStatement(sql); // 3.填充占位符for (inti=0; i<orgs.length; i++) { ps.setObject(i+1, orgs[i]); } // 4.执行sql语句ps.execute(); } catch (SQLExceptione) { // TODO 自动生成的 catch 块e.printStackTrace(); } finally { // 5.关闭资源JDBCUtils.closeConnection(con, ps); } }
注:增、删、改无返回值
2.查询操作
返回单个结果
// 返回一个结果publicstatic<T>TselectOne(Class<T>clazz, Stringsql, Object... args) { Connectioncon=null; PreparedStatementps=null; ResultSetrs=null; try { // 1.获取数据库连接con=JDBCUtils.getConnection(); // 2.预编译sql语句,得到PreparedStatement对象ps=con.prepareStatement(sql); // 3.填充占位符for (inti=0; i<args.length; i++) { ps.setObject(i+1, args[i]); } // 4.执行executeQuery(),得到结果集:ResultSetrs=ps.executeQuery(); // 5.得到结果集的元数据:ResultSetMetaDataResultSetMetaDatarsmd=rs.getMetaData(); // 6.1通过ResultSetMetaData得到columnCount,columnLabel;通过ResultSet得到列值intcolumnCount=rsmd.getColumnCount(); if (rs.next()) { Tt=clazz.newInstance(); // 遍历每一个列for (inti=0; i<columnCount; i++) { // 获取列值ObjectcolumnVal=rs.getObject(i+1); // 获取列的别名:列的别名,使用类的属性名充当StringcolumnLabel=rsmd.getColumnLabel(i+1); // 6.2使用反射,给对象的相应属性赋值Fieldfield=clazz.getDeclaredField(columnLabel); field.setAccessible(true); field.set(t, columnVal); } returnt; } } catch (Exceptione) { e.printStackTrace(); } finally { // 7.关闭资源JDBCUtils.closeConnection(con, ps); } returnnull; }
说明:使用PreparedStatement实现的查询操作可以替换Statement实现的查询操作,解决Statement拼串和SQL注入问题。
返回所有结果
// 获取所有结果publicstatic<T>List<T>selectAll(Class<T>clazz, Stringsql, Object... args) { Connectioncon=null; PreparedStatementps=null; ResultSetrs=null; Listlist=newArrayList(); try { // 1.获取数据库连接con=JDBCUtils.getConnection(); // 2.预编译sql语句,得到PreparedStatement对象ps=con.prepareStatement(sql); // 3.填充占位符for (inti=0; i<args.length; i++) { ps.setObject(i+1, args[i]); } // 4.执行executeQuery(),得到结果集:ResultSetrs=ps.executeQuery(); // 获取结果集的元数据ResultSetMetaDatarsmd=rs.getMetaData(); // 获取结果集列数intcolumnCount=rsmd.getColumnCount(); while (rs.next()) { Tt=clazz.newInstance(); for (inti=0; i<columnCount; i++) { // 6.使用反射,给对象的相应属性赋值Fieldfield=clazz.getDeclaredField(rsmd.getColumnLabel(i+1)); field.setAccessible(true); field.set(t, rs.getObject(i+1)); } list.add(t); } returnlist; } catch (Exceptione) { e.printStackTrace(); } finally { JDBCUtils.closeConnection(con, ps, rs); } returnnull; }
3.操作批量数据
- 使用
addBatch() / executeBatch() / clearBatch()
- 修改mysql配置文件,添加
?rewriteBatchedStatements=true
使mysql开启批处理的支持
user=rootpassword=123456url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true&serverTimezone=GMT%2B8driverClass=com.mysql.cj.jdbc.Driver
- 使用高版本的mysql驱动
//向表中插入50W条数据publicstaticvoidmain(String[] args) { Connectioncon=null; PreparedStatementps=null; try { longstart=System.currentTimeMillis(); Stringsql="insert into goods(name) values(?)"; // 获取连接con=JDBCUtils.getConnection(); // 关闭自动提交数据con.setAutoCommit(false); ps=con.prepareStatement(sql); //插入50W条数据for (inti=1; i<=500000; i++) { ps.setObject(1, "name_"+i); // 1."攒"sqlps.addBatch(); //攒够1000sql执行一次if (i%1000==0) { // 2.执行batch批量ps.executeBatch(); // 3.清空batchps.clearBatch(); } } // 手动提交数据con.commit(); longend=System.currentTimeMillis(); System.out.println(end-start); } catch (SQLExceptione) { e.printStackTrace(); } finally { // 关闭连接JDBCUtils.closeConnection(con, ps); } }
四、数据库事务☆
1. 数据库事务介绍
- 事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态。
- 事务处理(事务操作):保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久地保存下来;要么数据库管理系统将放弃所作的所有修改,整个事务**回滚(rollback)**到最初状态。
- 为确保数据库中数据的一致性,数据的操纵应当是离散的成组的逻辑单元:当它全部完成时,数据的一致性可以保持,而当这个单元中的一部分操作失败,整个事务应全部视为错误,所有从起始点以后的操作应全部回退到开始状态。
2. JDBC事务处理
- 数据一旦提交,就不可回滚。
- 数据什么时候意味着提交?
- 当一个连接对象被创建时,默认情况下是自动提交事务:每次执行一个 SQL 语句时,如果执行成功,就会向数据库自动提交,而不能回滚。
- **关闭数据库连接,数据就会自动的提交。**如果多个操作,每个操作使用的是自己单独的连接,则无法保证事务。即同一个事务的多个操作必须在同一个连接下。
- JDBC程序中为了让多个 SQL 语句作为一个事务执行:
- 调用 Connection 对象的 setAutoCommit(false); 以取消自动提交事务
- 在所有的 SQL 语句都成功执行后,调用 commit(); 方法提交事务
- 在出现异常时,调用 rollback(); 方法回滚事务
若此时 Connection 没有被关闭,还可能被重复使用,则需要恢复其自动提交状态 setAutoCommit(true)。尤其是在使用数据库连接池技术时,执行close()方法前,建议恢复自动提交状态。
【案例:用户AA向用户BB转账100】
publicvoidtestJDBCTransaction() { Connectionconn=null; try { // 1.获取数据库连接conn=JDBCUtils.getConnection(); // 2.开启事务,关闭自动提交conn.setAutoCommit(false); // 3.进行数据库操作Stringsql1="update user_table set balance = balance - 100 where user = ?"; update(conn, sql1, "AA"); // 模拟网络异常//System.out.println(10 / 0);Stringsql2="update user_table set balance = balance + 100 where user = ?"; update(conn, sql2, "BB"); // 4.若没有异常,则提交事务conn.commit(); } catch (Exceptione) { e.printStackTrace(); // 5.若有异常,则回滚事务try { conn.rollback(); } catch (SQLExceptione1) { e1.printStackTrace(); } } finally { try { //6.恢复每次DML操作的自动提交功能conn.setAutoCommit(true); } catch (SQLExceptione) { e.printStackTrace(); } //7.关闭连接JDBCUtils.closeResource(conn, null, null); } }
其中,对数据库操作的方法为:
//使用事务以后的通用的增删改操作publicvoidupdate(Connectionconn ,Stringsql, Object... args) { PreparedStatementps=null; try { // 1.获取PreparedStatement的实例 (或:预编译sql语句)ps=conn.prepareStatement(sql); // 2.填充占位符for (inti=0; i<args.length; i++) { ps.setObject(i+1, args[i]); } // 3.执行sql语句ps.execute(); } catch (Exceptione) { e.printStackTrace(); } finally { // 4.关闭资源JDBCUtils.closeResource(null, ps); } }
注:为了在出现故障时,数据能回滚到上一次提交之前,所以必须保持同一个事务的多个操作必须在同一个连接下,即让Connection连接从外传进来,方法内不关闭连接,当所有事务都执行完时,再关闭连接。需要关闭自动提交,改为手动提交
3. 事务的ACID属性
- 原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 - 一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。 - 隔离性(Isolation)
事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。 - 持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。
4. 数据库的并发问题
对于同时运行的多个事务, 当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制, 就会导致各种并发问题:
- 脏读: 对于两个事务 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段。之后, 若 T2 回滚, T1读取的内容就是临时且无效的。
- 不可重复读: 对于两个事务T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段。之后, T1再次读取同一个字段, 值就不同了。
- 幻读: 对于两个事务T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行。之后, 如果 T1 再次读取同一个表, 就会多出几行。
数据库事务的隔离性: 数据库系统必须具有隔离并发运行各个事务的能力, 使它们不会相互影响, 避免各种并发问题。
一个事务与其他事务隔离的程度称为隔离级别。数据库规定了多种事务隔离级别, 不同隔离级别对应不同的干扰程度, 隔离级别越高, 数据一致性就越好, 但并发性越弱。
5. 四种隔离级别
- 数据库提供的4种事务隔离级别:
- Oracle 支持的 2 种事务隔离级别:READ COMMITED, SERIALIZABLE。 Oracle 默认的事务隔离级别为: READ COMMITED 。
- Mysql 支持 4 种事务隔离级别。Mysql 默认的事务隔离级别为: REPEATABLE READ。
6. 在MySql中设置隔离级别
- 每启动一个 mysql 程序, 就会获得一个单独的数据库连接. 每个数据库连接都有一个全局变量 @@tx_isolation, 表示当前的事务隔离级别。
- 查看当前的隔离级别:
SELECT ;
- 设置当前 mySQL 连接的隔离级别:
settransactionisolationlevelreadcommitted;
- 设置数据库系统的全局的隔离级别:
setglobaltransactionisolationlevelreadcommitted;
- 补充操作:
- 创建mysql数据库用户:
createusertomidentifiedby'abc123';
- 授予权限
#授予通过网络方式登录的tom用户,对所有库所有表的全部权限,密码设为abc123. grantallprivilegeson*.*totom'%'identifiedby'abc123'; #给tom用户使用本地命令行方式,授予atguigudb这个库下的所有表的插删改查的权限。grantselect,insert,delete,updateonatguigudb.*totomidentifiedby'abc123';
五、数据库连接池☆☆☆
1. 数据库连接池技术
- 数据库连接池的基本思想:就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。
- 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
- 数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数来设定的。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。
- JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource 只是一个接口,该接口通常由服务器(Weblogic, WebSphere, Tomcat)提供实现
- DataSource 通常被称为数据源,它包含连接池和连接池管理两个部分,习惯上也经常把 DataSource 称为连接池
- DataSource用来取代DriverManager来获取Connection,获取速度快,同时可以大幅度提高数据库访问速度。
注: 数据源和数据库连接不同,数据源无需创建多个,它是产生数据库连接的工厂,因此整个应用只需要一个数据源即可。 当数据库访问结束后,程序还是像以前一样关闭数据库连接:conn.close(); 但conn.close()并没有关闭数据库的物理连接,它仅仅把数据库连接释放,归还给了数据库连接池。
2. Druid(德鲁伊)连接池(☆☆☆☆☆)
将Druid驱动的jar包拷贝到lib目录下
在驱动jar上右键–>【Build Path】–>【Add to Build Path】
Druid是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0、DBCP、Proxool等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池,可以说是目前最好的连接池之一。
package com.atguigu.druid; import java.sql.Connection; import java.util.Properties; import javax.sql.DataSource; import com.alibaba.druid.pool.DruidDataSourceFactory; public class TestDruid { public static void main(String[] args) throws Exception { InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties"); Properties pro = new Properties(); pro.load(is); DataSource ds = DruidDataSourceFactory.createDataSource(pro); Connection conn = ds.getConnection(); System.out.println(conn); } }
其中,src下的配置文件为:【druid.properties】
url=jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8 username=root password=123456 driverClassName=com.mysql.cj.jdbc.Driver
- 详细配置参数:
配置 | 缺省 | 说明 |
name | 配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:”DataSource-” + System.identityHashCode(this) |
url | 连接数据库的url,不同数据库不一样。例如:mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto |
username | 连接数据库的用户名 |
password | 连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里:https://github.com/alibaba/druid/wiki/使用ConfigFilter |
driverClassName | 根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置下) |
initialSize | 0 | 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 |
maxActive | 8 | 最大连接池数量 |
maxIdle | 8 | 已经不再使用,配置了也没效果 |
minIdle | 最小连接池数量 |
maxWait | 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 |
poolPreparedStatements | false | 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。 |
maxOpenPreparedStatements | -1 | 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100 |
validationQuery | 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。 |
testOnBorrow | true | 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 |
testOnReturn | false | 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 |
testWhileIdle | false | 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 |
timeBetweenEvictionRunsMillis | 有两个含义: 1)Destroy线程会检测连接的间隔时间2)testWhileIdle的判断依据,详细看testWhileIdle属性的说明 |
numTestsPerEvictionRun | 不再使用,一个DruidDataSource只支持一个EvictionRun |
minEvictableIdleTimeMillis | ||
connectionInitSqls | 物理连接初始化的时候执行的sql |
exceptionSorter | 根据dbType自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接 |
filters | 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall |
proxyFilters | 类型是List,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系 |
六、Apache-DBUtils实现CRUD操作☆☆☆
1. Apache-DBUtils简介
- commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。
- API介绍:
- API包说明:
在驱动jar上右键–>【Build Path】–>【Add to Build Path】
2. 主要API的使用
DbUtils
DbUtils :提供如关闭连接、装载JDBC驱动程序等常规工作的工具类,里面的所有方法都是静态的。主要方法如下:
- public static void close(…) throws java.sql.SQLException: DbUtils类提供了三个重载的关闭方法。这些方法检查所提供的参数是不是NULL,如果不是的话,它们就关闭Connection、Statement和ResultSet。
- public static void closeQuietly(…): 这一类方法不仅能在Connection、Statement和ResultSet为NULL情况下避免关闭,还能隐藏一些在程序中抛出的SQLEeception。
- public static void commitAndClose(Connection conn)throws SQLException: 用来提交连接的事务,然后关闭连接
- public static void commitAndCloseQuietly(Connection conn): 用来提交连接,然后关闭连接,并且在关闭连接时不抛出SQL异常。
- public static void rollback(Connection conn)throws SQLException:允许conn为null,因为方法内部做了判断
- public static void rollbackAndClose(Connection conn)throws SQLException
- rollbackAndCloseQuietly(Connection)
- public static boolean loadDriver(java.lang.String driverClassName):这一方装载并注册JDBC驱动程序,如果成功就返回true。使用该方法,你不需要捕捉这个异常ClassNotFoundException。
QueryRunner类
- 该类简单化了SQL查询,它与ResultSetHandler组合在一起使用可以完成大部分的数据库操作,能够大大减少编码量。
- QueryRunner类提供了两个构造器:
- 默认的构造器
- 需要一个 javax.sql.DataSource 来作参数的构造器
- QueryRunner类的主要方法:
- 更新
- public int update(Connection conn, String sql, Object… params) throws SQLException:用来执行一个更新(插入、更新或删除)操作。
- …
- 插入
- public T insert(Connection conn,String sql,ResultSetHandler rsh, Object… params) throws SQLException:只支持INSERT语句,其中 rsh - The handler used to create the result object from the ResultSet of auto-generated keys. 返回值: An object generated by the handler.即自动生成的键值
- 批处理
- public int[] batch(Connection conn,String sql,Object[][] params)throws SQLException: INSERT, UPDATE, or DELETE语句
- public T insertBatch(Connection conn,String sql,ResultSetHandler rsh,Object[][] params)throws SQLException:只支持INSERT语句
- …
- 查询
- public Object query(Connection conn, String sql, ResultSetHandler rsh,Object… params) throws SQLException:执行一个查询操作,在这个查询中,对象数组中的每个元素值被用来作为查询语句的置换参数。该方法会自行处理 PreparedStatement 和 ResultSet 的创建和关闭。
- …
示例
packagejdbc; importjava.io.IOException; importjava.io.InputStream; importjava.sql.Connection; importjava.sql.SQLException; importjava.util.List; importjava.util.Properties; importjavax.sql.DataSource; importorg.apache.commons.dbutils.DbUtils; importorg.apache.commons.dbutils.QueryRunner; importorg.apache.commons.dbutils.handlers.BeanHandler; importorg.apache.commons.dbutils.handlers.BeanListHandler; importorg.junit.Test; importcom.alibaba.druid.pool.DruidDataSource; importcom.alibaba.druid.pool.DruidDataSourceFactory; publicclassQueryRunnerTest { // 创建Druid连接池privatestaticDataSourcesource=null; static { try { InputStreamis=ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties"); Propertiespros=newProperties(); pros.load(is); source=DruidDataSourceFactory.createDataSource(pros); } catch (Exceptione) { e.printStackTrace(); } } // 测试添加publicvoidtestInsert() { Connectioncon=null; try { // 获取连接池连接con=source.getConnection(); Stringsql="insert into customers(name,email,birth) values(?,?,?)"; // 调用QueryRunner提供的update方法,返回修改了几条数据QueryRunnerrunner=newQueryRunner(); intupdate=runner.update(con, sql, "迪迦", "迪迦@qq.com", "2020-04-18"); } catch (SQLExceptione) { e.printStackTrace(); } finally { // 调用DbUtils类关闭释放连接DbUtils.closeQuietly(con); } } // 测试删除publicvoidtestdelete() { Connectioncon=null; try { // 获取连接池连接con=source.getConnection(); Stringsql="delete from customers where id=?"; // 调用QueryRunner提供的update方法,返回修改了几条数据QueryRunnerrunner=newQueryRunner(); intupdate=runner.update(con, sql, 21); } catch (SQLExceptione) { e.printStackTrace(); } finally { // 调用DbUtils类关闭释放连接DbUtils.closeQuietly(con); } } }
ResultSetHandler接口及实现类
- 该接口用于处理 java.sql.ResultSet,将数据按要求转换为另一种形式。
- ResultSetHandler 接口提供了一个单独的方法:Object handle (java.sql.ResultSet .rs)。
- 接口的主要实现类:
- ArrayHandler:把结果集中的第一行数据转成对象数组。
- ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中。
- **BeanHandler:**将结果集中的第一行数据封装到一个对应的JavaBean实例中。
- **BeanListHandler:**将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
- ColumnListHandler:将结果集中某一列的数据存放到List中。
- KeyedHandler(name):将结果集中的每一行数据都封装到一个Map里,再把这些map再存到一个map里,其key为指定的key。
- **MapHandler:**将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
- **MapListHandler:**将结果集中的每一行数据都封装到一个Map里,然后再存放到List
- **ScalarHandler:**查询单个值对象
packagejdbc; importjava.io.IOException; importjava.io.InputStream; importjava.sql.Connection; importjava.sql.Date; importjava.sql.ResultSet; importjava.sql.SQLException; importjava.util.Iterator; importjava.util.List; importjava.util.Properties; importjavax.sql.DataSource; importorg.apache.commons.dbutils.DbUtils; importorg.apache.commons.dbutils.QueryRunner; importorg.apache.commons.dbutils.ResultSetHandler; importorg.apache.commons.dbutils.handlers.BeanHandler; importorg.apache.commons.dbutils.handlers.BeanListHandler; importorg.apache.commons.dbutils.handlers.ScalarHandler; importorg.junit.Test; importcom.alibaba.druid.pool.DruidDataSource; importcom.alibaba.druid.pool.DruidDataSourceFactory; publicclassQueryRunnerTest { // 创建Druid连接池privatestaticDataSourcesource=null; static { try { InputStreamis=ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties"); Propertiespros=newProperties(); pros.load(is); source=DruidDataSourceFactory.createDataSource(pros); } catch (Exceptione) { e.printStackTrace(); } } // 测试返回一条数据publicvoidtestQueryInstance() { Connectioncon=null; try { // 获取线程池连接con=source.getConnection(); Stringsql="select id,name,email,birth from customers where id=?"; // 将结果集中的第一行数据封装到一个对应的JavaBean实例BeanHandler<Customers>bh=newBeanHandler<Customers>(Customers.class); // 调用QueryRunner类的query()方法,返回结果QueryRunnerrunner=newQueryRunner(); Customerscustomers=runner.query(con, sql, bh, 2); } catch (Exceptione) { e.printStackTrace(); } finally { DbUtils.closeQuietly(con); } } // 测试返回多个对象publicvoidtestQueryInstance1() { Connectioncon=null; try { // 获取线程池连接con=source.getConnection(); Stringsql="select * from user"; // 将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里BeanListHandler<User>bh=newBeanListHandler<User>(User.class); // 调用QueryRunner类的query()方法,返回结果的集合QueryRunnerrunner=newQueryRunner(); List<User>list=runner.query(con, sql, bh); } catch (Exceptione) { e.printStackTrace(); } finally { DbUtils.closeQuietly(con); } } // 测试自定义ResultSetHandler的实现类publicvoidtestQueryInstance2() { // 实现ResultSetHandler的内部接口ResultSetHandler<Customers>rsh=newResultSetHandler<Customers>() { publicCustomershandle(ResultSetarg0) throwsSQLException { if (arg0.next()) { intid=arg0.getInt("id"); Stringname=arg0.getString("name"); Stringemail=arg0.getString("email"); Datebirth=arg0.getDate("birth"); // 结果返回returnnewCustomers(id, name, email, birth); } returnnull; } }; Connectioncon=null; try { // 获取Druid连接池连接con=source.getConnection(); Stringsql="select id,name,email,birth from customers where id=?"; QueryRunnerrunner=newQueryRunner(); Customerscustomers=runner.query(con, sql, rsh, 2); System.out.println(customers); } catch (SQLExceptione) { e.printStackTrace(); } finally { DbUtils.closeQuietly(con); } } // ScalarHandler类 查询特殊的数据publicvoidtestQueryValue() { Connectioncon=null; try { con=source.getConnection(); QueryRunnerrunner=newQueryRunner(); // 测试一/** String sql = "select count(*) from customers"; ScalarHandler sh = new* ScalarHandler(); long count = (long) runner.query(con, sql, sh);* System.out.println(count);*/// 测试二:Stringsql="select max(birth) from customers"; ScalarHandlerhandler=newScalarHandler(); Datebirth= (Date) runner.query(con, sql, handler); System.out.println(birth); } catch (SQLExceptione) { e.printStackTrace(); } finally { DbUtils.closeQuietly(con); } } }
JDBC总结
总结publicvoidtestUpdateWithTx() { Connectionconn=null; try { /** 获取数据库连接两种方法* ①手写的连接:JDBCUtils.getConnection(); * ②数据库连接池:Druid*//**对数据表进行一系列CRUD操作的两种方法* ①自写PreparedStatement实现通用的增删改、查询操作 * ②使用dbutils提供的jar包中提供的QueryRunner类*///提交数据conn.commit(); } catch (Exceptione) { e.printStackTrace(); try { //回滚数据conn.rollback(); } catch (SQLExceptione1) { e1.printStackTrace(); } }finally{ //3.关闭连接等操作//① JDBCUtils.closeResource();//② 使用dbutils提供的jar包中提供的DbUtils类提供了关闭的相关操作 } }