我们于2021/07/30 的学习目标是:数据库、JDBC与MyBatis,核心任务为:
1、学习技术:
1)、删除delete与截断truncate
2)、事务
3)、JDBC的概念
4)、JDBC工具类
5)、预处理块
6)、BaseDao
7)、MyBatis
2、文档总结
1)、删除delete与截断truncate
delete from 表名 where 行过滤条件
主外键关系如下:
删除从表中的数据,可以直接删除
删除主表中的数据:违反约束条件,不允许直接删除
前提:主表被从表中数据引用
要删除被从表引用的主表数据:
- 先删除从表中引用的数据,再删除主表数据(默认)
- 删除主表数据的同时删除哪些引用了当前主表数据的从表数据
- 删除主表数据,从表中那些引用了当前主表数据的外键字段设置为null。
cid number(5) references class(cid) on delete set null;
truncate截断数据:可以实现截断(删除)表中所有的数据
truncate table t_user;
truncate不会开启事务,delete默认会开启事物
截断表中的数据时,如果表是主表,从表结构检查上不允许
2)、事务
事务:单个逻辑工作单元执行的一组相关操作。这些操作要求全部完成或全部不完成。使用事务是为了保证数据安全有效。
事务的特点:
- 原子性(Atomic):事务中所有数据的修改,要么全部执行,要么全部不执行。
- 一致性(Consistent):事务完成时,要使所有的数据都保持一致的状态。换言之:通过事务进行的所有数据修改,必须在所有相关的表中得到反映。
- 隔离性(Isolation):事务应该在另一个事务对数据的修改前或修改后进行访问。
- 持久性(Durability):保证事务对数据库的修改是持久有效的,即使发生系统故障也不应丢失。
事务的隔离级别:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
Read Uncommitted(读未提交) | 是 | 是 | 是 |
Read Committed(读已提交) | 否 | 是 | 是 |
Repeatable Read(可重复读) | 否 | 否 | 是 |
Serializable(串行读) | 否 | 否 | 否 |
Oracle默认的隔离级别是Read Committed。
Oracle支持Read Committed和Serializable。除此之外,Oracle中还定义了Read only与Read write隔离级别。
Read only:事务中不能有任何修改数据库中数据的操作语句,是Serializable的一个子集。
Read Write:它是默认配置,该选项表示在事务中可以有访问语句和修改语句,但不经常使用。
丢失更新:两个事务同时存储,其中一个的更新未成功
脏读:一个事务更新数据,未提交修改;另一个读到后回滚,读到的这种数据为脏读数据。
不可重复读:一个事务读取一行数据,另一个事务修改了刚刚读取的记录,再次读取发现两次读取的数据不一致。
幻读:一个事务读取一条带Where条件的语句,返回结果集;另一个事务插入一条新记录,恰好符合Where条件,再次查询时看到了新记录,新纪录就叫做幻读。
事务的开启:自动开启于DML之insert delete update
成功:
事务的结束:分成功与失败
正常执行完成的DDL语句:create alter drop
正常执行完的DCL语句 grant revoke
正常退出的SQL客户端
如果人工要使用隐式事务,set autocommit on(只针对一个连接)
手动提交:commit
失败:
rollback手动回滚
非法退出
意外断电
注意:在Java中操作数据库,事务会自动提交
conn.commit();//提交
conn.rollback();//回滚
3)、JDBC的概念
JDBC:Java Database Connectivity Java与数据库的连接
角色分类:
服务器(数据库端):接收SQL,执行SQL,返回结果
客户端(Java端):接收数据,组装SQL,发送SQL,分析结果
面向接口编程:
Java提供的SQL接口在java.sql.*包下
Oracle实现接口(jar)
Java提供的SQL接口:
java.sql.Connection 连接
java.sql.Statement 静态处理块
java.sql.PreparedStatement 预处理块
java.sql.ResultSet 结果集
java.sql.ResultSetMetaData 结果集元信息
JDBC基本流程:
把Oracle实现Jar包拿到项目下 add as library
- 加载驱动(选择数据库)
- 建立连接 Connection (与数据库之间建立连接)
- 准备SQL
- 封装处理块,发送SQL
- 得到结果集
- 处理结果
- 关闭资源
import java.sql.*;
public class Class001_JDBC {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1.加载驱动 (选择数据库)
Class.forName("oracle.jdbc.driver.OracleDriver");
//2.建立连接 Connection (与数据库之间建立连接)
Connection conn = DriverManager.getConnection(
"jdbc:oracle:thin:@localhost:1521:XE",
"SCOTT",
"TIGER"
);
//3.准备sql
String sql = "select * from dept";
//4.封装处理块
Statement state = conn.createStatement();
//5.发送sql,得到结果集
ResultSet reault = state.executeQuery(sql);
//6.处理结果
while(reault.next()){
//字段序号从1开始,每次+1
int deptno = reault.getInt(1);
String dname = reault.getString(2);
String loc = reault.getString(3);
System.out.println(deptno+"--->"+dname+"--->"+loc);
}
//7.关闭资源
reault.close();
state.close();
conn.close();
}
}
优化:
- 异常捕获
- 通过配置文件实现软编码
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
public class Class002_JDBC {
public static void main(String[] args){
//构建 properties对象
Properties pro = new Properties();
try {
pro.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));
} catch (IOException e) {
e.printStackTrace();
}
//1.加载驱动 (选择数据库)
try {
Class.forName(pro.getProperty("driver"));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//2.建立连接 Connection (与数据库之间建立连接)
Connection conn = null;
Statement state = null;
ResultSet result = null;
try {
conn = DriverManager.getConnection(
pro.getProperty("url"),
pro.getProperty("username"),
pro.getProperty("password")
);
//3.准备sql
String sql = "select * from dept";
//4.封装处理块
state = conn.createStatement();
//5.发送sql,得到结果集
result = state.executeQuery(sql);
//6.处理结果
while(result.next()){
//字段序号从1开始,每次+1
int deptno = result.getInt(1);
String dname = result.getString(2);
String loc = result.getString(3);
System.out.println(deptno+"--->"+dname+"--->"+loc);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
//7.关闭资源
if(result!= null){
try {
result.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(state!=null){
try {
state.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
4)、JDBC工具类
1.加载驱动
2.获取连接
3.关闭资源
静态块的内容每次运行都会加载,把加载模块放到静态块中
静态方法不能直接使用成员变量,需要静态变量
可以将获取连接和关闭资源的操作封装为静态方法
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
public class DBUtils {
private static Properties pro = new Properties();
static{
//1.加载驱动
//构建 properties对象
try {
pro.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));
} catch (IOException e) {
e.printStackTrace();
}
//加载驱动 (选择数据库)
try {
Class.forName(pro.getProperty("driver"));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//2.获取连接
public static Connection getConnection() throws SQLException {
Connection conn = null;
conn = DriverManager.getConnection(
pro.getProperty("url"),
pro.getProperty("username"),
pro.getProperty("password")
);
return conn;
}
//3.关闭资源
public static void close(ResultSet result, Statement state,Connection conn){
if(result!= null){
try {
result.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(state!=null){
try {
state.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
public static void close(Statement state,Connection conn){
close(null,state,conn);
}
}
5)、预处理块
静态块:得到数据直接和SQL语句模板拼接
SQL注入:在数据中加入代码,使系统不安全
解决:通过预处理块处理和传递参数
预处理块优点:
- 防止SQL注入
- 预先编译,可以提高效率
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
public class Class003_User {
public static void main(String[] args){
System.out.println(update("zhangsan","321321"));;
}
//修改根据用户名修改用户密码
public static boolean update(String username,String password){
//1.获取连接
Connection conn = null;;
PreparedStatement ps = null;
boolean flag = false;
try {
conn = DBUtils.getConnection();
//设置手动提交
conn.setAutoCommit(false);
//2.构建预处理块
ps = conn.prepareStatement("update t_user set password=? where username=?");
//3.为?赋值
ps.setObject(1,password);
ps.setObject(2,username);
//4.执行,得到影响行数
int rows = ps.executeUpdate();
//5.判断
if(rows>0){
flag = true;
conn.commit(); //提交
}else{
conn.rollback(); //回滚
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
DBUtils.close(ps,conn);
}
return flag;
}
//登录 : 1)根据用户名与密码一起到数据库中查询,查询到了数据登录成功,否则登录失败 2)根据用于名去查询,得到结果的密码值与用户输入的密码比较,相等登录,不等登录失败
public static boolean login(String username,String password){
//1.获取连接
Connection conn = null;
PreparedStatement state = null;
ResultSet result = null;
try {
conn = DBUtils.getConnection();
//2.准备sql
String sql = "select * from t_user where username=? and password=?";
//3.构建预处理快
state = conn.prepareStatement(sql);
//4.需要为sql中的?占位符传递参数
state.setObject(1,username);
state.setObject(2,password);
//5.执行sql,得到结果集
result = state.executeQuery(); //预处理块新增的方法 executeQuery() executeUpdate()
//5.处理结果
if(result.next()){
return true;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
DBUtils.close(result,state,conn);
}
return false;
}
//修改
//注销
//注册用户
public static boolean reg(String username,String password){
//1.获取连接
Connection conn = null;
Statement state = null;
try {
conn = DBUtils.getConnection();
//2.准备sql
String sql = "insert into t_user values('"+username+"',"+password+")";
//3.构建处理快
state = conn.createStatement();
//4.执行sql,得到结果
int rows = state.executeUpdate(sql);
if(rows<=0){
return false;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
DBUtils.close(state,conn);
}
return true;
}
}
6)、BaseDao
数据库通用访问对象封装 BaseDao --> 扩展
增删改 查询
可变参数:
...表示可变参数
对应类型的形参个数有0~n个
方法的形参列表最后存在
方法内部通过使用数组的方式使用可变参数接收的数据。
注意:在Oracle中的number类型在java中默认转为java.math.BigDecimal
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class BaseDao<T> {
public List<T> testQuery(String sql,Class<T> cls,Object ...args){
//1.获取连接
Connection conn = null;
PreparedStatement ps = null;
ResultSet result= null;
ResultSetMetaData data = null;
List<T> list = new ArrayList<>(); //存储查询到的对象信息
try {
conn = DBUtils.getConnection();
//2.构建预处理块
ps = conn.prepareStatement(sql);
//3.为?赋值
if(args!=null && args.length!=0){
for(int i=0;i<=args.length-1;i++){
ps.setObject(i+1,args[i]);
}
}
//4.执行sql,得到相应行数
result = ps.executeQuery();
//结果集原信息对象
data = result.getMetaData();
//从结果集原信息对象上获取当前结果集中每条数据的字段个数
int columnCount = data.getColumnCount();
//5.处理数据
//循环遍历结果集
while(result.next()){
//查询出一条数据,对应创建java中的一个对象
T obj = cls.newInstance();
//循环获取每一个列的值,获取每一个属性,为对象属性赋值
for(int i=1;i<=columnCount;i++){
//获取每一条数据的每一个字段的值
Object value = result.getObject(i);
//判断value是否指向一个java.math.BigDecimal类型的对象,转为对应的int
if(value instanceof BigDecimal){
BigDecimal b = (BigDecimal)value;
value = b.intValue();
}
//获取字段的名字
String columnName = data.getColumnLabel(i);
//获取与字段所对应的属性
Field field = cls.getDeclaredField(columnName);
//为当前创建的对象的这个属性赋值
//忽略权限
field.setAccessible(true);
field.set(obj,value);
}
//把对象放入集合
list.add(obj);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} finally {
DBUtils.close(ps,conn);
}
return list;
}
/**
* 增删改
* @param sql 要执行的sql语句
* @param args 为?赋值的实参
* @return 成功与否
*/
public boolean update(String sql,Object[] args){
//1.获取连接
Connection conn = null;
PreparedStatement ps = null;
boolean flag = false;
try {
conn = DBUtils.getConnection();
//2.构建预处理块
ps = conn.prepareStatement(sql);
//3.为?赋值
if(args!=null && args.length!=0){
for(int i=0;i<=args.length-1;i++){
ps.setObject(i+1,args[i]);
}
}
//4.执行sql,得到相应行数
int rows = ps.executeUpdate();
//5.对相应行数判断结果
if(rows>0){
flag = true;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
DBUtils.close(ps,conn);
}
return flag;
}
}
7)、MyBatis
MyBatis:更加简化jdbc代码,简化持久层,sql语句从代码中分离,利用反射,将表中数据与java bean 属性一一映射 即ORM(Object Relational Mapping 对象关系映射)
使用范围:日常的开发项目,中小型开发
特点:持久层 对象关系映射框架(ORM) 半自动化
使用mybatis框架的步骤:
下载jar包,jar包资源拿到项目中,add as lib...
mybatis核心jar包
mybatis依赖jar包
数据库的驱动jar
定义mybatis核心配置文件--> 参考文档+官网
定义sql映射文件
定义要执行的sql语句
jar类中进行测试
加载核心配置文件
构建工厂
获取回话
执行sql
处理结果
关闭回话
project01_mybatis.iml
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="asm-7.1" level="project" />
</component>
</module>
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--根元素: 核心配置-->
<configuration>
<!--
环境配置
default : 要使用的环境的id值
-->
<environments default="ev">
<!--environment : 一个环境的配置 id: 当前环境的唯一标识 -->
<environment id="ev">
<!--transactionManager 事务管理器 type="JDBC" :选择与jdbc相同的事务管理机制 -->
<transactionManager type="JDBC"/>
<!--数据源配置 type="POOLED" : 通过数据库连接池管理连接-->
<dataSource type="POOLED">
<property name="driver" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@localhost:1521:XE"/>
<property name="username" value="SCOTT"/>
<property name="password" value="TIGER"/>
</dataSource>
</environment>
</environments>
<!--sql映射配置加载 : 定义sql语句的配置文件-->
<mappers>
<mapper resource="com/yjxxt/mappers/DeptMapper.xml"/>
</mappers>
</configuration>
Java Class
import com.yjxxt.entity.Dept;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class Class001_Dept {
public static void main(String[] args) throws IOException {
//1.加载mybatis的核心配置文件
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//2.构建SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//3.根据工厂构建回话
SqlSession session = factory.openSession();
//4.执行sql,得到结果
//selectList("命名空间.id")
List<Dept> list = session.selectList("com.yjxxt.mappers.DeptMapper.queryAll");
//5.处理结果
list.forEach(System.out::println);
//5.关闭回话
session.close();
}
}