Web07:JDBC&连接池技术&DBUtil

写了一下午,刚刚写到自己昨天学的东西。

JDBC是什么?

就是Java程序与数据库连接的一个桥梁!

首先,连接JDBC需要一些jar包,这里有连接JDBC用的全部工具


提取码:ytqx 
 

这里默认大家会JUnit测试,会导入Jar包,不会的去学习一下,很快!

原始的JDBC

public class TestJDBC {
	@Test
	public void testLogin(){
		try {
			this.login("张三",1);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void login(String username,int uid) throws ClassNotFoundException, SQLException {
		// TODO Auto-generated method stub
		//加载驱动
		Class.forName("com.mysql.jdbc.Driver");
		//获得连接
		Connection conc = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/web02","root","12345");
		//sql语句
		String sql = " select * from user where uid = ?";
		//进行预处理
		PreparedStatement ps = conc.prepareStatement(sql);
		ps.setInt(1, uid);
		//执行语句
		ResultSet rs = ps.executeQuery();
		while(rs.next()){
			int id = rs.getInt(1);
			String name = rs.getString(2);
			System.out.println("id:"+id+" 姓名:"+name);
			
		}
		rs.close();
		ps.close();
		conc.close();
	}
}

老生常谈一下,原始的JDBC坏处,网上都有,就是什么硬编码啊之类的。比如上边代码中,我们使用的是端口3306,web02数据库。万一那天我们换了端口呢?换了数据库名字呢?换了用户,换了密码呢?程序已经写完了,交给了用户。你总不能在对用户说,我把软件拿回来,在反编译一下,改改东西吧!因此我们需要改进!

带配置文件的JDBC

有许多我们需要改动的东西,那我们怎么办呢?我们可以通过配置文件来为其传值。

同时,在上面的代码中,我们每次书写代码都会有许多重复的部分。因此接下来我们把加载驱动、获得连接与释放资源的代码提出来。

我们把需要加载的驱动名,数据库url,用户名,密码放到配置文件中,只需修改配置文件就可以了

先看一下项目结构

我们把配置文件放到src路径下,定义了一个test包,一个utils包,lib文件夹存放jar文件。

配置文件

db.properties

使用配置文件有两种方法:

方法一:ResourceBundle加载

JDBCUtils.java(获得连接类)

public class JDBCUtils {
	private static String driver;
	private static String url;
	private static String username;
	private static String password;
	//加载配置文件信息
	static{
		ResourceBundle rb = ResourceBundle.getBundle("db");
		driver = rb.getString("driver");
		url = rb.getString("url");
		username = rb.getString("username");
		password = rb.getString("password");
	}
	
	//获得连接
	public static Connection getConection(){
		Connection conc = null;
		try {
			//加载驱动
			Class.forName(driver);
			//获得连接
			conc = DriverManager.getConnection(url,username,password);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return conc;
	}
	//释放资源
	public static void release(Connection conc,PreparedStatement ps,ResultSet rs){
		if(rs!=null){
			try {
				rs.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if(ps!=null){
			try {
				ps.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if(conc!=null){
			try {
				conc.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	}
}

方法二:ClassLoader加载

public class JDBCUtil2 {
	private static String driver;
	private static String url;
	private static String username;
	private static String password;
	//静态代码块导入配置文件
	static{
		//类加载器导入配置文件
		ClassLoader classLoader = JDBCUtil2.class.getClassLoader();
		InputStream resourceAsStream = classLoader.getResourceAsStream("db.properties");
		//Properties类加载配置文件
		Properties properties = new Properties();
		try {
			//导入 
			properties.load(resourceAsStream);
			driver = properties.getProperty("driver");
			url = properties.getProperty("url");
			username = properties.getProperty("username");
			password = properties.getProperty("password");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public static Connection getConnection(){
		Connection conn = null;
		try {
			Class.forName(driver);
			conn = DriverManager.getConnection(url,username,password);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return conn;
	}
}

 

测试

TestUtils .java (测试类)

public class TestUtils {
	@Test
	public void jdbcQuery() {
		// TODO Auto-generated method stub
		String sql = "select * from user ";
//获得连接方法调用
		Connection conection = JDBCUtils.getConection();
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			ps = conection.prepareStatement(sql);
			rs = ps.executeQuery();
//循环获得返回值
			while(rs.next()){
				int id = rs.getInt(1);
				String name = rs.getString(2);
				int age = rs.getInt(3);
				System.out.println("id: "+id+" name: "+name+" age "+age);
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
//释放资源
			JDBCUtils.release(conection, ps, rs);
		}
	}
}

两种加载方法的测试其实更换一下调用的静态方法就可以了。

加上连接池技术的JDBC

上面的代码中,我们虽然带了配置文件,进行了解耦,但是连接数据库中开销最大其实是数据库的连接与释放资源。

从上边的代码还看不出来,但是如果有许多人许多人许多人来不断的连接与释放资源呢?估计服务器一会就完了。因此我们需要引入连接池技术。就是建立一个池子,每次连接用完了就放到池子里,谁用谁拿,用完返回来,这样就不用不断建立,不断释放了。

自定义连接池

首先,我们先写一个自己自定义的连接池。

实现连接池其实需要我们实现一个接口:DataSource

但是DataSource这个接口需要实现的方法太多,我们这里只实现一个getConnection方法,然后自己写一个backConnection方法.其他的方法不去实现。

只贴出实现的方法:

public class JDBCUtils implements DataSource {
	private static String driver;
	private static String url;
	private static String username;
	private static String password;
	private static LinkedList<Connection> pool;
	static {
		//类加载
		ClassLoader classLoader = JDBCUtils.class.getClassLoader();
		InputStream is = classLoader.getResourceAsStream("db.properties");
		//properties导入
		Properties prop = new Properties();
		try {
			prop.load(is);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		driver = prop.getProperty("driver");
		url = prop.getProperty("url");
		username = prop.getProperty("username");
		password = prop.getProperty("password");
		
		pool = new LinkedList<Connection>();
		//为连接池添加五个连接
		try {
			Class.forName(driver);
			for(int i=0 ;i < 5 ;i++ ){
				Connection conn = DriverManager.getConnection(url,username,password);
				pool.add(conn);
			}
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e){
			e.printStackTrace();
		}
		
	}
	@Override
	public Connection getConnection(){
		// TODO Auto-generated method stub
		Connection conn = null;
//判断是否还有可用的连接,如若没有就创建几个
		if(pool.size()==0){
			for(int i = 0;i < 3;i++){
				try {
					conn = DriverManager.getConnection(url,username,password);
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				pool.add(conn);
			}
		}
		System.out.println("可连接总数:"+pool.size());
		conn = pool.remove(0);
		System.out.println("可连接总数:"+pool.size());
		return conn;
	}
	/**
	 * 连接归还连接池
	 * */
	public void backConnection(Connection conn) {
		// TODO Auto-generated method stub
		try {
			pool.add(conn);
			System.out.println("已经归还到连接池!连接池可连接数:"+pool.size());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			new RuntimeException();
		}
	}
	.....一堆没写的没实现的方法...
}

上方的代码有个缺陷就是close()方法未被考虑到,我们应该将close()方法也改成放到连接池里,即跟上方的backConnection()方法相同。那我们要去哪里修改呢?当然是去close()方法的类里了,因此需要写一个自定义的Connection类。

而自定义Connection需要实现Connection这个接口

public class MyConnection implements Connection {
	/**
	 * conn:需要哪个连接回到连接池
	 * pool:回到哪个连接池
	 */
	Connection conn = null;
	LinkedList<Connection> pool = null;
	public MyConnection(Connection conn,LinkedList<Connection> pool) {
		// TODO Auto-generated constructor stub
		this.conn = conn;
		this.pool = pool;
	}
	
	/* (non-Javadoc)
	 * @see java.sql.Connection#close()
	 */
	@Override
	public void close() throws SQLException {
		// TODO Auto-generated method stub
		pool.add(conn);
	}
	/* (non-Javadoc)
	 * @see java.sql.Connection#prepareStatement(java.lang.String)
	 */
//这里需要更改
	@Override
	public PreparedStatement prepareStatement(String sql) throws SQLException {
		// TODO Auto-generated method stub
		return conn.prepareStatement(sql);
	}
       ..........许多没有实现的方法.........
}

其实上边的自定义连接就是设计模式中的装饰者模式.将一个对象拿进来加工后在去执行其接下来的工作。

C3P0连接池

在开始的百度网盘链接中有要使用到的xml和要使用的jar包文件,没错,注意,要导入一个jar包,建议大家存一份。要使用直接拷贝就行了,另外注意这个xml文件的名称是固定的

c3p0-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>

  <default-config>
    <property name="driverClass">com.mysql.jdbc.Driver</property>
	<property name="jdbcUrl">jdbc:mysql://localhost:3306/web03</property>
	<property name="user">root</property>
	<property name="password">12345</property>
	<property name="initialPoolSize">5</property>
	<property name="maxPoolSize">20</property>
  </default-config>
  
  <named-config name="my_c3p0"> 
    <property name="driverClass">com.mysql.jdbc.Driver</property>
	<property name="jdbcUrl">jdbc:mysql://localhost:3306/web02</property>
	<property name="user">root</property>
	<property name="password">12345</property>
  </named-config>
  

</c3p0-config>

上方的xml中其实是一份默认的default配置,一份自定义的my_c3p0配置,当然上边的两份配置其实就数据库名名称不一样.在下边的代码里,我们也两种配置都导入进来了,你可以使用默认的配置获得连接,也可以使用自定义的配置。

JDBCUtils3_c3p0.java

public class JDBCUtils3_c3p0 {
	//使用默认配置
	private static ComboPooledDataSource cp_default;
	//使用自定义配置
	private static ComboPooledDataSource cp_my;
	//初始化配置
	static{
		cp_default = new ComboPooledDataSource();
		cp_my = new ComboPooledDataSource("my_c3p0");
	}
	/**
	 * 获取数据源
	 * */
	public static DataSource getDataSource(){
		return cp_default;
	}
	/**
	 * 获取默认配置连接
	 * */
	public static Connection getDefaultConnection(){
		Connection conn = null;
		try {
			conn = cp_default.getConnection();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		return conn;
	}
	/**
	 * 获取自定义配置连接
	 * */
	public Connection getMyConnection() {
		Connection conn = null;
		try {
			conn = cp_my.getConnection();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return conn;
	}
}

DBCP连接池

相同的,上方的c3p0有配置文件,dbcp连接池也有配置文件,在开始的百度网盘链接中也有要使用到的dbcpconfig.properties文件和要使用的两个jar包,注意两个,建议大家存一份。

因为这个是properties文件所以我们要使用上面的ResourceBundle加载和ClassLoader加载配置文件这两种方式加载。

JDBCUtils4_dbcp.java

public class JDBCUtils4_dbcp {
	private static DataSource ds;
	static {
		InputStream is = JDBCUtils4_dbcp.class.getClassLoader()
				.getResourceAsStream("dbcpconfig.properties");
		Properties p = new Properties();
		try {
			p.load(is);
			ds = BasicDataSourceFactory.createDataSource(p);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			throw new RuntimeException();
		}
	}
	/**
	 * 获取数据源
	 * */
	public static DataSource getDataSource()
	{
		return ds;
	}
	/**
	 * 获取连接
	 * */
	public Connection getConnection(){
		try {
			return ds.getConnection();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			throw new RuntimeException();
		}
	}
}

下边是对于自定义连接池,c3p0,dbcp连接池的测试!

 

TestUtil.java 测试类

public class TestUtil {
	String test_sql = "select * from user";
	/**
	 * 测试自定义的连接池
	 * */
	@Test
	public void testQuery(){
		JDBCUtils ju = new JDBCUtils();
		Connection connection = ju.getConnection();
		ResultSet rs = null;
		try {
			PreparedStatement ps = connection.prepareStatement(test_sql);
			rs = ps.executeQuery();
			while(rs.next()){
				int id = rs.getInt(1);
				String name = rs.getString(2);
				int age = rs.getInt(3);
				System.out.println("id:"+id+" name:"+name+" age:"+age);
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//归还连接到连接池
		ju.backConnection(connection);
	}
	/**
	 * 测试加强了close()的自定义连接池
	 * */
	@Test
	public void testQuery1() {
		//获得连接
		JDBCUtils ju = new JDBCUtils();
		Connection conn = ju.getConnection();
		PreparedStatement ps = null;
		ResultSet rs = null;
		
		try {
			ps = conn.prepareStatement(test_sql);
			rs = ps.executeQuery();	
			while(rs.next()){
				int id = rs.getInt(1);
				String name = rs.getString(2);
				int age = rs.getInt(3);
				System.out.println("id:"+id+" name:"+name+" age:"+age);
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			try {
				rs.close();
				ps.close();
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	/**
	 * 测试c3p0连接池
	 * */
	@Test
	public void testQuery2(){
		
		JDBCUtils3_c3p0 juc = new JDBCUtils3_c3p0();
		Connection conn = juc.getDefaultConnection();
		try {
			PreparedStatement ps = conn.prepareStatement(test_sql);
			ResultSet rs = ps.executeQuery();
			while(rs.next()){
				int id = rs.getInt(1);
				String name = rs.getString(2);
				int age = rs.getInt(3);
				System.out.println("id:"+id+" name:"+name+" age:"+age);
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	/**
	 * 测试dbcp连接池
	 * */
	@Test
	public void testQuery3() {
		JDBCUtils4_dbcp jud = new JDBCUtils4_dbcp();
		//获得连接
		Connection conn = jud.getConnection();
		try {
			PreparedStatement ps = conn.prepareStatement(test_sql);
			ResultSet rs = ps.executeQuery();
			while(rs.next()){
				int id = rs.getInt(1);
				String name = rs.getString(2);
				int age = rs.getInt(3);
				System.out.println("id:"+id+" name:"+name+" age:"+age);
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}

终于到最后一个了,写的有点恶心了,写了一下午的博客,明明今晚上要玩会LOL的。结果都19:50了

DBUtil的使用

为啥要使用?还不是,程序员觉得敲得代码太多,想偷懒!通过这个,我们可以进一步减少那些CRUD的代码,就上边测试类中的那些代码。

我是受不了了!我要放假!我贴代码了!

一个要诀!:记住那个核心类QueryRunner

public class TestDBUtils {
	/**
	 * 使用DBUtil添加
	 * */
	@Test
	public void testUpdate(){
		JDBCUtils3_c3p0 juc = new JDBCUtils3_c3p0();
		//配置dbutil核心类
		QueryRunner qr = new QueryRunner(juc.getDataSource());
		//sql语句
		String sql = "insert into user value (?,?,?)";
		//参数数组
		Object[] param = {"smoke","7"};
		//运行命令
		try {
			int i = qr.update(sql, param);
			if(i>0){
				System.out.println("更新成功");
			}else{
				System.out.println("失败");
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	/**
	 * dbutil单个查询
	 * */
	@Test
	public void testQuery1() {
		//配置核心QueryRunner
		QueryRunner qr = new QueryRunner(JDBCUtils3_c3p0.getDataSource());
		
		//SQL语句
		
		String sql = "select * from user where uid=?";
		Object[] param = {"1"};
		try {
			User user = qr.query(sql,param, new BeanHandler<User>(User.class));
			System.out.println(user);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	/**
	 * dbutil全部查询
	 * */
	@Test
	public void testQuery2() {
		//配置核心QueryRunner
		QueryRunner qr = new QueryRunner(JDBCUtils3_c3p0.getDataSource());
		//SQL语句
		String sql = "select * from user";
		try {
			List<User> user = qr.query(sql, new BeanListHandler<User>(User.class));
			for(User u : user){
				System.out.println(u);
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值