JDBC是SUN公司提供的一套操作数据库的标准规范,用来规范java连接数据库。
JDBC与数据库驱动的关系:接口与实现的关系。
。主要涉及4个对象:
DriverManager:用于注册驱动
Connection:获取与数据库连接
Statement(因为存在SQL注入安全问题,一般采用PreperedStatement):操作数据库SQL语句的对象
ResultSet:结果集或一张虚拟表
JDBC规范存在于jdk API中 :java.sql.* 和 javax.sql.* 中
JDBC驱动由各数据库厂商已经提供,MySQL的驱动如下:
在开发中我们首先需要将驱动导入项目中,创建lib文件夹,将上面的jar包粘贴到lib文件夹中。在eclipse中可以使用右键选择jar包->Build Path完成操作后会生成如下小奶瓶:即表示导入jar库成功。
下面直接上例子,一个Login的Demo:
数据库名:jdbctest 表名:user
package com.aloha.entity;
import java.util.Date;
public class User {
private int id;
private String name;
private String password;
private String email;
private Date birthday;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", password=" + password
+ ", email=" + email + ", birthday=" + birthday + "]";
}
}
覆写了toString方法只是为了方便打印检测,可以不写。
写工具类之前我们需要考虑一个问题:连接数据库需要通过反射加载驱动时需要使用Driver()方法,我们定义一个值driverClass=com.mysql.jdbc.Driver;获取连接需要的url为 jdbc:mysql://localhost:3306/jdbctest ;同时获取连接还需要用户名和密码。这些数值都是可能随时改变的,为了在某值改变时不影响java代码的改变,我们使用配置dbinfo.properties文件存储数据,我目前配置书写如下:
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbctest
user=root
password=root
我们在DBUtil工具类中同样定义这些变量为静态私有成员变量以达到名称对应的规范效果。
创建与配置文件的连接并且传值。因为创建连接和传值我们只需要在程序每次执行的时候加载一次就可以,所以我们将这段代码写在一个静态代码块中,加载驱动只需要一次,也放在里面。初始化的代码片段如下:
private static String driverClass;
private static String user;
private static String password;
private static String url;
static{
//创建与配置文件的连接
ResourceBundle rb = ResourceBundle.getBundle("dbinfo");
//传值到java
url = rb.getString("url");
user = rb.getString("user");
password = rb.getString("password");
driverClass = rb.getString("driverClass");
try {
//加载驱动
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
因为已经通过配置文件将值传入,所以我们可以直接将获取连接的方法封装到一个无参函数中以简化调用:
/**
* 这是创建连接的方法,把API中的getConnection(String url, String user, String password)方
* 法引用简化
* @return 返回的是连接对象
* @throws SQLException
*/
public static Connection getConnection() throws SQLException{
//试图建立到给定数据库 URL 的连接
return DriverManager.getConnection(url, user, password);
}
这样我们在创建连接的时候只需要调用getConnection()这个方法就可以了。
在关闭资源时,我们需要关闭ResultSet、Statement、Connection资源。按照后建立先关闭的原则按照ResultSet、Statement、Connection顺序关闭资源。但是,如果我们的查询或者修改语句出错,那么程序执行到这里就没法继续执行,但是Statement、Connection的对象已经创建了,即资源是一直占用的,没法关闭。这样,其他人访问的时候由于资源占用,其他人访问的时候会阻塞等待。为了解决这个问题,我们在调用关闭资源这个方法中先将资源置为null,提出来:
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
User u = null;
然后就是closeAll()方法的定义:
public static void closeAll(ResultSet rs, Statement stmt, Connection conn){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs=null;
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt=null;
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn=null;
}
}
这样定义是为了配合DoLogin类中对closeAll()方法调用,防止因为SQL语句和这其他的错误而导致资源被占用无法释放。
DBUtil工具类的完全代码:
package com.aloha.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ResourceBundle;
/**
* 这个工具类是用来创建JDBC连接的工具类,类在加载的时候会加载驱动。这个类中有两个方法,一个连接的方法,一个关闭所有流的方法
* @author malaganguo
*
*/
public class DBUtil {
private static String driverClass;
private static String user;
private static String password;
private static String url;
static{
//创建与配置文件的连接
ResourceBundle rb = ResourceBundle.getBundle("dbinfo");
//传值到java
url = rb.getString("url");
user = rb.getString("user");
password = rb.getString("password");
driverClass = rb.getString("driverClass");
try {
//加载驱动
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 这是创建连接的方法,把API中的getConnection(String url, String user, String password)方法引用简化
* @return 返回的是连接对象
* @throws SQLException
*/
public static Connection getConnection() throws SQLException{
//试图建立到给定数据库 URL 的连接
return DriverManager.getConnection(url, user, password);
}
/**
* 这是关闭资源的工具方法
* @param rs
* @param stmt
* @param conn
*/
public static void closeAll(ResultSet rs, Statement stmt, Connection conn){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs=null;
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt=null;
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn=null;
}
}
}
DoLogin类的完全代码:
package com.aloha.service;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.aloha.entity.User;
import com.aloha.util.DBUtil;
/**
* 这是一个验证登陆信息的类,有一个发现用户的方法
* @author malaganguo
*
*/
public class DoLogin {
public User findUser(String uname, String pwd){
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
User u = null;
try {
conn = DBUtil.getConnection();
String sql = "SELECT * FROM user WHERE NAME=? AND PASSWORD=?";
pstmt = conn.prepareStatement(sql);
//将指定参数设置为给定 Java String 值
pstmt.setString(1, uname);
pstmt.setString(2, pwd);
//执行sql语句
rs = pstmt.executeQuery();
if(rs.next()){
u = new User();
u.setId(rs.getInt("id"));
u.setName(rs.getString("name"));
u.setPassword(rs.getString("password"));
u.setEmail(rs.getString("email"));
u.setBirthday(rs.getDate("birthday"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
DBUtil.closeAll(rs, pstmt, conn);
}
return u;
}
}
在这个类中的findUser方法我们就可以看出为什么工具中的closeAll()类要这么写了:完成一次查询或者java语句在执行时出现了错误,只要加载的资源的值都不为null,所以都会进入finally代码块中,这时候对不为null的资源变量进行释放,就可以完成查询一次后资源释放和语句查询错误时依旧释放资源的作用。
客户端键盘录入用户名和密码,主方法调用DoLogin中的findUser方法与数据库中的记录匹配,返回匹配结果。
package com.aloha.client;
import java.util.Scanner;
import com.aloha.entity.User;
import com.aloha.service.DoLogin;
public class Login {
/**
* @param args
*/
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("---欢迎来到ALOHA的注册系统---");
System.out.println("请输入用户名:");
String inputname = sc.nextLine();
System.out.println("请输入密码:");
String inputpwd = sc.nextLine();
DoLogin dl = new DoLogin();
User user = dl.findUser(inputname, inputpwd);
if(user!=null){
System.out.println("欢迎你"+inputname);
}else{
System.out.println("用户名或密码错误!");
}
}
}
这段代码简单就不加注释了。
这四个SQL语句的执行均由一个方法执行——Statement类的executeUpdate()方法执行。执行结果同查询函数返回的时ResultSet类型的值。CRUD四种方法的使用基本没有什么区别,就是SQL语句的变化。
一个Demo:
package com.itheima.crud;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import com.itheima.entity.User;
import com.itheima.util.DBUtils;
public class TestCRUD {
@Test //查询的方法
public void testSelect(){
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
conn = DBUtils.getConnection();
stmt = conn.createStatement();
rs = stmt.executeQuery("select * from users");
List list = new ArrayList();
while(rs.next()){
User u = new User();
u.setId(rs.getInt(1));
u.setName(rs.getString(2));
u.setPassword(rs.getString(3));
u.setEmail(rs.getString(4));
u.setBirthday(rs.getDate(5));
list.add(u);
}
for (User user : list) {
System.out.println(user);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
DBUtils.closeAll(rs, stmt, conn);
}
}
@Test //插入记录的方法
public void testInsert(){
Connection conn = null;
PreparedStatement stmt = null;
try {
conn = DBUtils.getConnection();
stmt = conn.prepareStatement("INSERT INTO users VALUES(?,?,?,?,?)");
stmt.setInt(1, 5);
stmt.setString(2, "tom");
stmt.setString(3, "333");
stmt.setString(4, "[email protected]");
//stmt.setDate(5, new java.sql.Date(System.currentTimeMillis()));
stmt.setString(5, "2015-09-11");
int i = stmt.executeUpdate();
if(i>0){
System.out.println("success");
}
} catch (Exception e) {
e.printStackTrace();
}finally{
DBUtils.closeAll(null, stmt, conn);
}
}
@Test //更新记录的方法
public void testUpdate(){
Connection conn = null;
PreparedStatement stmt = null;
try {
conn = DBUtils.getConnection();
stmt = conn.prepareStatement("UPDATE users SET NAME=?,PASSWORD=?,email=? WHERE id=?");
stmt.setString(1, "jerry123");
stmt.setString(2, "123");
stmt.setString(3, "[email protected]");
stmt.setInt(4, 5);
int i = stmt.executeUpdate();
if(i>0){
System.out.println("success");
}
} catch (Exception e) {
e.printStackTrace();
}finally{
DBUtils.closeAll(null, stmt, conn);
}
}
@Test //删除数据的方法
public void testDelete(){
Connection conn = null;
Statement stmt = null;
try {
conn = DBUtils.getConnection();
stmt = conn.createStatement();
int i = stmt.executeUpdate("DELETE FROM users WHERE id=4");
if(i>0){
System.out.println("success");
}
} catch (Exception e) {
e.printStackTrace();
}finally{
DBUtils.closeAll(null, stmt, conn);
}
}
}
用户类user和工具类DBUtil与前面Login的Demo一样。