JDBC是什么?
JDBC是一套接口,各大厂商来实现这套接口,进行数据库连接操作
比如Mysql驱动,Oracle驱动,sqlServer驱动,高斯驱动
以Mysql为例:
JDBC编程六步
第一步:注册驱动
第二步:获取连接
第三步:获取数据库操作对象
第四步:执行SQL语句(DQL DML....)
第五步:处理查询结果集(只有当第四步执行的是select语句的时候,才有这第五步处理查询结果集。)
第六步:释放资源
关于JDBC的几个演示demo
增删改都可以使用int executeUpdate(String sql)方法
查询ResultSet excuteQuery(String sql)方法 <DQL>
注意,使用JDBC写sql时候不需要写;分号
public class JDBCTest01 {
public static void main(String[] args){
Connection connection=null;
Statement statement=null;
try {
//1.加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
connection = DriverManager.getConnection("url", "username", "password");
//事实上该Connection对象是 Connection connection=new com.mysql.cj.jdbc.JDBC4Connection()
//可以输出看看 System.out.println(connection);
//3.获取数据库操作对象
statement = connection.createStatement();
//4.执行sql,获得结果集(查询)
int i = statement.executeUpdate("insert into dept(deptNo,dname,loc) values(50,'人事部','北京')");
//5.查询需要处理结果集(不是查询一般不用处理)
System.out.println(i==1?"保存成功":"保存失败");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
//6.释放资源 从小到大依次关闭 注意分别try catch
try {
if(statement!=null){
statement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(connection!=null){
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
删除
public class JDBCTest02 {
public static void main(String[] args){
Connection connection=null;
Statement statement=null;
try {
//1.加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
connection = DriverManager.getConnection("url", "username", "password");
//事实上该Connection对象是 Connection connection=new com.mysql.cj.jdbc.JDBC4Connection()
//可以输出看看 System.out.println(connection);
//3.获取数据库操作对象
statement = connection.createStatement();
//4.执行sql,获得结果集(查询)
statement.executeUpdate("delete from dept where deptNo=40");
//5.查询需要处理结果集(不是查询一般不用处理)
System.out.println(i==1?"删除成功":"删除失败");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
//6.释放资源 从小到大依次关闭 注意分别try catch
try {
if(statement!=null){
statement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(connection!=null){
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
注册驱动的另外一种方式
public class JDBCTest04 {
public static void main(String[] args) throws Exception {
Connection connection=null;
Statement statement=null;
ResultSet resultSet=null;
try {
//1.加载驱动
DriverManager.registerDriver(new Driver());
//2.获取连接
connection = DriverManager.getConnection("url", "username", "password");
//事实上该Connection对象是 Connection connection=new com.mysql.cj.jdbc.JDBC4Connection()
//可以输出看看 System.out.println(connection);
//3.获取数据库操作对象
statement = connection.createStatement();
//4.执行sql,获得结果集(查询)
resultSet = statement.executeQuery("select * from user");
//5.查询需要处理结果集(不是查询一般不用处理)
while(resultSet.next()){
//getString()方法,不管你数据库里字段类型是什么,我都按String取出来
//getString()有两个重载的方法,一个根据下标取从1开始,一个根据字段取比如"deptNo"
//也可以按指定类型取出,可以按列名也可以下标
String str1 = resultSet.getString(1);
}
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
//6.释放资源 从小到大依次关闭 注意分别try catch
try {
if(resultSet!=null){
resultSet.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(statement!=null){
statement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(connection!=null){
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
关于ResultSet取值方式:
1.getString(int i),可以以下标的方式取出,从1开始.
2.getString(String c),可以以列名的方式取出
3.getInt(int i),getDouble(int i)等按下标的方式取出,从1开始.
4.getInt(String c),getDouble(String c),按指定类型取出,注意可能出现类型无法转换的问题
主要是Class.forName("com.mysql.cj.jdbc.Driver");通过反射加载该.class文件到内存
和DriverManager.registerDriver(new Driver()),两种方式有啥区别
看下mysql的实现类
通过Class.forName("com.mysql.cj.jdbc.Driver");将该类加载到内存,static静态代码块会在该.class被加载的时候执行.静态代码块(static block)在类加载过程中执行,且只执行一次。它用于在类加载时进行一些初始化操作或执行一些静态的计算逻辑。
做了DriverManager.registerDriver(new Driver())这么件事
关于Statement和PrepareStatement区别
/**
* 实现功能:
* 1.需求:模拟用户登录功能的实现
* 2.业务描述:
* 程序运行的时候,提供一个输入的入口,可以让用户输入用户名和密码
* 用户输入用户名和密码之后,提交信息,java程序收集刀用户信息
* java程序连接数据库验证用户名和密码是否合法
* 合法:显示登录成功
* 不合法:显示登录失败
* 3.数据准备
* 在实际开发中,表的设计会使用专业的建模工具,我们这里安装一个建模工具:PowerDesigner
* 使用PD工具来进行数据库表的设计
* @author hrui
* @date 2023/5/26 16:35
*/
public class JDBCTest01 {
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) {
//JDBC代码
Connection connection=null;
Statement statement=null;
ResultSet resultSet=null;
boolean loginSuccess=false;
try {
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
connection = DriverManager.getConnection("url", "username", "password");
//3.获取数据库操作对象
statement = connection.createStatement();
//4.执行SQL
String userName=userLoginInfo.get("loginName");
String password=userLoginInfo.get("loginPwd");
resultSet = statement.executeQuery("select * from t_user where userName='"+userName+"' and passWord='"+password+"'");
//5.处理结果集 因为只有一条记录
if(resultSet.next()){
loginSuccess=true;
}
return loginSuccess;
} catch (Exception e) {
e.printStackTrace();
}finally {
//6.释放资源
try {
if(resultSet!=null){
resultSet.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (statement!=null){
statement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(connection!=null){
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
return false;
}
/**
* 初始化用户页面
* @return 用户输入的用户名和密码等登录信息
*/
private static Map<String, String> initUI() {
Scanner scanner=new Scanner(System.in);
System.out.println("用户名:");
String loginName=scanner.nextLine();
System.out.println("密码:");
String loginPwd=scanner.nextLine();
Map<String,String> userLginInfo=new HashMap<>();
userLginInfo.put("loginName", loginName);
userLginInfo.put("loginPwd", loginPwd);
return userLginInfo;
}
}
演示SQL注入 随便乱输入个
asd' or '1'='1
下面引出了PreoareStatement
导致SQL注入的根本原因:用户输入的信息中含有SQL语句关键字,并且这些关键字参与SQL语句的编译过程,导致SQL语句的原意被扭曲,进而达到SQL注入.最主要原因参与了编译
解决SQL注入问题
只要用户提供的信息不参与SQL语句的编译过程,问题就解决了
即使用户提供的信息中含有SQL语句的关键字,但是没有参与编译,不起作用.
PreoareStatement继承了java.sql.Statement
PreoareStatement是属于预编译的数据库操作对象
PreoareStatement原理是:预先对SQL语句进行编译,然后再给SQL语句传"值"
PreoareStatement:
private static boolean login(Map<String, String> userLoginInfo) {
//JDBC代码
Connection connection=null;
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
boolean loginSuccess=false;
try {
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
connection = DriverManager.getConnection("url", "username", "password");
String userName=userLoginInfo.get("loginName");
String password=userLoginInfo.get("loginPwd");
//?表示一个占位符,注意?占位符不能使用单引号括起来
String sql="select * from t_user where userName=? and passWord=?";
System.out.println(sql);
//3.获取预编译的数据库操作对象
preparedStatement = connection.prepareStatement(sql);
//给占位符?号传值 第一个问号下标是1
preparedStatement.setString(1, userName);
preparedStatement.setString(2,password);
//4.执行
resultSet = preparedStatement.executeQuery();
//5.处理结果集 因为只有一条记录
if(resultSet.next()){
loginSuccess=true;
}
return loginSuccess;
} catch (Exception e) {
e.printStackTrace();
}finally {
//6.释放资源
try {
if(resultSet!=null){
resultSet.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (preparedStatement!=null){
preparedStatement.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(connection!=null){
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
return false;
}
解决SQL注入的关键:用户提供的信息中即使含又SQL语句的关键字,但是这些关键字并没有参与编译,不起作用
关于Statement和PrepareStatement对比
1.Statement存在SQL注入问题,PrepareStatement解决了SQL注入问题
2.执行效率:Statement是编译一次执行一次,PrepareStatement是编译一次执行多次.PrepareStatement效率较高.PrepareStatement会在编译阶段做类型安全检查
在什么情况下必须使用Statement:凡是业务方面要求需要进行sql拼接的必须用Statement
比如说升序降序 个人认为说PrepareStatement会加''单引号说法是错误的,应该说PrepareStatement执行SQL语句时会根据参数的类型来处理参数的转义和引号的添加。这个当我没说,查询时候只要只要类型间可以转换是可以的 比如说数据库id可能是int类型,但是select * from t_user where id='1'也是ok的
String str="desc";
String sql="select ename from emp order by ename "+str;这里如果用PrepareStatement就会出错了,执行该SQL必须用Statement 先进性SQL拼接
使用PrepareStatement做增删改
使用PrepareStatement的时候需要注意:
如果这样
我做了主键id自增,虽然4号位置numbera数据库是int,但是也没有报错
修改
删除
上面理论上来说应该是preparedStatement.setInt setDouble等等,但是测试,setString也是可以的.但是如果数据库是int 你setString(1,"haha") 那么会报错,报错原因是 'haha'无法转换为int
JDBC事务自动提交机制
JDBC中事务默认是自动提交的,只要执行任意一条DML(增删改语句),则自动提交
但是实际业务中,通常是N条DML语句共同联合完成的,必须保证DML语句在同一个事务中同时成功或者同时失败.
在获取数据库连接对象时
重点三行代码?
conn.setAutoCommit(false);开启手动提交事务
conn.commit();//提交事务
conn.rollback();出现异常则回滚
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
try {
// 1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 2、获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root","333");
// 将自动提交机制修改为手动提交
conn.setAutoCommit(false); // 开启事务
// 3、获取预编译的数据库操作对象
String sql = "update t_act set balance = ? where actno = ?";
ps = conn.prepareStatement(sql);
// 给?传值
ps.setDouble(1, 10000);
ps.setInt(2, 111);
int count = ps.executeUpdate();
//String s = null;
//s.toString();
// 给?传值
ps.setDouble(1, 10000);
ps.setInt(2, 222);
count += ps.executeUpdate();
System.out.println(count == 2 ? "转账成功" : "转账失败");
// 程序能够走到这里说明以上程序没有异常,事务结束,手动提交数据
conn.commit(); // 提交事务
} catch (Exception e) {
// 回滚事务
if(conn != null){
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
e.printStackTrace();
} finally {
// 6、释放资源
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
刚好项目要用,工具类
/**
* @author hrui
* @date 2023/5/28 15:28
*/
public class DBUtil {
static{
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private DBUtil(){
}
public static Connection getConnection(String url,String username,String password) throws SQLException {
return DriverManager.getConnection(url, username, password);
}
public static void close(Connection conn, Statement st, ResultSet rs){
try {
if(rs!=null){
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(st!=null){
st.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(conn!=null){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public class JDBCTest07 {
public static void main(String[] args) {
Connection connection=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
connection = DBUtil.getConnection("url", "username", "password");
//获取预编译的数据库操作对象
String sql="select * from t_user where userName like ?";
ps=connection.prepareStatement(sql);
ps.setString(1, "_h%");
rs = ps.executeQuery();
if(rs.next()){
System.out.println(rs.getString(1));
System.out.println(rs.getString(2));
System.out.println(rs.getString(3));
System.out.println(rs.getString(4));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtil.close(connection, ps, rs);
}
}
}
JDBC将查询结果转Map
public class JdbcExample {
public static void main(String[] args) {
try {
// 连接数据库
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase", "username", "password");
Statement statement = connection.createStatement();
// 执行查询语句
ResultSet resultSet = statement.executeQuery("SELECT * FROM mytable");
// 封装查询结果
List<Map<String, String>> resultList = new ArrayList<>();
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
while (resultSet.next()) {
Map<String, String> row = new HashMap<>();
for (int i = 1; i <= columnCount; i++) {
String columnName = metaData.getColumnName(i);
String columnValue = resultSet.getString(i);
row.put(columnName, columnValue);
}
resultList.add(row);
}
// 打印查询结果
for (Map<String, String> row : resultList) {
for (Map.Entry<String, String> entry : row.entrySet()) {
String columnName = entry.getKey();
String columnValue = entry.getValue();
System.out.println(columnName + ": " + columnValue);
}
System.out.println("---------------------");
}
// 关闭连接
resultSet.close();
statement.close();
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
行级锁概念(又被称为悲观锁)
也就是说加了 for update job='MANAGER'的三条记录被锁住了
悲观锁和乐观锁机制
行级锁演示