首页 > WEB开发 > 数据库 > MySQL(JDBC)
2014
08-23

MySQL(JDBC)

02. MySQL(JDBC)

1、JDBC简介

SUN公司为了简化、统一对数据库的操作,定义了一套Java操作数据库的规范,称之为JDBC。JDBC规范的作用如下:

02. MySQL(JDBC)82

2、第一个JDBC程序:编写一个程序,这个程序从users表中读取数据,并打印在命令行窗口中。Users表下载

注意程序注释中写的步骤:

public static void main(String[] args) throws SQLException {
	// 1、注册驱动(向 DriverManager注册给定驱动程序)
	DriverManager.registerDriver(new com.mysql.jdbc.Driver());
	// 2、建立连接
	Connection conn = DriverManager.getConnection(
			"jdbc:mysql://localhost:3306/mydb1", "root", "");
	// 3、创建Statement对象(用于向数据库发送SQL)
	Statement stmt = conn.createStatement();
	// 4、执行SQL语句(查询)
	ResultSet rs = stmt.executeQuery("select * from users");
	// 5、遍历结果集
	while (rs.next()) {
		System.out.println(rs.getString("id") + "\t" + rs.getString("name")
				+ "\t" + rs.getString("password") + "\t"
				+ rs.getString("email") + "\t"
				+ rs.getDate("birthday").toLocaleString());
	}
	// 6、释放占用的资源
	rs.close();
	stmt.close();
	conn.close();
}

3、相关API(☆)

在JDBC规范中定义了如下类和接口:

02. MySQL(JDBC)920

1)DriverManager类,该类作用如下:

① 注册驱动

方式一:DriverManager.registerDriver(new com.mysql.jdbc.Driver());

不建议使用,原因:

a、依赖具体驱动。在注册驱动的时候必须要传入Driver的实例,否则无法编译。(注:Driver接口在JDBC规范中定义,由各个数据库厂商提供实现。)

b、导致驱动注册2遍。在com.mysql.jdbc.Driver类的静态初始化块中已经调用了registerDriver()方法进行了注册。

方式二(建议):Class.forName(“com.mysql.jdbc.Driver”);

附:依赖(dependencies)分类:

编译时依赖(Compile Dependency):编译的时候必须要导入相应jar包,否则编译不通过,此种情况应尽量避免,做到与具体Jar包无关。

运行时依赖(Runtime Dependency):只有到了项目实际运行时才需要具体jar包。

测试时依赖(Test Dependency):测试时需要。

② 连接数据库,并返回Connection对象:public static Connection getConnection(String url,String user,String password)

数据库URL的组成(☆):

02. MySQL(JDBC)1516

2)Connection

Connection对象代表与数据库的连接,所有的数据库操作都是基于连接之上的。常用方法:

createStatement():创建向数据库发送sql的statement对象。

prepareStatement():创建向数据库发送预编译sql的PrepareSatement对象。(占位符)

3)Statement

Jdbc程序中的Statement对象用于向数据库发送SQL语句,完成对数据库的CRUD操作。

常用方法:

ResultSet executeQuery(String sql):向数据库发送select查询语句。

int executeUpdate(String sql):发送insert、update、delete语句,返回值表示有几行受影响。

boolean execute(String sql):sql可以是任意的语句。返回值不是代表成功与否:如果是查询语句,就有结果集,返回true;没有返回结果集的,返回false。(一般用executeQuery和executeUpdate就够了)

4)ResultSet(☆)

Resultset封装了executeQuery()的结果,采用的类似于表格的方式。ResultSet 对象维护了一个指向表格数据行的游标,初始的时候,游标在第一行之前,调用ResultSet.next() 方法,使游标指向具体的数据行,再调用getXxx()方法即可获取该行具体字段的数据。

ResultSet中的getXxx()方法和数据表中的字段类型有个对应关系,具体如下:

getXxx in ResultSet

除了获取每行指定字段的getXxx方法,ResultSet中还提供如下移动游标的方法:

  • next():移动到下一行
  • previous():移动到前一行
  • absolute(int row):移动到指定行
  • beforeFirst():移动resultSet的最前面
  • afterLast():移动到resultSet的最后面

5)PreparedStatement

PreperedStatement是Statement的子接口,它的实例对象可以通过调用Connection.preparedStatement()方法获得,相对于Statement对象而言:

① SQL 语句被预编译并存储在 PreparedStatement 对象中,然后可以使用此对象多次、高效地执行该语句(支持?占位符,参数化SQL语句)。

② PreperedStatement可以避免SQL注入

