数据库:JDBC+DBCP

JDBC:

Java Database Connectivity。

重要接口:

Connection:用于保持和关闭JDBC的连接。
Statement:用于将SQL语句发送到数据库。
PreparedStatement:Statement的子接口,预编译SQL。
ResultSet:结果集,用于接收数据库的返回数据。

特点:

为几乎所有主流关系型数据库提供的统一API接口,这些不同的DBMS使用的都是同样的JDBC API,代码都是相同的,避免了程序员在基于不同的DBMS进行编程时可能遇到的困难。JDBC最大的特点在于它不受数据库供应商的限制,而数据库供应商主动提供基于JDBC API的资源包(驱动)。

使用:

将数据库供应商提供的驱动导入项目,即可开始数据库开发。

packages:

mysql-connector-java-5.x.x-bin.jar(mysql 5)
ojdbc14_g.jar(oracle 10g)

步骤:

1.导包:

这个不用多说,使用JDBC驱动前务必保证其已经在classpath中。

2.如有配置,则加载配置:

比较适合JDBC配置的形式是properties的键值对,mysql JDBC配置的格式为:
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://[主机名或ip地址]/[数据库名]?useUnicode=[是否使用Unicode编码:true/false]&characterEncoding=[字符集]
userName=[用户名]
password=[密码]
实际例子:
//DBConfig.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost/MyDatabase?useUnicode=true&characterEncoding=UTF-8
userName=root
password=123456
oracle JDBC配置的格式为:
driverClassName=Oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@[主机名或ip地址]:[端口,默认为1521]:[数据库名]
userName=[用户名]
password=[密码]
实际例子:
//DBConfig.properties
driverClassName=Oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@192.168.8.1:1521:yuewei
userName=root
password=123456
配置了之后,千万不要忘了读取,以备后面的步骤使用。Properties的操作十分简单,就不细说了。
//DBUtil.java
private static String driverClassName;
private static String url;
private static String userName;
private static String password;
public static void loadDBConfig(String filePath){
	Properties properties = new Properties();
	try {
		properties.load(new FileInputStream(new File(filePath)));
	} catch (FileNotFoundException e) {
		e.printStackTrace();
	} catch (IOException e) {
		e.printStackTrace();
	}
	driverClassName = (String) properties.get("driverClassName");
	url = properties.getProperty("url");
	userName = properties.getProperty("userName");
	password = properties.getProperty("password");
}
当然也可以不使用配置,直接写在代码里,但这样做将导致更新困难,极不推荐。

3.加载驱动

在这一步中,JDBC利用了java的反射机制,利用驱动的类名动态加载驱动。
注意,这个驱动加载只需要在启动时加载一次,驱动就会一直装载在JVM中。
Class.forName(driverClassName);
对于mysql,driverClassName为com.mysql.jdbc.Driver。
对于oracle,driverClassName为oracle.jdbc.OracleDriver。
加载驱动时可能产生ClassNotFoundException,需要捕捉。
另外,JDK6以后,甚至都不需要使用Class.forName();方法了,只需放在classpath下就会自动加载。

4.创建连接对象

利用驱动管理器类的静态方法getConnection即可获得与数据库的连接。
Connection connection = DriverManager.getConnection(url, userName, password);
这一步就相当于建立对数据库的连接了。
创建连接对象时可能产生SQLException,需要捕捉。

5.创建Statement对象

Statement statement = connection.createStatement();
Statement对象由连接对象创建,主要利用建立起的连接执行静态SQL语句,并能返回影响的行数或结果集(ResultSet)对象。
创建Statement对象时可能产生SQLException,需要捕捉。

6.发起SQL请求

利用Statement对象执行静态SQL语句。
发起SQL请求时可能产生SQLException,需要捕捉。
6.1.增删改
对于增删改,将返回一个整型值,代表的是受增删改操作影响的行数,调用者可以利用此。
//DBUtil.java
int rowAffected = statement.executeUpdate(sql);
6.2.查询
对于查询,将返回一个结果集,调用者需要接收并解析结果集。
//DBUtil.java
ResultSet resultSet = statement.executeQuery(sql);


