JDBC
Java DataBase Connectivity
java的数据库连接技术
在进行数据库操作时,对于java而言需要连接使用不同的数据库,如果每种数据库进行自己的方式进行java的连接,那么我们在进行数据库连接时就需要不断的学习不同数据的连接api,jdbc就是java对于不同的数据库厂商提供的用于数据库连接的接口的总称。
jdbc原理
由java提供操作方法的接口,由不同数据库厂商实现相应方法,提供统一的执行流程与返回类型,实现java对不同数据库相同方式的使用
使用jdbc连接数据库
1、导入相应数据库提供的jar包
将jar包粘贴至项目路径下在导入,否则可能会出现由于jar包移动导致项目出错
2、将驱动导入运行环境中
将指定连接数据库的驱动class文件加载到虚拟机中
Class.forName("com.mysql.jdbc.Driver");
3、获取连接
使用java提供DriverManager驱动管理类静态方法输入连接地址,账号,密码获取指定数据库连接对象
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/javatest", "root", "root");
4、创建声明对象
用于将指定的sql保存发送至数据库执行的对象,通常与sql的准备一起执行
Statement statement = con.createStatement();
String sql = "select * from account";
5、执行sql
提供多个方法 分别执行不同功能返回不同类型数据
执行方法、执行更新方法、执行查询方法(额外进行结果集处理)
// boolean execute = statement.execute(sql);
//execute执行方法
//可以执行任意sql语句 返回为boolean 用于判断执行的是否是查询语句
//不经常使用 基本用于执行ddl语句
//int i = statement.executeUpdate(sql);
//executeUpdate执行更新方法
//用于执行dml语句进行数据的添加 修改 删除
//返回int类型 返回的是执行sql影响数据条数
//executeQuery执行查询方法
//ResultSet结果集对象 保存查询语句执行后返回的数据库相关的所有信息(查询虚拟表的映射)
ResultSet rs = statement.executeQuery(sql);
//如果执行的是查询语句 那么需要额外进行结果集处理
//结果集的处理方式与集合中迭代器类似
while(rs.next()){//是否存在下一行数据
//在循环中处理的数据就是当前行数据
//获取列数据的方法 get数据类型 数据数据保存到java中的数据类型(参数)
//(1)通过指定列获取当前行指定列的数据(从1开始)
System.out.println(rs.getObject(1)+"|"+rs.getObject(2)+"|"+rs.getObject(3)+"|"+rs.getObject(4)+"|"+rs.getObject(5)+"|"+rs.getObject(6));
//(2)通过指定列名获取当前行指定列的数据
//System.out.println(rs.getObject("username")+"|"+rs.getObject("username"));
}
// System.out.println(i);
6、关闭连接释放资源
rs.close();
//数据源额保存者数据库查询的结果映射关系 需要进行释放
con.close();
基本jdbc操作数据库语法
import java.sql.*;
public class JDBCTest {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1、导入连接数据库的jar包
//java中只提供了连接数据库使用的api接口
//连接不同数据库的实现有不同的数据库厂商提供
//需要在连接不同数据库时导入相应数据库连接的实现jar包
//2、加载相应的驱动
//需要使用类加载器将导入的jar包中的核心驱动类class加载到jvm中
//不同数据库由于实现不同所以加载的驱动不同
Class.forName("com.mysql.jdbc.Driver");
//驱动的class路径不要死记 通过添加的jar包进行寻找
//3、使用驱动管理类获取连接对象
//驱动管理类需要使用驱动类class获取指定数据库的连接,所以必须在使用驱动管理类加载驱动获取连接之前
//将驱动类的class加入jvm
//驱动管理类使用连接地址进行数据库连接需要填入连接地址全路径与端口号与连接数据库名称(设置连接编码集)
//如果使用的是mysql 8.0 注意jar包与路径都需要更改 jar需要使用8.0的jar包 路径需要额外添加时区信息否则连接失败
//地址写法可以从百度获取 但是最好熟悉
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/javatest", "root", "root");
//4、使用连接对象创建sql声明对象
//用于存储sql的对象,将声明对象发送给数据库,数据库会执行存储的sql
Statement statement = con.createStatement();
//准备sql
//String sql="insert into account (username,name) values('a3','3')";
String sql = "select * from account";
//5、使用声明对象执行sql
// boolean execute = statement.execute(sql);
//execute执行方法
//可以执行任意sql语句 返回为boolean 用于判断执行的是否是查询语句
//不经常使用 基本用于执行ddl语句
//int i = statement.executeUpdate(sql);
//executeUpdate执行更新方法
//用于执行dml语句进行数据的添加 修改 删除
//返回int类型 返回的是执行sql影响数据条数
//executeQuery执行查询方法
//ResultSet结果集对象 保存查询语句执行后返回的数据库相关的所有信息(查询虚拟表的映射)
ResultSet rs = statement.executeQuery(sql);
//如果执行的是查询语句 那么需要额外进行结果集处理
//结果集的处理方式与集合中迭代器类似
while(rs.next()){//是否存在下一行数据
//在循环中处理的数据就是当前行数据
//获取列数据的方法 get数据类型 数据数据保存到java中的数据类型(参数)
//(1)通过指定列获取当前行指定列的数据(从1开始)
System.out.println(rs.getObject(1)+"|"+rs.getObject(2)+"|"+rs.getObject(3)+"|"+rs.getObject(4)+"|"+rs.getObject(5)+"|"+rs.getObject(6));
//(2)通过指定列名获取当前行指定列的数据
//System.out.println(rs.getObject("username")+"|"+rs.getObject("username"));
}
// System.out.println(i);
//6、关闭连接释放资源
rs.close();
//数据源额保存者数据库查询的结果映射关系 需要进行释放
con.close();
}
}
statement对象进行数据库jdbc操作的局限
在进行sql书写与查询时,后台常常需要获取前台发送的数据,使用前台发送的数据作为sql的条件进行sql查询,在之前通常使用字符串拼接的方式进行sql的执行
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/javatest", "root", "root");
Statement statement = connection.createStatement();
//在进行sql书写与查询时,后台常常需要获取前台发送的数据,使用前台发送的数据作为sql的条件进行sql查询,
// 在之前通常使用字符串拼接的方式进行sql的执行
String username="a1";
String password="aaaa";
String sql="select * from account where username='"+username+"' and password='"+password+"'";
System.out.println(sql);
//虽然使用字符串拼接的方式可以将数据进行拼接指定sql 进行执行,但是存在一种方式可以事sql查询结果与预期不符
ResultSet rs = statement.executeQuery(sql);
while(rs.next()){
System.out.println(rs.getObject(1)+"|"+rs.getObject(2)+"|"+rs.getObject(3)+"|"+rs.getObject(4)+"|"+rs.getObject(5)+"|"+rs.getObject(6));
}
rs.close();
connection.close();
如果客户端用户懂得sql的语法并且知道后台sql是使用字符串拼接的形式,那么就可以使用输入数据为sql语句的形式事sql查询的结果与功能预期不符
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/javatest", "root", "root");
Statement statement = connection.createStatement();
//当客户端客户猜测sql并进行sql注入
//sql注入:在进行数据添加时将sql语法加入数值
//如果客户端使用拼接sql的形式 则可能造成sql注入导致的数据不准确
String username="a1";
String password="1' or password <>'asdsadaasdsadsad";
String sql="select * from account where username='"+username+"' and password='"+password+"'";
System.out.println(sql);
//select * from account where username='a1' and password='1' or password <>'asdsadaasdsadsad'
ResultSet rs = statement.executeQuery(sql);
while(rs.next()){
System.out.println(rs.getObject(1)+"|"+rs.getObject(2)+"|"+rs.getObject(3)+"|"+rs.getObject(4)+"|"+rs.getObject(5)+"|"+rs.getObject(6));
}
rs.close();
connection.close();
prepareStatement预编译sql对象
用于解决sql注入的问题,使用占位符将需要加入的数据进行替代,之后使用专门的方法进行数据的添加,完美解决sql注入问题
String username="a1";
String password="1' or password <>'asdsadaasdsadsad";
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/javatest", "root", "root");
//preparedStatement是继承与statement的接口
//sql预编译对象
//在使用连接创建时将sql进行预编译
String sql="select * from account where username=? and password=?";
//预编译的sql书写方式为使用?占位符对数据进行占位
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//之后使用preparedStatement提供的方法进行数据的替换
//set数据类型(占位符数,值)
//一般使用setObject()因为进行赋值时会自动调用相应方法进行赋值
preparedStatement.setObject(1,username);
preparedStatement.setObject(2,password);
//注意:在使用set方法对占位符进行赋值时
//要求占位符个数与调用set方法个数不一定相同,但是必须为每个占位符都赋值
//可以对同一个占位符进行重复赋值,但只最后一次生效
ResultSet rs = preparedStatement.executeQuery();
//executeQuery()无参方法是preparedStatement提供了,因为sql已经预编译过了所以不需要在填入sql
//如果再次填入sql会使用statement的方法进行sql的执行,如果存在占位符则直接报错
while(rs.next()){
System.out.println(rs.getObject(1)+"|"+rs.getObject(2)+"|"+rs.getObject(3)+"|"+rs.getObject(4)+"|"+rs.getObject(5)+"|"+rs.getObject(6));
}
rs.close();
connection.close();
使用prepareStatement防止sql注入的原理,会将整个占位符的数据当做值进行查找,可以理解为根据数据类型为整个字段值添加了()将输入的所有当做值的内容
事务
在进行数据库操作时,往往根据需求一条sql语句不能直接满足。而是需要执行多条sql语句,在数据库中多条sql的执行管理称之为事务。例如:进行银行转账时,需要对转出账号余额与转入账号分别进行修改,书写两条sql语句,但是可能由于各种原因导致sql语句执行失败或当第一条执行成功后第二条失败,对于业务场景而言那么本次执行需求则失败,但是对于流程执行的mysql而言会将成功的记录进行保存失败不保存,导致数据的不准确
事务的特性
数据库将完整的执行作为事务,事务必须满足4个条件,也就是事务的4大原则(ACID)
Atomicity原子性
一个事务代表的多条sql语句,必须全部执行成功,才会存储修改数据库中的数据,存在任意一条sql语句执行失败,那么当前事务之前执行成功的sql语句也不会进行数据的修改 功亏一篑
consistency一致性
一个事务执行结果无论成功或者失败,对于数据中的数据应该没有额外影响,也就是说执行前后的数据应保持一致
也可以理解为事务执行成功则数据库结果为成功结果,失败则没有任何影响
loslation隔离性
每个事务与其他事务之间独立运行,互不影响。
durability持久性
事务执行成功后数据会持久化保存。如果是由于mysql软件或服务器硬件崩溃导致的事务执行中断,会将执行保存到日志中,再重新启动服务时会继续执行
Mysql中的事务
mysql默认情况下每一条sql语句当做一个事务自动提交
在mysql数据库中使用事务
· 开启事务:start transaction
· 结束事务:commit或rollback
使用start transaction关闭事务的自动提交,将需要执行的多条sql语句进行书写,如果执行成功使用commit提交结果,如果不成功使用rollback回滚数据
在执行SQL语句之前,先执行start transaction,这就开启了一个事务(事务的起点),然后可以去执行多条SQL语句,最后要结束事务,commit表示提交,即事务中的多条SQL语句所作出的影响会持久到数据库中,或者rollback,表示回滚到事务的起点,之前做的所有操作都被撤销了。
在jdbc中使用事务
setAutoCommit(false);//关闭事务的自动提交
commit();//提交当前事务
rollback();//回滚事务
使用jdbc进行数据库连接获取数据的代码
import java.sql.*;
public class OverJDBC {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1添加jar包
//2加载class文件
Class.forName("com.mysql.jdbc.Driver");
//3获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/javatest", "root", "root");
//事务处理
//获取连接后关闭自动提交
connection.setAutoCommit(false);
ResultSet rs=null;
try {
//4准备sql创建预编译对象
String sql="select * from account where username=?";
PreparedStatement ps = connection.prepareStatement(sql);
//为占位符进行赋值
ps.setObject(1,"a1");
//5执行sql
rs = ps.executeQuery();
//处理结果集
while(rs.next()){
System.out.println(rs.getInt("aid")+rs.getString(2)+rs.getString("password"));
}
//执行结束后执行提交
connection.commit();
}catch (Exception e){
//如果sql执行出现异常则进行事务回滚
connection.rollback();
}finally {
//6关闭连接释放资源
rs.close();
connection.close();
}
}
}
事务的隔离级别
事务的隔离性指的是没有关联的两个或多个事务执行期间的互不影响(一个事务不会因为另一个事务某一条sql语句执行失败而失败)
事务并发可能导致的问题
· 脏读:读取到另外一个事务未提交数据(不允许出来的事);
· 幻读(虚读):读到另一事务已提交数据。
· 不可重复读:对同一个表执行同一个sql 两次读取不一致;
数据库可以通过设置不同的隔离级别,解决以上这些问题
数据中拥有四大隔离级别
1、SERIALIZABLE串行化
在一个事务进行数据操作时(在事务提交之前),不允许另一个事务对该数据进行操作
可以解决所有事务并发产生的问题
2、REPEATABLE READ可重复读
只能读取另一个事务提交后的数据,并且允许多个事务同时对数据进行dml
可以解决不可重复读与脏读
mysql默认的事务隔离级别
3、READ COMMITTED读已提交
事务可以读取另一个事务已提交的数据
能解决脏读,不能解决幻读与不可重复读
oracle默认的事务隔离级别
4、READ UNCOMMITTED读未提交
啥都能读。啥都解决不了
修改查询隔离级别
MySQL的默认隔离级别为Repeatable read,可以通过下面语句查看:
SELECT @@TX_ISOLATION
;
也可以通过下面语句来设置当前连接的隔离级别:
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ ;//[4选1]
JDBC设置隔离级别
con.setTransactionIsolation(int level) :参数可选值如下:
· Connection.TRANSACTION_READ_UNCOMMITTED;
· Connection.TRANSACTION_READ_COMMITTED;
· Connection.TRANSACTION_REPEATABLE_READ;
· Connection.TRANSACTION_READ_SERIALIZABLE。。
DAO设计模式
数据访问对象(Data Access Object)
在进行数据库查询时,jdbc接口api将所有数据返回为resultSet结果集的形式,关系型数据库中的数据与java对象之间存在orm映射
( 对象关系映射(Object Relational Mapping)) 将数据库中的映射为java中的类,一个表可以存储多条数据,一个类可以创建多个对象,表中存在存储数据设置的列,对象中存在存储数据属性,orm映射就是数据库中表的数据与java中存储相应类型集合的映射
虽然进行了映射关系,但是jdbc返回的结果集仍然需要处理,并且java以面向对象编程的思想,需要创建对象从数据库中获取数据,dao设计模式就是数据访问对象,将访问数据库返回数据的过程进行封装,使用对象进行数据的操作
DAO设计模式就是已有代码的规范书写
将数据库的操作分别书写在不同功能的包中进行规范
pojo/javabean/VO包
用于存储存放数据库数据每条数据对象的类(为每个表创建实体类用于数据存储 表名->类名 表字段->属性)
public class Account {
private int aid;//id
private String username;//账号
private String password;//密码
private String name;//姓名
private String sex;//性别
private int age;//年龄
public Account() {
}
public Account(int aid, String username, String password, String name, String sex, int age) {
this.aid = aid;
this.username = username;
this.password = password;
this.name = name;
this.sex = sex;
this.age = age;
}
public int getAid() {
return aid;
}
public void setAid(int aid) {
this.aid = aid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Account{" +
"aid=" + aid +
", username='" + username + '\'' +
", password='" + password + '\'' +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
'}';
}
}
dao包
用于存放操作相应表的方法接口(为每个表创建一个对应的dao接口 用于获取数据方法的书写)
import com.yunhe.javabean.Account;
import java.util.ArrayList;
public interface AccountDao {
//获取全部数据
ArrayList<Account> selectAll() throws Exception;
//根据用户名密码获取指定数据
ArrayList<Account> selectByUsernameAndPassword(String username,String password) throws Exception;
}
daoimpl包
用于存放操作相应表的方法接口实现类(实现对应dao接口的所有方法)
import com.yunhe.dao.AccountDao;
import com.yunhe.javabean.Account;
import java.sql.*;
import java.util.ArrayList;
public class AccountDaoImpl implements AccountDao {
@Override
public ArrayList<Account> selectAll() throws Exception {
ArrayList<Account> list =new ArrayList<>();
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/javatest", "root", "root");
Statement statement = connection.createStatement();
String sql="select * from account ";
ResultSet rs = statement.executeQuery(sql);
while(rs.next()){
int aid = rs.getInt("aid");
String username = rs.getString("username");
String password = rs.getString("password");
String name = rs.getString("name");
String sex = rs.getString("sex");
int age = rs.getInt("age");
Account account=new Account(aid,username,password,name,sex,age);
list.add(account);
}
rs.close();
connection.close();
return list;
}
@Override
public ArrayList<Account> selectByUsernameAndPassword(String user,String pass) throws Exception {
ArrayList<Account> list =new ArrayList<>();
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/javatest", "root", "root");
String sql="select * from account where username=? and password =?";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setObject(1,user);
statement.setObject(2,pass);
ResultSet rs = statement.executeQuery();
while(rs.next()){
int aid = rs.getInt("aid");
String username = rs.getString("username");
String password = rs.getString("password");
String name = rs.getString("name");
String sex = rs.getString("sex");
int age = rs.getInt("age");
Account account=new Account(aid,username,password,name,sex,age);
list.add(account);
}
rs.close();
connection.close();
return list;
}
}