提示:关于SQL预编译和注入的概念可以上网搜下。

4、释放资源(☆)

JDBC程序运行完后,切记要释放程序在运行过程中,创建的那些与数据库进行交互的对象,这些对象通常是ResultSet, Statement和Connection对象。特别是Connection对象,它是非常稀有的资源,用完后必须马上释放,如果Connection不能及时、正确的关闭,极易导致系统宕机。

为确保资源释放代码能运行,资源释放代码也一定要放在finally语句中。

以下程序可作为一般的JDBC程序的模板(☆):

Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
	conn = DriverManager.getConnection(……);
	stmt = conn.createStatement();
	rs = stmt.executeQuery("……");
} catch (Exception e) {
	e.printStackTrace();
} finally{
	if(rs!=null){//关闭rs
		try {
			rs.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		rs = null;
	}
	if(stmt!=null){//关闭stmt
		try {
			stmt.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		stmt = null;
	}
	if(conn!=null){//关闭conn
		try {
			conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		conn = null;
	}
}

根据上述模板可将JDBC程序共同的部分抽取成JDBC工具类(JdbcUtil.java),代码如下:

public class JDBCUtil {
	private static String driver;
	private static String url;
	private static String user;
	private static String password;
	static{
		//driver、url、user、password从dbconn.properties中读取
		ResourceBundle rb = ResourceBundle.getBundle("dbconn");
		driver = rb.getString("driver");
		url = rb.getString("url");
		user = rb.getString("user");
		password = rb.getString("password");
		try {
			Class.forName(driver);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	//获取连接
	public static Connection getConnection() throws ClassNotFoundException, SQLException{
		Connection conn = DriverManager.getConnection(url,user,password);
		return conn;
	}
	//释放资源
	public static void release(ResultSet rs,Statement stmt,Connection conn){
		if (rs != null) {// 关闭rs
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			rs = null;
		}
		if (stmt != null) {// 关闭stmt
			try {
				stmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			stmt = null;
		}
		if (conn != null) {// 关闭conn
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			conn = null;
		}
	}
}

5、批处理

何时需要:当需要向数据库发送一批SQL语句执行时,应避免向数据库一条条的发送执行,而应采用JDBC的批处理机制,以提升执行效率。

1)多条不同的SQL语句(Statement.addBatch())

下面的代码模拟了向数据库发送两条添加和一条删除SQL语句:

@Test
public void test1() throws Exception{
	Connection conn = JDBCUtil.getConnection();
	Statement stmt = conn.createStatement();
	//添加三条SQL到 Statement对象的命令列表中
	stmt.addBatch("insert into t1(id,name) values(1,'wang')");
	stmt.addBatch("insert into t1(id,name) values(2,'xiang')");
	stmt.addBatch("delete from t1 where id =1");
	//执行批处理并打印返回结果
	int[] ii = stmt.executeBatch();
	for(int i : ii ){
		System.out.println(i);
	}
	//释放资源
	JDBCUtil.release(null, stmt, conn);
}

在Statement实例内部有一个List(命令列表),执行addBatch(sql) 时就将sql语句加到List中了。

2)多条相同的SQL语句(PreparedStatement.addBatch())

下面的代码模拟了向数据库发送10条相同的sql语句(只是参数不同),此种情况最适合用PreparedStatement:

@Test
public void test2()throws Exception{
	Connection conn = JDBCUtil.getConnection();
	PreparedStatement stmt = conn.prepareStatement("insert into t1(id,name) values(?,?)");

	for(int i =1 ;i <=10;i ++){
		stmt.setInt(1, i);
		stmt.setString(2, "haha"+i);
		stmt.addBatch();
	}
	stmt.executeBatch();
	//释放资源
	JDBCUtil.release(null, stmt, conn);
}

当需要发送大量SQL语句时一定要注意内存溢出问题,如下面的代码模拟了向数据库发送100,000条插入语句:

@Test
public void test3()throws Exception{
	Connection conn = JDBCUtil.getConnection();
	PreparedStatement stmt = conn.prepareStatement("insert into t1(id,name) values(?,?)");
	ResultSet rs = null;

	for(int i =1 ;i <=100000;i ++){
		stmt.setInt(1, i);
		stmt.setString(2, "haha"+i);
		stmt.addBatch();
		if(i%1000 ==0){
			stmt.executeBatch();
			stmt.clearBatch();//清理缓存
		}
	}
	stmt.executeBatch();

	JDBCUtil.release(rs, stmt, conn);
}

留下一个回复

你的email不会被公开。