7.关闭连接资源

主要是关闭三个资源:Connection、Statement(或PreparedStatement,下文中将介绍)、ResultSet,方法均为close();。特别是Connection,如不及时关闭,面对大量连接时极有可能造成内存溢出。
关闭时最好遵循按顺序关闭,先后顺序为ResultSet对象、Statement对象、Connection对象。
关闭时可能产生SQLException异常,需要捕捉。
try {
	resultSet.close();
	statement.close();
	connection.close();
} catch (SQLException e) {
	e.printStackTrace();
}
还是不够严谨,再加一层判断:
try {
	if(resultSet!=null){
		resultSet.close();
	if(statement!=null)
		statement.close();
	if(connection!=null)
		connection.close();
} catch (SQLException e) {
	e.printStackTrace();
}

回顾:

看完整代码前,先简要回顾一下步骤。
1.导包。
2.加载配置,包括驱动名、url、用户名、密码,或直接写在代码里。
3.加载驱动,JDK6+自动加载。
4.创建连接对象,Connection。
5.由连接对象创建Statement对象。
6.发起SQL连接请求。
对于增删改,executeUpdate,返回影响的行数。
对于查询,executeQuery,返回结果集ResultSet。
7.关闭各种对象,ResultSet,Statement,Connection。

实例:

下面来一个完整的实例吧。
/**
 * JDBC工具类
 * @author hsdsmljj
 *
 */
public class DBUtil {
	private static String driverClassName;
	private static String url;
	private static String userName;
	private static String password;
	public static void loadDBConfig(String filePath){
		//加载配置properties
		Properties properties = new Properties();
		try {
			properties.load(new FileInputStream(new File(filePath)));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		//读取配置
		driverClassName = (String) properties.get("driverClassName");
		url = properties.getProperty("url");
		userName = properties.getProperty("userName");
		password = properties.getProperty("password");
		//加载JDBC驱动
		try {
			Class.forName(driverClassName);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 建立数据库连接
	 * 
	 */
	public static Connection getConnection(){
		Connection connection = null;
		//利用DriverManager(驱动管理器)的静态方法建立连接
		try {
			connection = DriverManager.getConnection(url, userName, password);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return connection;
	}
	
	/**
	 * 执行增删改
	 * @param sql
	 * @return 影响的行数
	 */
	public static int update(String sql){
		//建立连接
		Connection connection = getConnection();
		Statement statement = null;
		int rowAffected = 0;
		try{
			//由连接对象创建Statement对象
			statement = connection.createStatement();
			//执行增删改,返回影响的行数
			rowAffected = statement.executeUpdate(sql);
			//自动关闭Statement及Connection对象
			close(statement, connection);
		}catch (SQLException e) {
			e.printStackTrace();
		}
		return rowAffected;
	}

	/**
	 * 执行查询
	 * @param sql
	 * @param statement
	 * @param connection
	 * @return 结果集
	 */
	public static ResultSet query(String sql, Statement statement, Connection connection){
<span style="white-space:pre">		</span>//预先准备好connection及statement
		ResultSet resultSet = null;
		try{
			//执行查询,返回结果集
			resultSet = statement.executeQuery(sql);
			//不能在此处关闭Statement及Connection对象,需要等result set读取使用完毕再依次关闭
			//close(statement, connection);
		}catch (SQLException e) {
			e.printStackTrace();
		}
		return resultSet;
	}
	
	/**
	 * 关闭ResultSet对象
	 * @param resultSet
	 */
	public static void closeResultSet(ResultSet resultSet){
		try{
			if(resultSet!=null)
				resultSet.close();
		}catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 关闭Statement及Connection对象
	 * @param resultSet
	 */
	public static void close(Statement statement, Connection connection){
		try{
			if(statement!=null)
				statement.close();
			if(connection!=null)
				connection.close();
		}catch (SQLException e) {
			e.printStackTrace();
		}
	}

}

/**
* JDBC工具类DBUtil的测试类
* @author hsdsmljj
*/
public static void main(String[] args) {
	//载入配置
	DBUtil.loadDBConfig("conf/DBConfig.properties");
	String querySql = "SELECT * FROM sometable";
	String insertSql = "INSERT INTO sometable VALUES('somenewvalue')";
	String updateSql = "UPDATE sometable SET value='someothervalue'";
	String deleteSql = "DELETE FROM sometable WHERE value='someothervalue'";
	try {
		Connection connection = DBUtil.getConnection();
		Statement statement = connection.createStatement();
		//查询,返回结果集
		ResultSet resultSet = DBUtil.query(querySql, statement, connection);
		//解析结果集,使用ResultSet类的next()方法迭代移动游标
		while(resultSet.next()){
			//使用ResultSet类的getInt或getString等方法获取每一行的各个属性值
			//也可以用列的位置(int)来获取,位置索引从1开始
			String value = resultSet.getString("value");
			//对数据进行处理,组装成对象等
			...
		}
		//关闭结果集等对象
		DBUtil.closeResultSet(resultSet);
		DBUtil.close(statement, connection);
	} catch (SQLException e) {
		e.printStackTrace();
	}
	//执行增删改,自动关闭各种对象
	int affectedRows1 = DBUtil.update(insertSql);
	int affectedRows2 = DBUtil.update(updateSql);
	int affectedRows3 = DBUtil.update(deleteSql);		
}


预编译:

java中的PreparedStatement 接口继承了Statement,并与之在两方面有所不同:有人主张,在JDBC应用中,如果你已经是稍有水平开发者,你就应该始终以PreparedStatement代替Statement。也就是说,在任何时候都不要使用Statement。                                     

---百度百科《PreparedStatement》词条

既然度娘都这么说了,那我等自然就要用“稍有水平开发者”的水准来要求自己,不是嘛?

数据库引擎执行SQL语句的大致过程是:
1.Sql分析,语法检测。
2.解析编译。
3.绑定数据。
4.安排计划。
5.执行(commit)。
6.返回数据。
其中1-2占的时间较大,预编译能在一次性执行大量SQL时让引擎只需检测、编译一次,故节省时间。

在java中,要使用预编译,需要修改上述步骤的第五到第六步。

创建PreparedStatement预编译执行器对象:

在这一步,数据先不填,比如insert语句的value,或update语句的set 字段的值,用问号代替。

String sql = "insert into sometable values(?,?)"; //PreparedStatement中的字符串参数不能加引号!
PreparedStatement preStat = conn.prepareStatement(sql);

绑定数据并执行:

先来十万条。

int affectedRows = 0;
for(int i=1;i<100000;i++){
	preStat.setInt(1,i); //参数下标从1开始
	preStat.setString(2,"value"+i);
	affectedRows += preStat.executeUpdate();
}

或者加批处理,风味更佳(可选):

for(int i=1;i<100000;i++){
	preStat.setInt(1,i);
	preStat.setString(2,"value"+i);
	preStat.addBatch();
}
int affectedRows = preStat.executeBatch();

与Statement比较:

PreparedStatement适用于发起大爆发量的SQL请求(这里说的请求是增删改,谁没事不停地查询十万次),在这种情形下其效率比Statement高出很多。PreparedStatement还有一个好处就是用绑定数据对象的方式来完成sql语句的构造,而不是像Statement那样用字符串拼接,出错率会比Statement低。

另外,PreparedStatement利用占位符“?”来传参,也在一定程度上防御了sql注入。

当然,Statement仍有其用武之地,不然早就被deprecated了。当面对一条或者若干条SQL语句时,Statement的代码量更小,更加简单易读。

另外,写法上,Statement将sql语句在请求阶段传入,而PreparedStatement则在创建其对象时传入,如下:

//Statement的语法
Statement stat = conn.createStatement(); //空参
stat.executeUpdate(sql); //传入完整SQL语句参数

//PreparedStatement语法
PreparedStatement preStat = conn.prepareStatement(sql); //传入不完整的、待绑定数据对象的SQL语句参数
preStat.executeUpdate(); //空参

DBCP:

Database Connection Pool

packages:

common-dbcp.jar

common-pool.jar

common-collections.jar

来由:

在JDBC的请求过程中,建立连接与释放连接所耗时间所占整个请求过程的时间比重最大。

再者,每次发起SQL请求都需要建立连接,最后再关闭连接,太过麻烦。

为了应对这种频繁建立与释放连接的需求,预先建立好一系列连接,放在连接缓冲池中,需要时从池中取用,这就是连接池的来由。

原理:

动态代理。

过程:

1.App启动。

2.读取配置。

3.根据配置发起多个连接,放入连接池中待取用。

4.取连接,发起SQL请求,SQL请求完毕后,连接放回连接池中。

连接池管理:

1.接到取连接请求。

若连接池中连接数足够,取出并返回连接。

若连接池中连接数不够,创建新的连接并返回。

2.连接空闲超时时间到。

若空闲连接数小于允许的空闲连接数,不操作。

若空闲连接数超过允许的空闲连接数,关闭超额的超时空闲连接。

使用:

下面是一个使用了连接池的数据库JDBC工具类。
//DBCPUtil.java
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

public class DBCPUtil{
	private static BasicDataSource bds;
	private static String dbDriver;
	private static String url;
	private static String userName;
	private static String password;
	private static int maxActive;
	private static int maxIdel;
	private static int maxWait;

	/**
	* 此方法载入时静态调用
	*
	*/
	protected static void createBasicDataSource() {
	
		try {
			...// 读取配置文件中的配置
			bds = new BasicDataSource();
			// 建立数据源
			bds.setDriverClassName(dbDriver); // JDBC驱动
			bds.setUrl(url); // url
			bds.setUsername(userName); // userName
			bds.setPassword(password); // password
			bds.setMaxActive(maxActive); // 最大连接数
			bds.setMaxIdle(maxIdle); // 最大空闲数
			bds.setMaxWait(maxWait); // 最大等待时间
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	* 获取连接,不需手动调用
	*
	*/
	private static Connection getConn() {
		// 不需手动关闭,请求完毕后,连接回到连接池中,并在超时时关闭超额的连接
		Connection conn = bds.getConnection();
		return conn;
	}

	/**
	 * 增删改
	 * 
	 */
	public static int update(String sql) {
		int rowAffected = 0;
		try{
			Statement stat = getConn().createStatement();
			rowAffected = stat.executeUpdate(sql);
			if(stat!=null) 
				stat.close();
		} catch(SQLException e){
			e.printStackTrace();
		}
		return rowAffected;
	}
	
	/**
	 * 查
	 * 
	 */
	public static ResultSet query(String sql, Statement stat){
		ResultSet rs = null;
		try{
			//预先准备好statement对象
			rs = stat.executeQuery(sql);
		} catch(SQLException e){
			e.printStackTrace();
		}
		return rs;
	}
	
    	/**
     	* 手动关闭result set
     	* @param rs
     	*/
    	public void closeResultSet(ResultSet rs){  
		try {
	        if(rs!=null)
	        	rs.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}  
    	}
    	/**
    	 * 手动关闭statement
    	 * @param stat
    	 */
    	public void closeStatement(Statement stat){
		try {
	        if(stat!=null)
	        	stat.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}  
    	}
}

//Test.java
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.SQLException;

public class Test {
	public static void main(String[] args){
		String querySql = "SELECT * FROM sometable";
		String insertSql = "INSERT INTO sometable VALUES('somenewvalue')";
		String updateSql = "UPDATE sometable SET value='someothervalue'";
		String deleteSql = "DELETE FROM sometable WHERE value='someothervalue'";
		Statement stat = DBCPUtil.getConn().createStatement();
		ResultSet rs = DBCPUtil.query(querySql, stat);
		try {
			while(rs.next()){
				String value = rs.getString("value");
				//对数据进行处理,组装成对象等
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		DBCPUtil.closeResultSet(rs);
		DBCPUtil.closeStatement(stat);
		DBCPUtil.update(insertSql);
		DBCPUtil.update(updateSql);
		DBCPUtil.update(deleteSql);
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值