本文以mysql数据库为例
一:Java访问数据库的具体步骤:
1.加载(注册)数据库
Class.forName("com.mysql.jdbc.Driver").newInstance();
2.建立链接
java.sql.DriverManager类中有三种获取数据库连接的方法,虽说是三种,但是获取链接的方法是大同小异的,方法中都是通过return (getConnection(url, info, Reflection.getCallerClass()))语句来返回一个Connection:
1.getConnection(String url,java.util.Properties info)
参数说明:
url: 数据库url,例如jdbc:mysql://localhost:3306/media2
info: 一个Properties 至少包括”user” and “password”
2.getConnection(String url,String user, String password):
参数说明:
url: 数据库url,例如jdbc:mysql://localhost:3306/media2
user:数据库用户名
password:数据库密码
3.getConnection(String url):
参数说明:
url:数据库url,例如jdbc:mysql://localhost:3306/media2?user=root&password=123456&useUnicode=true&characterEncoding=gb2312*/
3.执行SQL语句和处理结果集
可以通过创建Statement对象或者PreparedStatement对象两种方式来执行SQL语句。
首先来讲述一下这两个对象的区别:
1.PreparedStatement是预编译的,对于批量处理可以大大提高效率.也叫JDBC存储过程,通过set不同数据只需要生成一次执行计划,可以重用。
而Statement处理两次相似操作必须执行两条sql语句。
例如:
PreparedStatement pst = null;//代码中需要添加try-catch,这里为了演示效果未添加,可参考下面给出的完整代码。
String sql="select * from fuser where name = ? and email = ?";
pst = connection.prepareStatement(sql);
pst.setString(1, "4");
pst.setString(2, "4@qq.com");
2.PreparedStatement对象的开销比Statement大,对于一次性操作并不会带来额外的好处,所以综合第一点:在对数据库只执行一次性存取的时侯,它应该和普通的对象毫无差异,体现不出它预编译的优越性。此时使用 Statement 对象进行处理较为方便。
3.prepareStatement会先初始化SQL,先把这个SQL提交到数据库中进行预处理,多次使用可提高效率。
createStatement不会初始化,没有预处理,每次都是从0开始执行SQL。
4.安全性:
prepareStatement相比于Statement更加安全,比如一个简单的查询,当我们输入用户名为[’ or ‘1’ = ‘1]时,传入的任何内容就不会和原来的语句发生任何匹配的关系,因此也不会执行。
String sql="select * from fuser where email = ? and password = ?";
pst = connection.prepareStatement(sql);
pst.setString(1, "1@qq.com");
//pst.setString(2, "xMpCOKC5I4INzFCab3WEmw==");
pst.setString(2, "' or '1' = '1");
如果使用的是Statement,那是可以查出来的
String email="4@qq.com";
String password = "' or '1' = '1";
String sql = "select * from fuser where email= '"+email+"' and password='"+password+"'";
statement = (Statement) connection.createStatement();
rs = statement.executeQuery(sql);
综上,建议在任何时候都使用PreparedStatement较好。
附上此段代码:
package com.v512.mysql;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import com.mysql.jdbc.Statement;
public class Test {
public static Connection getconn(){
/*1 加载(注册)数据库 驱动加载就是把各个数据库提供的访问数据库的API加载到我们程序进来,加载JDBC驱动,并将其注册到DriverManager中,每一种数据库提供的数据库驱动不一样,*/
try {
Class.forName("com.mysql.jdbc.Driver").newInstance();
} catch (InstantiationException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IllegalAccessException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (ClassNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
//2 建立链接
Connection conn = null;
try {
/*3种获取connection的方法:
1.getConnection(String url,java.util.Properties info)
参数说明:
url: 数据库url,例如jdbc:mysql://localhost:3306/media2
info: 一个Properties 至少包括"user" and "password"
2.getConnection(String url,String user, String password):传入数据库名,用户名,密码
参数说明:
url: 数据库url,例如jdbc:mysql://localhost:3306/media2
user:数据库用户名
password:数据库密码
3.getConnection(String url):直接传入URL
参数说明:
url:数据库url,例如jdbc:mysql://localhost:3306/media2?user=root&password=123456&useUnicode=true&characterEncoding=gb2312*/
Properties properties = new Properties();
properties.put("user", "root");
properties.put("password", "123456");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/media2", properties);
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/media2", "root","123456");
String url="jdbc:mysql://localhost:3306/media2?user=root&password=123456&useUnicode=true&characterEncoding=gb2312";
conn=DriverManager.getConnection(url);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
}
public static void main(String[] args) {
/*3. 执行SQL语句
数据库连接建立好之后,接下来就是一些准备工作和执行sql语句了,
准备工作要做的就是建立Statement对象PreparedStatement对象,例如:*/
//1.使用Statement对象
/* Connection connection = getconn();
PreparedStatement pst = null;
ResultSet rs = null;
try {
String sql="select * from fuser where name = ? and email = ?";
pst = connection.prepareStatement(sql);
pst.setString(1, "4");
pst.setString(2, "4@qq.com");
rs = pst.executeQuery();
if(rs.next()){
int id = rs.getInt("id");
System.out.println("id:"+id);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
rs.close();
pst.close();
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}*/
//2.使用PreparedStatement对象
/* Connection connection = getconn();
ResultSet rs = null;
Statement statement = null;
try {
String sql="select * from fuser where name = '4' and email = '4@qq.com' ";
statement = (Statement) connection.createStatement();
rs = statement.executeQuery(sql);
if(rs.next()){
int id = rs.getInt("id");
System.out.println("id:"+id);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
rs.close();
statement.close();
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}*/
//PreparedStatement对比Statement安全性问题
//使用PreparedStatement
/* Connection connection = getconn();
PreparedStatement pst = null;
pst.executeQuery()
pst.execute()
ResultSet rs = null;
try {
String sql="select * from fuser where email = ? and password = ?";
pst = connection.prepareStatement(sql);
pst.setString(1, "1@qq.com");
//pst.setString(2, "xMpCOKC5I4INzFCab3WEmw==");
pst.setString(2, "' or '1' = '1");
rs = pst.executeQuery();
if(rs.next()){
int id = rs.getInt("id");
System.out.println("id:"+id);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
rs.close();
pst.close();
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}*/
//使用Statement
/* Connection connection = getconn();
ResultSet rs = null;
Statement statement = null;
try {
String email="4@qq.com";
String password = "' or '1' = '1";
String sql = "select * from fuser where email= '"+email+"' and password='"+password+"'";
statement = (Statement) connection.createStatement();
rs = statement.executeQuery(sql);
if(rs.next()){
int id = rs.getInt("id");
System.out.println("id:"+id);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
rs.close();
statement.close();
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}*/
}
}
这是我测试用的数据表:
二、数据库连接池
对于大多数应用程序,当它们正在处理通常需要数毫秒完成的事务时,仅需要能够访问JDBC连接的 1 个线程。当不处理事务时,这个连接就会闲置。相反,连接池允许闲置的连接被其它需要的线程使用。
事实上,当一个线程需要用 JDBC 对一个 GBase 或其它数据库操作时,它从池中请求一个连接。当这个线程使用完了这个连接,将它返回到连接池中,这样这就可以被其它想使用它的线程使用。
当连接从池中“借出”,它被请求它的线程专有地使用。从编程的角度来看,这和用户的线程每当需要一个 JDBC 连接的时候调用DriverManager.getConnection() 是一样的,采用连接池技术,可通过使用新的或已有的连接结束线程。
常用的连接池有以下这些:DBCP 连接、C3P0 连接、JndI与 Tomact 连接池,C3PO 连接池是一个优秀的连接池,推荐使用,但并说明其他连接池不好,只是各有优点, C3P0 能够更好的支持高并发,但是在稳定性方面略逊于DBCP 。这里我就主要介绍一下C3P0的使用:
1.需要导入的Jar包:
c3p0需要导入c3p0.jar、mchange-commons-.jar,如果操作的是Oracle数据库,还需要导入c3p0-oracle-thin-extras-pre1.jar。
下载地址:http://download.youkuaiyun.com/detail/loverestart/9841162
2.修改配置文件(配置文件也放在上述资源中了):
修改可参考下图
3.JdbcUtils类:
我们需要通过这个类来获取数据库连接或者释放连接,方法说明类中有了我就不重复了。
package com.v512.util;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class JdbcUtils {
/**
* 释放连接
* @param connection
*/
public static void releaseConnection(Connection connection){
try {
if(connection != null ) {
connection.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
private static DataSource dataSource = null;
static{
//dataSource资源只能初始化一次
dataSource= new ComboPooledDataSource("mvcApp");
}
/**
* 获取连接
* @return
* @throws SQLException
*/
public static Connection getConnection() throws SQLException{
return dataSource.getConnection();
}
}