今日内容
- JDBC基本概念
- 快速入门
- 对JDBC中各个接口和类详解
JDBC:
- 概念:Java DataBase Connectivity Java 数据库连接, Java语言操作数据库
* JDBC本质:其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。 - 快速入门:
- 步骤:
1. 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
2. 注册驱动 Class.forname载入注册
3. 获取数据库连接对象 Connection
4. 定义sql
5. 获取执行sql语句的对象 Statement
6. 执行sql,接受返回结果
7. 处理结果
8. 释放资源
- 步骤:
代码实现:
public static void main(String[] args) {
//1. 导入驱动jar包
//2. 注册驱动
Statement sta=null;
Connection conn=null;
try {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
String sql="insert into account values(null,'王五',3000)";
//2.获取连接对象
conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/db3",
"root","CR123123");
//3.定义sql
//4.获取执行sql对象
sta=conn.createStatement();
//5. 执行sql
int count=sta.executeUpdate(sql);
System.out.println(count);
if(count>0)
{
System.out.println("成功");
}
else
System.out.println("失败");
}
catch (ClassNotFoundException | SQLException e){
e.printStackTrace();
}
finally{
if(sta!=null){
try {
sta.close();
}
catch (SQLException E){
E.printStackTrace();
}
}
if(conn!=null){
try {
sta.close();
}
catch (SQLException E){
E.printStackTrace();
}
}
}
}
- 详解各个对象:
-
DriverManger:驱动管理对象
功能:
1. 注册驱动:告诉程序应该使用哪一个数据库驱动jar(如例子中使用的是Mysql数据库驱动static void registerDriver(Driver driver) :注册与给定的驱动程序 DriverManager 。
但是在实际写代码时却用的是
`Class.forName("com.mysql.jdbc.Driver");
通过查看com.mysql.Driver类存在静态代码块,在执行Class.forname函数时会自动执行类的静态代码块。存在的静态代码块是
static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } }
这样一目了然。
而mysql5之后的驱动jar包可以省略注册驱动的步骤。
原因:在mysql5以后导入jar包后
会自动生成java.sq.Driver文件
文件内容:
当你没有写注册驱动的代码时,系统会自动帮你读取到该文件,自动完成注册。- 获取数据库连接:
- 方法:static Connection getConnection(String url, String user, String password)
参数:-
url:指定连接的路径
- 语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
- 例子:jdbc:mysql://localhost:3306/db3
细节:如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称
- user:用户名
- password:密码
-
-
Connection:数据库连接对象
- 功能:
- 获取执行sql的对象:
Statement CreateStatement();
PreparedStatement prepareStatement(String sql) - 管理事务:
- 开启事务:
SetAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务 - 提交事务
commit() - 回滚事务:rollback();
一般在try catch finally中
在finally中进行回滚操作
- 开启事务:
- 获取执行sql的对象:
- 功能:
-
Statement:
执行sql的对象-
boolean execute(String sql):执行任意的sql
-
int executeupdate(String sql):执行DML(iinsert update delete)语句 和DDL(create ,alter ,drop)语句
返回值 :数据库中表的影响的行数,可以通过这个影响的行数判断DML语句是否执行成功 返回值>0的则执行成功,反之,则失败。 -
ResultSet executeQuery(String sql):
执行DQL(select)语句:
练习:public static void main(String[] args) { //1. 导入驱动jar包 //2. 注册驱动 Statement sta=null; Connection conn=null; try { //1.注册驱动 Class.forName("com.mysql.jdbc.Driver"); String sql="insert into account values(null,'王五',3000)"; //2.获取连接对象 conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root","CR123123"); //3.定义sql //4.获取执行sql对象 sta=conn.createStatement(); //5. 执行sql int count=sta.executeUpdate(sql); System.out.println(count); if(count>0) { System.out.println("成功"); } else System.out.println("失败"); } catch (ClassNotFoundException | SQLException e){ e.printStackTrace(); } finally{ if(sta!=null){ try { sta.close(); } catch (SQLException E){ E.printStackTrace(); } } if(conn!=null){ try { sta.close(); } catch (SQLException E){ E.printStackTrace(); } } } }
-
-
ResultSet:结果集对象,封装查询结果
-
boolean next():
游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回false,如果不是则返回true -
getxxx(参数):
- Xxx:代表数据类型 如: int getInt() , String getString()
-
- int:代表列的编号,从1开始 如: getString(1)
- String:代表列名称。 如: getDouble(“balance”)
-
使用步骤:
1. 游标向下移动一行
2. 判断是否有数据
3. 获取数据while(re.next()){ int id=re.getInt(1); String name=re.getString("name"); double balance=re.getDouble("balance"); System.out.println(id+name+balance); } ```
-
1. 定义Emp类
2. 定义方法 public List findAll(){}
3. 实现方法 select * from emp; -
String sql = "select * from emp";
rs = st.executeQuery(sql);
list=new ArrayList<>();
while (rs.next()) {
emp=new Emp();
int id = rs.getInt(1);
String name = rs.getString(2);
int job_id = rs.getInt(3);
int mgr = rs.getInt(4);
Date date = rs.getDate(5);
double salary = rs.getDouble(6);
double bonus = rs.getDouble(7);
int dept_id = rs.getInt(8);
emp.setId(id);
emp.setEname(name);
emp.setJob_id(job_id);
emp.setMgr(mgr);
emp.setJoindate(date);
emp.setSalary(salary);
emp.setBonus(bonus);
emp.setDept_id(dept_id);
list.add(emp);
}
- PreparedStatement:执行sql的对象
-
SQL注入问题:就是在拼接sql的时候,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题
- 如sql:select * from user where username = ‘fhdsjkf’ and password = ‘a’ or ‘a’ = ‘a’
‘a’ = ‘a’ 结果是true,因此会使得用户成功登录
- 如sql:select * from user where username = ‘fhdsjkf’ and password = ‘a’ or ‘a’ = ‘a’
-
解决sql注入问题:使用PreparedStatement对象来解决
-
预编译的SQL:参数使用?作为占位符
-
步骤
- 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
- 注册驱动
- 获取数据库连接对象 Connection
- 定义sql
* 注意:sql的参数使用?作为占位符。 如:select * from user where username = ? and password = ?; - 获取执行sql语句的对象 PreparedStatement Connection.prepareStatement(String sql)
- 给?赋值:
* 方法: setXxx(参数1,参数2)
* 参数1:?的位置编号 从1 开始
* 参数2:?的值 - 执行sql,接受返回结果,不需要传递sql语句
8. 处理结果
9. 释放资源
-
注意:后期都会使用PreparedStatement来完成增删改查的所有操作
1. 可以防止SQL注入
2. 效率更高,在Mysql中
(缓存机制中有预编译语句,如果查询语句的结构相同,就不会去再进行编译,而是进行一些参数的替换,这样就会提高效率,但是在进行con.prepareStatement(sql)和prst.executeUpdate()中会进行两次访问数据库操作,第一次是解析和优化这个查询,第二次是进行查询操作,如果该数据库是以分布式为架构的话,实际上网络请求成本也许会大于查询的效率优化。实际上Statement的执行,不一定是没有缓存的,而恰恰相反, 现在大多数的数据库软件的查询分析模块, 会透明的在第一次执行 像insert into tb_name (col1,col2) values (‘11’,‘22’) 这样的sql简单语句时将其转化为 insert into tb_name (col1,col2) values (?,?), 为后面重复执行缓存优化。 当然不同的数据库实现的策略是不同的相关的资料见解请参考
[PreparedStatement效率为什么高/为什么要使用PreparedStatement代替Statement]
-
抽取JDBC工具类 : JDBCUtils
- 目的:简化书写
- 分析:
- 注册驱动也抽取
- 抽取一个方法获取连接对象
* 需求:不想传递参数(麻烦),还得保证工具类的通用性。
* 解决:配置文件
jdbc.properties
url=
user=
password= - 抽取一个方法释放资源
- 分析:
package JDBC;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.sql.*;
import java.util.Properties;
public class JDBCutills {
/**
* 获取连接
* @ return 连接对象
*/
private static String url;
private static String user;
private static String password;
private static String Driver;
static {
try{
Properties p=new Properties();
ClassLoader classLoader = JDBCutills.class.getClassLoader();
URL resource = classLoader.getResource("jdbc.properties");
String path = resource.getPath();
System.out.println(path);
p.load(new FileReader(path));
url = p.getProperty("url");
user = p.getProperty("user");
password = p.getProperty("password");
Driver = p.getProperty("driver");
Class.forName(Driver);
}
catch(IOException| ClassNotFoundException E){
E.printStackTrace();
}
}
public static Connection getConnection() throws SQLException{
return DriverManager.getConnection(url,user,password);
}
public static void close(Statement st, Connection c, ResultSet rs){
if (c != null) {
try {
c.close();
}
catch (SQLException E) {
E.printStackTrace();
}
}
if (st != null){
try {
st.close();
}
catch (SQLException E) {
E.printStackTrace();
}
}
if (rs!=null) {
try {
rs.close();
}
catch (SQLException E) {
E.printStackTrace();
}
}
}
}
- 练习:
* 需求:
1. 通过键盘录入用户名和密码
2. 判断用户是否登录成功
* select * from user where username = “” and password = “”;
* 如果这个sql有查询结果,则成功,反之,则失败
public class exercise {
/**
* 登录方法
*/
public Boolean login(String username, String password) {
if (username == null || password == null) {
return false;
}
Connection c = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
Connection connection = JDBCutills.getConnection();
String sql = "SELECT * from user where username =? and password=?";
ps= connection.prepareStatement(sql);
ps.setString(1,username);
ps.setString(2,password);
rs = ps.executeQuery();
return rs.next();
} catch (SQLException E) {
E.printStackTrace();
} finally {
JDBCutills.close(ps, c, rs);
}
return false;
}
public static void main(String[] args) {
//1.键盘录入,接受用户名和密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = sc.nextLine();
System.out.println("请输入密码:");
String password = sc.nextLine();
//2.调用方法
boolean flag = new exercise().login(username, password);
//3.判断结果,输出不同语句
if(flag){
//登录成功
System.out.println("登录成功!");
}else{
System.out.println("用户名或密码错误!");
}
}
}
使用Connection对象来管理事务
- 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
* 在执行sql之前开启事务 - 提交事务:commit()
* 当所有sql都执行完提交事务 - 回滚事务:rollback()
* 在catch中回滚事务
try{
c=JDBCutills.getConnection();
c.setAutoCommit(false);
String sql1="update account set balance=balance-? where id=?";
String sql2="update account set balance=balance+? where id=?";
p1 = c.prepareStatement(sql1);
p2 = c.prepareStatement(sql2);
p1.setInt(1,500);
p1.setInt(2,1);
p2.setInt(1,500);
p2.setInt(2,2);
p1.executeUpdate();
//手动制造异常
p2.executeUpdate();
c.commit();
}
catch (Exception E){
if(c!=null) {
try {
c.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
E.printStackTrace();
}
finally {
JDBCutills.close(p1, c, null);
JDBCutills.close(p2, c, null);
}