1.介绍
由于每一个数据库的底层实现原理不一样,Oracle数据库有自己的实现原理,Mysql数据库有自己的实现原理,MS(SqlServer)有自己的实现原理,所以sun公司制定了一套接口,由各大数据库厂家去实现,程序员只需要去调用这套接口即可,这套接口就是JDBC;
Java DataBase Connective--Java与数据库的连接;
JDBC本质是sun公司制定的一套接口,java.sql.*;
2.JDBC编程六步
①注册驱动(告诉java程序,即将要连接的是哪个品牌的数据库)
②获取连接(表示jvm进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的,使用后一定要关闭)
③获取数据库操作对象(专门执行sql语句的对象)
④执行sql语句(DQL,DML...)
⑤处理查询结果集(只有第四步是select的时候才需要)
⑥释放资源
public class JDBC {
public static void main(String[] args) {
Statement stmt = null;
Connection conn = null;
ResultSet rs = null;
try {
// 注册驱动第一种方式
// DriverManager.registerDriver(new com.mysql.jdbc.Driver());
// 注册驱动第二种方式,因为参数是字符串,字符串可以写到配置文件中,所以这种方式更常用
// 实际开发中不建议把数据库信息写死在java代码中
ResourceBundle rb = ResourceBundle.getBundle("jdbc");
Class.forName(rb.getString("driver"));
// 连接数据库,打开通道
// conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/movie",
// "root", "root");
conn = DriverManager.getConnection(rb.getString("connect"), rb.getString("username"),
rb.getString("password"));
// System.out.println(conn);
// 获取数据库操作对象
stmt = conn.createStatement();
// 执行sql语句
String sql = "insert into y_movie (m_name,m_up_year,m_director) values('后天',2018,'诺兰')";
String sql2 = "select * from y_movie";
String sql3 = "delete from y_movie where m_id=5";
String sql4 = "update y_movie set m_name='阿凡达8' where m_id=4;";
// 返回值是影响表的数据记录的条数
// int count = stmt.executeUpdate(sql4);
rs = stmt.executeQuery(sql2);
while (rs.next()) {
System.out.print((rs.getInt("m_up_year") + 1000) + rs.getString("m_name") + rs.getString("m_director"));
System.out.println();
}
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
} finally {
// 为了保证通道一定关闭,在finnaly语句中执行
// 并且遵循从小到大原则
if (rs != null) {
try {
rs.close();
} catch (Exception e) {
// TODO: handle exception
}
}
if (stmt != null) {
try {
stmt.close();
} catch (Exception e) {
// TODO: handle exception
}
}
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
// TODO: handle exception
}
}
}
}
}
3.PreparedStatement-预编译数据库操作对象
由于Statement存在sql注入风险,所以用PreparedStatement代替Statement;
PreparedStatement的原理是:预先对sql语句的框架进行编译,然后再给sql语句传值;
private static boolean isLogin(HashMap<String, String> map) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
ResourceBundle rb = ResourceBundle.getBundle("db");
// 注册驱动
Class.forName(rb.getString("driver"));
// 连接数据库
conn = DriverManager.getConnection(rb.getString("connector"), rb.getString("username"),
rb.getString("password"));
// 获取数据库操作对象
// sql语句框架,其中一个?代表一个占位符,一个?将来接收一个值,将来接收的整个值会被单作一个字符串处理,占位符不能用单引号括起来
String sql = "select * from login where l_username= ? and l_pwd=?";
// 发送sql语句框架给DBMS,DBMS进行sql语句的预编译
ps = conn.prepareStatement(sql);
// 给占位符?传值,第一个?下标是1,第二个是2,JDBC中所有下标从1开始
ps.setString(1, map.get("username"));
ps.setString(2, map.get("password"));
// 执行sql语句
rs = ps.executeQuery();
if (rs.next()) {
System.out.println("您好:" + rs.getString("l_nick"));
return true;
}
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
} finally {
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 (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return false;
}
解决SQL注入的关键是:用户提供的信息中虽然含有sql语句的关键字,但这些关键字并没有参与编译,不起作用;
mysql不会对相同的语句做重复编译,如果当条语句与之前编译过的相同,则会之间执行,只有不同的sql语句才会重新编译,再执行;由于Statement需要每次传入不同的参数,sql语句每次也是不同的语句,所以Statement是编译一次执行一次,PreparedStatement是编译一次可供后续多次执行,后者执行效率更高;
PreparedStatement会在编译阶段做数据类型的安全检查;
实际开发中PreparedStatement使用较多,只有j较少数情况下使用Statement,例如业务方面必须要求sql注入的时候,就只能用Statement;
以下情况只能使用Statement:
"select * from login order by l_id " + str;
4.事务
JDBC中的事务是自动提交的,即只要执行一条DML语句,则自动提交一次,这是JDBC默认的事务行为;
public static void main(String[] args) {
PreparedStatement ps = null;
Connection conn = null;
ResultSet rs = null;
ResourceBundle rb = ResourceBundle.getBundle("db");
try {
Class.forName(rb.getString("driver"));
conn = DriverManager.getConnection(rb.getString("connector"), rb.getString("username"),
rb.getString("password"));
// 取消自动提交,改为手动提交
conn.setAutoCommit(false);
String sql = "insert into login (l_username,l_pwd,l_nick) values(?,?,?)";
ps = conn.prepareStatement(sql);
ps.setString(1, "twx");
ps.setString(2, "twx111");
ps.setString(3, "海王星");
int count = ps.executeUpdate();
// int a = 10 / 0;
String sql3 = "update login set l_nick=? where l_id=?";
ps = conn.prepareStatement(sql3);
ps.setString(1, "猎户座");
ps.setInt(2, 1);
count += ps.executeUpdate();
System.out.println(count);
// 手动提交
conn.commit();
} catch (ClassNotFoundException e) {
// 事务回滚
if (conn != null) {
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
if (conn != null) {
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
e.printStackTrace();
} finally {
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 (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
5.封装JDBC
package com.idea.jdbc;
import java.sql.*;
import java.util.ResourceBundle;
public class DBUtils {
static ResourceBundle rb = ResourceBundle.getBundle("db");
// 静态代码块在类加载的时候执行,并且只执行一次
static {
try {
Class.forName(rb.getString("driver"));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private DBUtils() {
}
/**
* 连接数据库
*
* @return 数据库连接通道
* @throws SQLException
*/
public static Connection connect() throws SQLException {
return DriverManager.getConnection(rb.getString("connector"), rb.getString("username"),
rb.getString("password"));
}
/**
* 释放数据库资源
*
* @param conn
* @param ps
* @param rs
*/
public static void close(Connection conn, Statement 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 (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
PreparedStatement模糊查询:
String sql = "select * from login where l_nick like ?";
ps = conn.prepareStatement(sql);
ps.setString(1, "%星%");