个人java学习路线----JDBC
介绍
JDBC: Java DataBase Connectivity (Java语言连接数据库)
本质是sun制定的一套接口
接口都有调用者和实现者
面向接口调用,面向接口写实现类,这都属于面向接口编程
面向接口编程是为了解耦合
JDBC编程六步
第一步:注册驱动(告诉Java程序,即将要连接的是哪个品牌 的数据库)
第二步:获取连接(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的,使用完一定要关闭通道)
第三步:获取数据库操作对象(专门执行sql语句的对象)
第四步:执行sql语句(DQL DML)
第五步:处理查询结果集(第四步执行了select才有这步)
第六步:释放资源(java和数据库之间属于进程通信,使用完必须关闭通道)
初次使用
public class JDBCTest1{
public static void main(String[] args){
Statement stmt=null;
Connection conn=null;
try{
//1-(1-6为JDBC六步)注释掉的还有oracle相关协议等
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
//2
String url="jdbc:mysql://127.0.0.1:3306/xxx";
//String url="jdbc:oracle:thin:@localhost:1521:orcl";
String user="root";
String password="root";
conn=DriverManager.getConnection(url,user,password);
System.out.println("sql----->"+conn);
//3
stmt=conn.createStatement();
//4
String sql ="insert into dept2(deptno,dname,loc) values(50,'人事部门','北京')";
//专门执行DML语句的(insert delete update)
//返回值是“影响数据库中的记录条数”
int count=stmt.executeUpdate(sql);
System.out.println(count==1?"save success":"save failed");
}catch(SQLException e){
e.printStackTrace();
}finally{
//6 释放资源
try{
if(stmt != null){
stmt.close();
}
}catch(SQLException e){
e.printStackTrace();
}
try{
if(conn != null){
conn.close();
}
}catch(SQLException e){
e.printStackTrace();
}
}
}
}
注册驱动升级
com.mysql.jdbc.Driver()方法中含有注册驱动的静态代码块,只需类加载就行,方便通过反射机制,操作properties文件,容易修改使用
//DriverManager.registerDriver(new com.mysql.jdbc.Driver());
Class.forName("com.mysql.jdbc.Driver");
运用poperties文件:
项目下建立peoperties文件:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/数据库名
user=root
password=root
连接数据库改进:
public class JDBCTest4{
public static void main(String[] args){
ResourceBundle bundle=ResourceBundle.getBundle("jdbctest4");
String driver=bundle.getString("driver");
String url=bundle.getString("url");
String user=bundle.getString("user");
String password=bundle.getString("password");
Connection conn=null;
Statement stmt=null;
try{
Class.forName(driver);
conn=DriverManager.getConnection(url,user,password);
stmt=conn.createStatement();
String sql="insert into dept2(deptno,dname,loc) values(50,'wuliao','jialidun')";
int count=stmt.executeUpdate(sql);
System.out.println(count==1?"insert success":"insert failed");
}catch(SQLException e){
e.printStackTrace();
}catch(ClassNotFoundException e){
e.printStackTrace();
}finally{...}//省略
}
}
模拟用户登录
ps:数据库相关改了
public class JDBCTest1 {
public static void main(String[] args) {
//初始化界面
Map<String,String> userLoginInfo = initUI();
//验证用户名密码
boolean loginSuccess=login(userLoginInfo);
System.out.println(loginSuccess?"登录成功":"登录失败");
}
/**
* 用户登录
* @param userLoginInfo 用户登录信息
* @return false 表示登录失败,true表示登录成功
*/
private static boolean login(Map<String, String> userLoginInfo) {
boolean loginSuccess=false;
//JDBC代码
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
try {
//1注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2获取连接
conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/xxx","root","root");
//3获取数据库操作对象
stmt=conn.createStatement();
//4执行sql
String sql="select * from t_user where loginName='"+userLoginInfo.get("loginName")+"' and loginPwd='"+userLoginInfo.get("loginPwd")+"'";
/*该命令有bug,当输入任意用户名密码后,在密码后加(' or 'xxx'='xxx) ,因为该sql语句会编入mysql中,就变成了where ...and... or true=true,必定成功*/
rs=stmt.executeQuery(sql);
//5处理结果集
if(rs.next()){
loginSuccess=true;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
//6释放资源
//....省略
}
return loginSuccess;
}
/**
* 初始化用户界面
* @return 用户输入的用户名密码等信息
*/
private static Map<String, String> initUI() {
Scanner s=new Scanner(System.in);
System.out.print("用户名:");
String loginName=s.nextLine();
System.out.print("密码:");
String loginPwd=s.nextLine();
Map<String,String> userLoginInfo =new HashMap<>();
userLoginInfo.put("loginName",loginName);
userLoginInfo.put("loginPwd",loginPwd);
return userLoginInfo;
}
}
sql注入问题和PerparedStatement接口
模拟用户登录代码中说了有sql注入问题,即在登录输入数据时我们可以适当的输入sql语言从而破坏原有sql语言结构。
为了防止用户输入sql语言出现了PerparedStatement接口,PerparedStatement会预编译sql,用户输入的数据始终不会破坏原sql语言结构。
public class JDBCTest2 {
public static void main(String[] args) {
Map<String,String> userLoginInfo = initUI();
boolean loginSuccess=login(userLoginInfo);
System.out.println(loginSuccess?"登录成功":"登录失败");
}
private static boolean login(Map<String, String> userLoginInfo) {
boolean loginSuccess=false;
Connection conn=null;
//*
PreparedStatement ps=null;
ResultSet rs=null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/xx","root","root");
//*
String sql="select * from t_user where loginName=? and loginPwd=?";//?占位符
ps=conn.prepareStatement(sql);
ps.setString(1,userLoginInfo.get("loginName"));//占位符传值
ps.setString(2,userLoginInfo.get("loginPwd"));
rs=ps.executeQuery();
if(rs.next()){
loginSuccess=true;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
//...省略
}
return loginSuccess;
}
private static Map<String, String> initUI() {
Scanner s=new Scanner(System.in);
System.out.print("用户名:");
String loginName=s.nextLine();
System.out.print("密码:");
String loginPwd=s.nextLine();
Map<String,String> userLoginInfo =new HashMap<>();
userLoginInfo.put("loginName",loginName);
userLoginInfo.put("loginPwd",loginPwd);
return userLoginInfo;
}
}
需要sql注入时举例
一般我们只用PerparedStatement类就行。
但有时我们也需要Statement,例如需要用户输入数据完成升序降序排序时需要sql注入,否则完不成需求
增删改
注意方法就行
public class JDBCTest4 {
public static void main(String[] args) {
Connection conn=null;
PreparedStatement ps=null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/xxx","root","root");
String sql="update dept2 set dname=? ,loc=? where deptno=?";
ps=conn.prepareStatement(sql);
ps.setInt(3,50);
ps.setString(1,"wudigongsi");
ps.setString(2,"chaodadifang");
int count=ps.executeUpdate();
System.out.println(count);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
//....
}
}
}
JDBC事务
事务是必须的
/*
* JDBC中事务时自动提交的,只要执行任意一条DML语句,则自动提交一次
* 但在实际的业务中,通常都是N条DML语句共同联合才能完成的,必须保证他们这些DML语句在同一个事务中同时成功或同时失败
* 以下程序验证JDBC只要执行任意一条DML语句,就提交一次
* */
public class JDBCTest5 {
public static void main(String[] args) {
Connection conn=null;
PreparedStatement ps=null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/xxx","root","root");
String sql="update dept2 set dname=? where deptno=?";
ps=conn.prepareStatement(sql);
ps.setInt(2,50);
ps.setString(1,"wudigongsi");
int count=ps.executeUpdate();
System.out.println(count);
ps.setString(1,"asdsad");
ps.setInt(2,40);
count=ps.executeUpdate();
System.out.println(count);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
//...
}
}
}
工具类DBUtil
到这我们会发现我们写JDBC时,会有很多重复的代码,我们完全可以创建一个公共类来减少代码的重复,这里进行初步的抽取
/**
* JDBC工具类,简化JDBC编程
*/
public class DBUtil {
private DBUtil(){}
static {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException{
return DriverManager.getConnection("jdbc:mysql://localhost:3306/xxx","root","root");
}
/**
* 关闭资源
* @param conn 连接对象
* @param ps 数据库操作对象
* @param rs 结果集
*/
public static void close(Connection conn, Statement ps, ResultSet rs){
if (rs != null) {
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
这时我们写JDBC就会方便很多:
public class JDBCTest7 {
public static void main(String[] args) {
Connection conn= null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
conn=DBUtil.getConnection();
String sql="select ename from emp where ename like ?";
ps=conn.prepareStatement(sql);
ps.setString(1,"_A%");
rs=ps.executeQuery();
while(rs.next()){
System.out.println(rs.getString("ename"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
DBUtil.close(conn,ps,rs);
}
}
}
开启事务,专门进行查询,并且使用行级锁/悲观锁,锁住相关的记录
简单看一下
public class JDBCTest81 {
public static void main(String[] args) {
Connection conn= null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
conn= DBUtil.getConnection();
conn.setAutoCommit(false);
String sql="select ename,job,sal from emp2 where job=? for update ";
ps=conn.prepareStatement(sql);
ps.setString(1,"MANAGER");
rs=ps.executeQuery();
while (rs.next()){
System.out.println(rs.getString("ename")+","+rs.getString("job")+","+rs.getDouble("sal"));
}
conn.commit();
} catch (SQLException throwables) {
if(conn !=null){
try {
conn.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
throwables.printStackTrace();
}finally {
DBUtil.close(conn,ps,rs);
}
}
}