在Dao层操作数据库时,需要频繁的得到数据库链接,所以自定义一个数据库的工具类JdbcUtils.java

有两种方案:

1、线程不安全的数据库工具类

2、线程安全的数据库工具类

代码如下

1、线程不安全的数据库工具类

db.properties

driver=com.mysql.jdbc.Driver
url=jdbc\:mysql\://localhost\:3306/test
username=root
password=mysqladmin
JdbcUtils.java

public class JdbcUtils{
	private static Properties config = new Properties();	//单例模式
	static{	//静态代码块,读取资源文件,加载驱动
		try{
			config.load(JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties"));	//静态代码块,读取配置文件
			
			Class.forName(config.getProperty("driver"));	//加载驱动
			
		}catch(Exception e){
			throw new ExceptionInInitializerError(e);
		}
	}
	
	public static Connection getConnection() throws SQLException{	//创建链接
		return DriverManager.getConnection(config.getProperty("url"),config.getProperty("username"),config.getProperty("password"));
	}
	
	public static void release(Connection conn,Statement st,ResultSet rs){	//释放资源
		if(rs != null){
			try{
				rs.close();
			}catch(Exception e){}
		}
		if(st != null){
			try{
				st.close();
			}catch(Exception e){}
		}
		if(conn != null){
			try{
				conn.close();
			}catch(Exception e){}
		}
	}
}
test.java

public User find(String id){
		Connectionconn = null;
		PreparedStatement st = null;
		ResultSet rs = null;
		try{
			conn = JdbcUtils.getConnection();	//创建链接,引用JdbcUtils工具包 
			String sql = "";	//其余步骤省略
		}catch(Exception e){
			throws new DaoException(e);
		}finally{
			Jdbcutils.release(conn,st,rs);	//释放资源
		}
	}


2、线程安全的数据库工具类

该类中用到了c3p0数据源和ThreadLocal类,也涉及到了mysql的事物开启与提交。ThreadLocal的作用:set(Object obj)将一个对象 “存入” 到当前线程, Object  get()方法将一个对象从当前线程中将对象取出来,所以我们可以将从c3p0中得到的数据库链接存入当前的线程中,也可以将数据库链接从当前线程中取出来

代码如下

c3p0-config.xml

<c3p0-config>
	<!-- c3p0-config.xml的默认配置,在classpath下-->
	<!-- 如果要使用自己的配置,需要该name属性 -->
	<default-config>
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property>
		<property name="user">root</property>
		<property name="password">mysqladmin</property>
		
		<property name="initialPoolSize">10</property>
		<property name="maxIdleTime">30</property>
		<property name="maxPoolSize">20</property>
		<property name="minPoolSize">5</property>
		<property name="maxStatements">200</property>
	</default-config>
	
	<!-- 自己的配置 -->
	<named-config name="mysql">
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property>
		<property name="user">root</property>
		<property name="password">mysqladmin</property>
		
		<property name="acquireIncrement">50</property>
		<property name="initialPoolSize">100</property>
		<property name="minPoolSize">50</property>
		<property name="maxPoolSize">1000</property><!-- intergalactoApp adopts a different approach to configuring statement caching -->
		<property name="maxStatements">0</property>
		<property name="maxStatementsPerConnection">5</property>
	</named-config>
	
	<named-config name="oracle">
		<property name="acquireIncrement">50</property>
		<property name="initialPoolSize">100</property>
		<property name="minPoolSize">50</property>
		<property name="maxPoolSize">1000</property><!-- intergalactoApp adopts a different approach to configuring statement caching -->
		<property name="maxStatements">0</property>
		<property name="maxStatementsPerConnection">5</property>
	</named-config>
</c3p0-config>

JdbcUtils.java

public class DbUtils {
	private static ComboPooledDataSource ds = null;	//c3p0数据源
	/*
	用于存放当前线程的connection
	所以在当前线程下,无论在哪或的的connection都是同一个connection
	在事物上安全
	*/
	private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
	static{
		try{
			ds = new ComboPooledDataSource();	//创建数据源
		}catch (Exception e) {
			throw new ExceptionInInitializerError(e);
		}
		
	}
	public static ComboPooledDataSource getDataSource(){	//获得数据源
		return ds;
	}
	public static Connection getConnection() throws SQLException{	//获得数据库链接
		try{
			Connection conn = tl.get();	//从当前线程获取链接,是同一个connection,所以线程安全
			if(conn == null){
				conn = ds.getConnection();	//从连接池获得链接
				tl.set(conn);	//当前线程,所以线程安全
			}
			return conn;
		}catch (Exception e) {
			throw new RuntimeException(e);
		}
		
	}
	public static void startTransaction(){	//开启事物
		try{
			Connection conn = tl.get();	//从当前线程获取链接,所以线程安全
			if(conn == null){
				conn = ds.getConnection();
				tl.set(conn);
			}
			conn.setAutoCommit(false);
		}catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	public static void commitTransaction(){	//提交事物
		try{
			Connection conn = tl.get();	//从当前线程获取链接,所以线程安全
			if(conn != null){
				conn.commit();
			}
		}catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	public static void closeConnection(){	//关闭链接
		try{
			Connection conn = tl.get();
			if(conn != null){
				conn.close();
			}
		}catch (Exception e) {
			// TODO: handle exception
		}finally{
			tl.remove();//千万注意,解除当前线程上绑定的链接(从threadlocal容器中移除对应当前线程的链接)
		}
	}
	public static void release(Connection conn,PreparedStatement ps,ResultSet rs){	//释放资源
		if(rs != null){
			try{
				rs.close();
			}catch (Exception e) {
				// TODO: handle exception
			}
		}
		if(ps != null){
			try{
				ps.close();
			}catch (Exception e) {
				// TODO: handle exception
			}
		}
		if(conn != null){
			try{
				conn.close();
			}catch (Exception e) {
				// TODO: handle exception
			}
		}
	}
}

test.java

	@Test
	//转账测试,多条sql语句在同一个connection下执行
	public void transfer() throws SQLException{
		Connection conn = null;
		conn = DbUtils.getConnection();	//从当前线程得到Connection
		conn.setAutoCommit(false);	//开启事物
		QueryRunner qr = new QueryRunner();
		String sql1 = "update account set money=money-100 where name='aaa'";
		qr.update(conn, sql1);	//会回滚
	//	int i = 1/0;	//抛异常
		String sql2 = "update account set money=money+100 where name='bbb'";
		qr.update(conn, sql2);
		conn.commit();	//提交事物
	}




Logo

更多推荐