Java DataBase Connectivity (java语言连接数据库)
是SUN公司制定的一套接口(interface)
接口都有调用者和实现者
面向接口去调用,面向接口实现类,都属于面向接口编写
为什么要面向接口编程?
解耦合,降低程序的耦合度,提高程序的扩展力
多态机制,就是典型的面向抽象编程。(不要面向具体编程)
//建议:
Animal a = new Cat();
Animal a = new Dog();
// 喂养的方法
public void feed(Animal a){ // 面向父类型编程。
}
//不建议:
Dog d = new Dog();
Cat c = new Cat();
为什么SUN制定一套JDBC接口呢?
因为每一个数据库的底层实现原理都不一样。
Oracle数据库有自己的原理。
MySQL数据库也有自己的原理。
MS SqlServer数据库也有自己的原理。
…
每一个数据库产品都有自己独特的实现原理。
1:JDBC接口的制定
2:数据库厂商实现JDBC接口
3:程序员面向接口写代码(结合反射机制,properties文件)
import java.util.ResourceBundle;
/**
* java程序员
* 不需要关心具体是哪个品牌的数据库,只需要面向JDBC接口写代码
* 面向接口编程,面向抽象编程,不面向具体编程
* 面向接口去创建对象
*
*/
public class JavaMy {
public static void main(String[] args) throws Exception {
JDBC jdbc = new MySQL();
jdbc.getConnection();
JDBC jdbc1 = new Oracal();
jdbc1.getConnection();
//上面这两种方式都是通过面向接口编程
//也可以利用反射机制去创建对象
ResourceBundle db = ResourceBundle.getBundle("DB");
String classname = db.getString("classname");
Class c = Class.forName(classname);
JDBC jdbc2 =(JDBC)c.newInstance();
jdbc2.getConnection();
// new JDBC() {
// @Override
// public void getConnection() {
//
// }
// }匿名内部类 利用接口去接收对象
}
}
第一步:注册驱动(作用:告诉Java程序,即将要连接的是哪个品牌的数据库)
第二步:建立连接(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的,使用完之后一定要关闭通道。)
第三步:获取数据库操作对象(专门执行sql语句的对象)
第四步:执行SQL语句(DQL DML…)
第五步:处理查询结果集(只有当第四步执行的是select语句的时候,才有这第五步处理查询结果集。)
第六步:释放资源(使用完资源之后一定要关闭资源。Java和数据库属于进程间的通信,开启之后一定要关闭。)
jdbc完成insert
import java.sql.*;
public class Test02 {
public static void main(String[] args) {
// 注册驱动
Statement statement = null;
Connection connection = null;
try {
Driver driver = new com.mysql.jdbc.Driver(); // 多态,父类型的引用指向子类型的对象
DriverManager.registerDriver(driver); // 使用 父类型的数据类型去接收子类型对象
String url = "jdbc:mysql://127.0.0.1:3306/learn";
String user = "root";
String password = "333";
connection = DriverManager.getConnection(url, user, password);
/**
* Creates a Statement
object for sending
* SQL statements to the database.
* 获取数据库操作对象
*/
statement = connection.createStatement();
String sql = "insert into dept(deptno,dname,loc) values(50,'人事部','宁波')";
int i = statement.executeUpdate(sql);//专门执行DML语句(insert delete update)返回数据库中受影响的记录条数
System.out.println(i == 1 ? "保存成功" : "保存失败");
} catch (SQLException e) {
e.printStackTrace();
} finally {
//保证资源一定释放,需要在finally语句中将资源关闭,并且遵循从小到大的顺序依次关闭
// 分别对其进行try catch
if (statement!=null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
利用反射机制让静态代码块执行,参数是字符串,可以写入配置文件
不需要接收返回值。
package com.fangjun.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ResourceBundle;
/**
* 将连接数据库的所有文件写入到配置文件中
*/
public class Test02 {
public static void main(String[] args) {
//利用资源绑定器绑定资源
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
String driver1 = bundle.getString("driver");
String url1 = bundle.getString("url");
String user1 = bundle.getString("user");
String pass1 = bundle.getString("pass");
Statement statement=null;
Connection root=null;
try {
Class.forName(driver1);//底层会执行对象的静态代码块
root = DriverManager.getConnection(url1,user1,pass1);//获取数据库连接,三个参数,url,user,passwd
statement = root.createStatement();//数据库执行的对象
String sql="delete from dept where deptno=40";
int i = statement.executeUpdate(sql);//这个对象去执行sql语句
//DML语句:executeUpdate 专门执行增删改,返回int类型,受影响的条数
//执行查询的语句:executeQuery,这个Query语句会返回一个ResultSet
System.out.println(i==1? "删除成功":"删除失败");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
if (statement!=null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (root!=null){
try {
root.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
package com.fangjun.jdbc;
import java.sql.*;
import java.util.ResourceBundle;
public class Test05 {
public static void main(String[] args) {
Connection connection=null;
Statement statement =null;
ResultSet resultSet=null;//结果集对象
ResourceBundle jdbc = ResourceBundle.getBundle("jdbc");
String driver = jdbc.getString("driver");
String url = jdbc.getString("url");
String user = jdbc.getString("user");
String pass = jdbc.getString("pass");
try {
Class.forName(driver);
connection=DriverManager.getConnection(url,user,pass);
statement=connection.createStatement();
String sql="select empno,ename,sal from emp";
resultSet = statement.executeQuery(sql);
while (resultSet.next()){
String empno = resultSet.getString(1);
String ename = resultSet.getString(2);
String sal = resultSet.getString(3);
System.out.println(empno+" "+ename+" "+sal);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
if (resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement!=null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
1)模拟登陆业务(存在SQL注入问题)
package com.fangjun;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Scanner;
public class Userlogin {
public static void main(String[] args) {
Map userLoginInfo= initUI();
boolean flag = login(userLoginInfo);
System.out.println(flag? "登录成功":"登录失败");
}
private static boolean login(Map userLoginInfo) {
ResourceBundle jdbc = ResourceBundle.getBundle("jdbc");
String driver = jdbc.getString("driver");
String url = jdbc.getString("url");
String user = jdbc.getString("user");
String pass = jdbc.getString("pass");
Connection connection=null;
Statement statement=null;
ResultSet resultSet=null;
boolean loginflag=false;
String loginName = userLoginInfo.get("loginName");
String loginPwd = userLoginInfo.get("loginPwd");
try {
Class.forName(driver);
connection = DriverManager.getConnection(url, user, pass);
statement = connection.createStatement();
String sql="select *from t_user where loginName='"+loginName+"' and loginPwd='"+loginPwd+"'";
System.out.println(sql);
resultSet = statement.executeQuery(sql);
//只有一条
if (resultSet.next()){
loginflag=true;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
if (resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement!=null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return loginflag;
}
private static Map initUI() {
Scanner scanner = new Scanner(System.in);
System.out.println("Please input the Username");
String Username = scanner.nextLine();
System.out.println("Please input the passWord");
String password = scanner.nextLine();
HashMap Userloginfo = new HashMap<>();
Userloginfo.put("loginName",Username);
Userloginfo.put("loginPwd",password);
return Userloginfo;
}
}
2)关于sql注入
select *from t_user where loginName='fdsa' and loginPwd='fdsa' or '1'='1'
查询结果如下
用户注入的信息含有SQL语句的关键字,并且这些关键字参与SQL语句的编译过程,导致SQL原意被扭曲。
3)如何解决SQL注入问题
使用PreparedStatement 预先对SQL语句进行逻辑的编译,后续输入含有逻辑的SQL语句被当做普通字符串,不再参与数据库查询逻辑的操作。
package com.fangjun;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Scanner;
public class Userlogin001 {
public static void main(String[] args) {
Map userLoginInfo= initUI();
boolean flag = login(userLoginInfo);
System.out.println(flag? "登录成功":"登录失败");
}
private static boolean login(Map userLoginInfo) {
ResourceBundle jdbc = ResourceBundle.getBundle("jdbc");
String driver = jdbc.getString("driver");
String url = jdbc.getString("url");
String user = jdbc.getString("user");
String pass = jdbc.getString("pass");
Connection connection=null;
PreparedStatement statement=null; //这里修改成 PreparedStatement
ResultSet resultSet=null;
boolean loginflag=false;
String loginName = userLoginInfo.get("loginName");
String loginPwd = userLoginInfo.get("loginPwd");
try {
Class.forName(driver);
connection = DriverManager.getConnection(url, user, pass);
//sql语句的模板,?表示一个占位符;注意:占位符不要使用单引号
String sql="select *from t_user where loginName = ? and loginPwd = ?";
//执行到此处,会发送SQL模板给DBMS,预先对SQL语句的逻辑进行编译,之后不在会添加其他逻辑,逻辑符号位普通字符
statement=connection.prepareStatement(sql);
//此处给站位符号进行传值操作,第一个问号下标为1
statement.setString(1,loginName);
statement.setString(2,loginPwd);
resultSet = statement.executeQuery(); //依旧是需要执行,需要返回结果集
//只有一条
if (resultSet.next()){
loginflag=true;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
if (resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement!=null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return loginflag;
}
private static Map initUI() {
Scanner scanner = new Scanner(System.in);
System.out.println("Please input the Username");
String Username = scanner.nextLine();
System.out.println("Please input the passWord");
String password = scanner.nextLine();
HashMap Userloginfo = new HashMap<>();
Userloginfo.put("loginName",Username);
Userloginfo.put("loginPwd",password);
return Userloginfo;
}
}
4)对比Statement 与 PreparedStatement
a:前者存在SQL注入问题,后者解决此问题
b:PreparedStatement 预先编译含有占位符的SQL语句,后续只需要简单传值,只编译一次,效率较高一些。
c:PreparedStatement 在编译期间会自动进行类型的安全检查。
注意:Statement 支持SQL语句的拼接,凡是业务需要进行SQL语句拼接的,必须使用Statement ,传值使用PreparedStatement
jdbc事务自动提交,只要执行任意一条DML语句,则自动提交一次;这是JDBC默认的事务行为。
结论:JDBC中只要执行任意一条DML语句就提交一次。
代码验证:
package com.fangjun;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ResourceBundle;
public class JdbcTest001 {
public static void main(String[] args) {
ResourceBundle jdbc = ResourceBundle.getBundle("jdbc");
String driver = jdbc.getString("driver");
String url = jdbc.getString("url");
String user = jdbc.getString("user");
String pass = jdbc.getString("pass");
Connection connection=null;
PreparedStatement statement=null; //这里修改成 PreparedStatement
ResultSet resultSet=null;
try {
Class.forName(driver);
connection = DriverManager.getConnection(url,user,pass);
statement = connection.prepareStatement("update dept set dname=? where deptno=?");
statement.setString(1,"x");
statement.setString(2,"30");
int count = statement.executeUpdate();
System.out.println(count); //执行到这里数据库已经被修改
statement.setString(1,"y");
statement.setString(2,"20");
count = statement.executeUpdate();
System.out.println(count);
} catch (Exception e) {
e.printStackTrace();
}
}
}
(补充 代码演示银行账户转账 总金额会产生错误)
package com.fangjun;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ResourceBundle;
public class JdbcTest001 {
public static void main(String[] args) {
ResourceBundle jdbc = ResourceBundle.getBundle(“jdbc”);
String driver = jdbc.getString(“driver”);
String url = jdbc.getString(“url”);
String user = jdbc.getString(“user”);
String pass = jdbc.getString(“pass”);
Connection connection=null;
PreparedStatement statement=null; //这里修改成 PreparedStatement
ResultSet resultSet=null;
try {
Class.forName(driver);
connection = DriverManager.getConnection(url,user,pass);
statement = connection.prepareStatement(“update t_act set balance=? where actno=?”);
statement.setDouble(1,10000);
statement.setInt(2,111);
int count = statement.executeUpdate();
System.out.println(count); //执行到这里数据库已经被修改
String s=null;
s.toString();
statement.setDouble(1,10000);
statement.setInt(2,222);
count += statement.executeUpdate();
System.out.println(count==2? "right":"error");
} catch (Exception e) {
e.printStackTrace();
}
}
}
将JDBC的事务修改成手动提交
package com.fangjun;
import java.sql.*;
import java.util.ResourceBundle;
public class JdbcTest001 {
public static void main(String[] args) {
ResourceBundle jdbc = ResourceBundle.getBundle("jdbc");
String driver = jdbc.getString("driver");
String url = jdbc.getString("url");
String user = jdbc.getString("user");
String pass = jdbc.getString("pass");
Connection connection=null;
PreparedStatement statement=null; //这里修改成 PreparedStatement
ResultSet resultSet=null;
try {
Class.forName(driver);
connection = DriverManager.getConnection(url,user,pass);
connection.setAutoCommit(false); //将jdbc的自动提交机制进行关闭
statement = connection.prepareStatement("update t_act set balance=? where actno=?");
statement.setDouble(1,10000);
statement.setInt(2,111);
int count = statement.executeUpdate();
System.out.println(count); //执行到这里数据库已经被修改
String s=null;
s.toString();
statement.setDouble(1,10000);
statement.setInt(2,222);
count += statement.executeUpdate();
System.out.println(count==2? "right":"error");
//程序执行到这里如果没有发生意外,可以提交所有事务.手动提交数据
connection.commit();
} catch (Exception e) {
//如果出现意外,在这里回滚事务,确保数据的安全
if (connection!=null){
try {
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
e.printStackTrace();
}
}
}
package com.fangjun;
import java.sql.*;
public class DbTools {
//工具类构造方法建议私有化
private DbTools() {
}
//将注册驱动的行为执行一次,在类加载的时候执行
static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取数据库连接对象,异常抛出,在主程序中进行捕捉
* @return
* @throws SQLException
*/
public static Connection getCollection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://localhost:3306/learn","root","333");
}
/**
* 关闭资源
* @param resultSet
* @param statement 面向接口编程,Statement是父接口
* @param connection
*/
public static void close(ResultSet resultSet, Statement statement,Connection connection){
if (resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement!=null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
JDBC借助工具类实现模糊查询
import com.fangjun.DbTools;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class MohuQuery {
public static void main(String[] args) {
Connection connection=null;
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
try {
connection = DbTools.getCollection();
preparedStatement = connection.prepareStatement("select ename from emp where ename like ?");
preparedStatement.setString(1,"_A%");//查询第二个字母为A
resultSet = preparedStatement.executeQuery();
while (resultSet.next()){
String ename = resultSet.getString("ename");
System.out.println(ename);
}
} catch (Exception e) {
e.printStackTrace();
}
finally {
DbTools.close(resultSet,preparedStatement,connection);
}
}
}
for update 悲观锁 锁住一行记录(行级锁)
jdbc代码演示行级锁
加行级锁
package com.fangjun;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 代码演示行级锁,这个进行查询,使用行级锁
*/
public class hangjisuo {
public static void main(String[] args) {
Connection connection=null;
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
try {
connection = DbTools.getCollection();
connection.setAutoCommit(false);//关闭jdbc的自动提交机制
preparedStatement = connection.prepareStatement("select ename,job,sal from emp where job=? for update");
preparedStatement.setString(1,"MANAGER");
resultSet=preparedStatement.executeQuery();
while (resultSet.next()){
System.out.println(resultSet.getString("ename")+","+resultSet.getString("job")+","+resultSet.getDouble("sal"));
}
connection.commit();//开启jdbc的手动提交机制
} catch (Exception e) {
if (connection!=null){ //avoid nullpointexception
try {
connection.rollback();//遇到错误的自动回滚事务机制
} catch (SQLException e1) {
e1.printStackTrace();
}
}
e.printStackTrace();
}finally {
DbTools.close(resultSet,preparedStatement,connection);
}
}
}
另外的代码对锁定数据执行修改操作
package com.fangjun;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 操作被锁定的数据
*/
public class hangjisuo01 {
public static void main(String[] args) {
Connection connection=null;
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
try {
connection = DbTools.getCollection();
connection.setAutoCommit(false);//关闭自动提交机制
preparedStatement=connection.prepareStatement("update emp set sal=sal*1.1 where job=?");
preparedStatement.setString(1,"MANAGER");
int count = preparedStatement.executeUpdate();
System.out.println(count);
connection.commit();//手动提交机制
} catch (Exception e) {
if (connection!=null){
try {
connection.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
e.printStackTrace();
}finally {
DbTools.close(null,preparedStatement,connection);
}
}
}