JDBC
JDBC是什么?
jdbc是一个由sun公司(Oracle)提出的规范(一套接口),jdbc指的就是java语言连接数据库
jdbc就是一套面向所有数据库厂家的接口
每一个数据库厂家都希望可以通过java语言连接到自己开发的数据库,
这个时候,厂家肯定要写一套程序,java语言连接数据库的具体实现,这个实现类必须要实现JDBC接口
- 作为开发人员来说,我们可能会连接到很多组不同的数据库,根据公司的业务不同,可能会连接mysql,SqlServer等等,但是我们不需要关注数据库具体的实现类是什么。我们关注的是jdbc这样的一套接口,因为这个接口提供了具体获取数据库对象的方法,还有相关执行增删改查的方法,还有相关事务提交的方法,事物处理的方法,我们只需要关注接口中有哪些方法,方法具体的实现交由数据库的厂家去完成!
例如:java语言连接 Mysql
mysql的厂家就必须要实现jdbc这个接口
JDBC的驱动类型
- ODBC是微软为了解决跨数据平台的一个方案,sun提供支持
- 部分java驱动程序使用特定数据库的API(一般是C和C++)将jdbc翻译成数据库的特定代码
- Jdbc-net将jdbc翻译成不针对特定数据库的网络协议,由数据库服务器翻译成特定代码
- 本地协议纯java驱动程序将jdbc请求转化成针对特定数据库的网络协议
JDBC的用途
- 加载对应数据库驱动(Load Driver)
- 与数据库建立连接(connection)
- 发送操作数据库的语句(createStatement)
- 执行并处理返回结果(executeQuery)
Java链接数据库
保证mysql服务是开启的
将java包导入到工程lib下,并且添加到类路径下(保证版本对应)
注册驱动,使用Class类下的一个静态方法forName,使得类加载
获取数据库连接 DriverManager类提供了getConnection方法 (需要传入数据源信息)
获取数据库操作对象 createStatement方法
编写sql语句
按照sql语句的类别调用不同方法,获取到返回的结果
关闭数据库资源(放在finally语句块中)
package com.os467.test;
import java.sql.*;
/**
* JDBC编程六步:
* 1.注册驱动
* 2.获取数据库连接
* 3.获取数据库操作对象
* 4.执行sql
* 5.返回记录条数/获取结果集对象
* 6.关闭资源
*/
public class JdbcTest01 {
public static void main(String[] args) {
//使三个实例作为成员变量存在在类中,使得finally语句块中可以读取到
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try{
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取数据库链接
/**
* String url 链接数据库的地址
* String user 数据库的用户名 root
* String password 数据库密码 root
* jdbc:mysql:// 协议
* localhost 本地的ip地址
* 3306 默认端口号
* os467test 需要使用的数据库
* ?serverTimezone=GMT 设置时区
*/
String url = "jdbc:mysql://localhost:3306/os467test?serverTimezone=GMT";
String user = "root";
String password = "root";
connection = DriverManager.getConnection(url, user, password);
System.out.println(connection);
//3.获取数据库操作对象
statement = connection.createStatement();
//4.编写sql语句,执行sql
String sql = "select * from emp";
//5.执行sql,获取返回的结果集对象,遍历结果集
resultSet = statement.executeQuery(sql);
while(resultSet.next()){
//取出结果集中的数据
int empno = resultSet.getInt("EMPNO");
String ename = resultSet.getString("ENAME");
System.out.println("员工的编号为"+empno+" 员工的姓名为"+ename);
}
}catch (Exception e){
e.printStackTrace();
}finally {
//6.关闭数据库资源,先进行非空校验,对close()可能发生的异常进行处理
if (resultSet != null){
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (statement != null){
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (connection != null){
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
注意:在为字符集添加中文数据时,在url后面用**&拼接characterEncoding=utf-8**来设置字符集
并且数据库校对中每个字段的字符集也要设置为utf-8
package com.os467.test;
import javax.swing.*;
import java.sql.*;
/**
* 往学生表中添加数据
*/
public class JdbcTest03 {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
try {
//加载jdbc驱动
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/os467test?serverTimezone=GMT&characterEncoding=utf-8";
String user = "root";
String password = "root";
//链接数据库
connection = DriverManager.getConnection(url, user, password);
//获取操作对象
statement = connection.createStatement();
String sql = "insert into tb_student(name,age,hobby)" +
"value('Tom',18,'打篮球')";
int num = statement.executeUpdate(sql);
if (num != 0){
System.out.println("添加成功");
}else{
System.out.println("添加失败");
}
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭数据库资源,避免占用系统资源
if (statement != null){
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (connection != null){
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
//模拟用户登入
package com.os467.test;
import java.sql.*;
import java.util.Scanner;
public class UserJDBCTest02 {
public static void main(String[] args) {
//创建控制台打印对象
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = scanner.next();
System.out.println("请输入密码:");
String password = scanner.next();
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try{
//注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/web_test?serverTimezone=GMT&characterEncoding=utf-8";
//获取数据库对象
connection = DriverManager.getConnection(url,"root","root");
//获取数据库操作对象
statement = connection.createStatement();
resultSet = statement.executeQuery("select * from tb_user where username = '" + username + "' and password = '" + password + "'");
//判断结果集是否为空和集合中是否有元素
if(resultSet != null && resultSet.next()){
System.out.println("登入成功");
}else{
System.out.println("登入失败");
}
}catch (Exception e){
e.printStackTrace();
}finally {
if (resultSet != null){
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
//关闭数据库资源
if (statement != null){
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (connection != null){
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
数据库中创建的每一张表 在java中都有一个实体与之对应
table中的字段对应的是实体beans中的属性
tb_user User
uid uid
username username
password password
通过jdbc来读取数据,这些数据也是以对象的形式被封装在结果集容器中ResultSet
resultSet !=null && resultSet.next()
第一个条件保证容器对象不为空,判断容器中是否有元素
我们会将读取的数据封装成对象,然后将对象装进集合中(ArrayList/HashMap)
后面学习了前端之后,我们会将集合对象传到前端,在前端取出集合中的数据,渲染数据!
事务处理
java事务
实际上,一个Java应用系统,如果要操作数据库,则通过JDBC来实现的。增加、修改、删除都是通过相应方法间接来实现的,事务的控制也相应转移到java程序代码中。因此,数据库操作的事务习惯上就称为Java事务。
通俗的理解,事务是一组原子操作单元,从数据库角度说,就是一组SQL指令,要么全部执行成功,若因为某个原因其中一条指令执行有错误,则撤销先前执行过的所有指令。更简单的说就是:要么全部执行成功,要么撤销不执行。
- 事务必须服从ISO/IEC所制定的ACID原则。ACID是原子性(atomicity),一致性(consistency),隔离性(isolation)和持久性(durability)的缩写
- 通常的观念认为,事务仅与数据库相关
- 事务只存在于增删改状态下
事务的原子性:表示事务执行过程中的任何失败都将导致事务所作的任何修改失败
事务的一致性:表示当事务执行失败时,所有被该事务影响的数据都应该恢复到事务执行前的状态
事务的隔离性:表示在事务执行过程中对数据的修改,在事务提交之前对其他事务不可见
事务的持久性:表示已提交的数据在事务执行失败时,数据的状态都应该正确
案例:
银行存钱
银行取钱
转账方法 A B
假设有一个转账的场景:A账户转账给B账户,这个时候我们需要在A账户上扣钱,在B账户上加钱
所以我们需要去写两条sql来完成这样的需求,但是我们要开启事务的支持,因为这两条sql是在一条事务线上的,所以要么同时执行成功,要么同时失败,要满足事务的原子性和一致性。
事务设置setAuntoCommit()
事务案例代码:
jdbc默认状态下是自动提交的
A账户向B账户转账1000
模拟一个异常
开启对事务的支持
在jdbc中如何开启对事务的支持
jdbc默认是关闭事务的,如果想开启事务的话需要
connection.setAutoCommit();
然后在sql都执行成功的情况下我们要进行一个事务提交的操作connection.commit();
connection.rollback()
是一个回滚的方法,是为了让事务回到执行之前的状态
/**
* A账户向B账户转账1000
*
* 模拟一个异常
*
* 开启对事务的支持
*
* 在jdbc中如何开启对事务的支持
*
* jdbc默认是关闭事务的,如果想开启事务的话需要 connection.setAutoCommit();
*/
public class JdbcDemo02 {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
try{
Class.forName("com.mysql.cj.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/web_test?serverTimezone=GMT&characterEncoding=utf-8", "root", "root");
//将自动提交设置为手动提交
connection.setAutoCommit(false);
statement = connection.createStatement();
//A账户扣钱
int num = statement.executeUpdate("update tb_account set balance_A = balance_A - 1000 where id = 1");
System.out.println(num==1 ? "扣钱成功" : "扣钱失败");
//模拟一个异常
int i = 1 / 0;
//B账户加钱
num = statement.executeUpdate("update tb_account set balance_B = balance_B + 1000 where id = 1");
System.out.println(num==1?"转账成功":"转账失败");
//提交事务
connection.commit();
} catch (Exception e) {
e.printStackTrace();
}finally{
if (statement != null){
try {
statement.close();
} catch (SQLException throwables) {
//事务回滚
try {
connection.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
throwables.printStackTrace();
}
}
if (connection != null){
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
预编译PreparedStatement
sql注入:指的就是通过在sql中加入数据库的关键字,导致最终sql执行的结果是错误的
PreparedStatement 这个对象可以防止sql注入 因为这个对象在执行sql的时候
是分为两个步骤: 1.先预编译sql,先把sql的架子给搭建出来,但是没有赋值,2.给sql中注入具体的数据,执行sql
Statement对象没有预编译的过程,直接就是执行sql
考虑到安全性我们以后不会直接使用Statement
而是使用PreparedStatement
//获取数据库操作对象并且预编译sql
PreparedStatement preparedStatement = connection.prepareStatement("select * from tb_user where username = ? and password = ? ");
//给占位符?赋值,占位符从1开始
preparedStatement.setString(1,"jack001");
preparedStatement.setString(2,"123456");
//执行sql
resultSet = preparedStatement.executeQuery();
if (resultSet != null && resultSet.next()){
System.out.println("登录成功");
}
关于数据库的悲观锁和乐观锁(都是跟事务有关的):
乐观锁:假设在多线程场景下,多个线程同时对数据库中的一个表进行操作
如果多个线程会去修改同一条数据,乐观锁的思想就是觉得这种情况不可能发生
乐观锁:乐观锁可以不用去开启事务的支持(采用的是自动提交的策略)
update tb_user set username = 'tom' where id = 1 and version = "影响记录条数只要大于等于1,这个 版本号就会改变"
悲观锁:针对于以上描述的情况,悲观锁思想就会觉得这种情况一定会发生,然后要提前去解决这个问题
悲观锁一定要去开启对事务的支持,采用手动提交的策略来解决问题,行级锁,在sql后面加上 for update
select * from tb_account where id = 1 for update;
当一个线程在对某一条记录进行查询的时候,那么别的线程只能等待
封装工具类
工具类不能实例化对象,构造函数被private修饰
所有的方法都要被static修饰
package com.os467.utils;
import java.sql.*;
/**
* 封装工具类的步骤:
*
* 1.构造方法私有化
* 2.所有的方法都得是静态方法
*
*/
public class JdbcUtils {
//私有化构造方法
private JdbcUtils(){
}
//注册驱动
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取数据库链接方法
*/
public static Connection getConnection() throws SQLException{
return DriverManager.getConnection("jdbc:mysql://localhost:3306" +
"/web_test?serverTimezone=GMT&characterEncoding=utf-8",
"root", "root");
}
/**
* 用于关闭资源的方法
*/
public static void getClose(ResultSet resultSet, Statement statement,Connection connection){
if (resultSet != null){
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (statement != null){
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (connection != null){
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
利用工具类来执行sql
package com.os467.demmo;
import com.os467.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class JdbcDemo03 {
public static void main(String[] args) {
PreparedStatement preparedStatement = null;
ResultSet resultSet =null;
Connection connection = null;
try {
//获取数据库链接对象
connection = JdbcUtils.getConnection();
//获取数据库操作对象
preparedStatement = connection.prepareStatement("select * from tb_user");
//执行sql
resultSet = preparedStatement.executeQuery();
if (resultSet != null){
while (resultSet.next()){
int uid = resultSet.getInt("uid");
String username = resultSet.getString("username");
String password = resultSet.getString("password");
System.out.println(uid+" "+username+" "+password);
}
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JdbcUtils.getClose(resultSet,preparedStatement,connection);
}
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以邮件至 1300452403@qq.com