连接数据库的5中方式(第5种为最终版)
第五种连接方式中jdbc.prperties文件配置 对数据库的基本操作
准备工作(将连接数据库操作和关闭数据库操作封装起来)
ORM编程思想
对customers表对order表 图解
从customers表中修改一条数据从customers添加一条数据对所有表通用的增删改操作对customer表查询的一种对customer表通用的查询getColumnLabel()对Order的通用查询对不同的表的单行数据通用查询操作对不同的表的多行数据通用查询操作获取字段值的两种方式向数据表中插入二进制文件查询下载数据表customers中Blob类型的字段四种批量插入数据的操作(第四种最优) 数据库事务
准备工作针对于数据表user_table来说:AA用户给BB用户转账100
普通写法考虑数据库事务以后的转账操作(在数据库中查看操作后结果) 数据库的并发问题数据库的隔离级别
在MySql中设置隔离级别
查看当前的隔离级别:设置当前 mySQL 连接的隔离级别:设置数据库系统的全局的隔离级别:创建mysql数据库用户:授予权限 在idea中测试DAO访问数据信息的类和接口
定义一个用来被继承的对数据库进行基本操作的DaoCustomerDAO(规范继承类)CustomersDAOImpl对其进行测试CustomersDAOImplTest 数据可连接池
连接数据库的5中方式(第5种为最终版)//方式1:
@Test
public void testConnection1() throws SQLException {
Driver driver=new com.mysql.cj.jdbc.Driver();//车
//ur1:http://localhost:8080/gmall/keyboard.jpg
//jdbc:mysql:协议
//localhost:ip地址
//3306:默认mysql端口号
//test:test数据库,即连接的数据库名称
String ur1="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8";//目标
//建用户和密码封装在Properties中
Properties info=new Properties();
info.setProperty("user","root");//人
info.setProperty("password","fsfs");//钥匙
Connection conn=driver.connect(ur1,info);//有目标的人拿车钥匙坐着车出发
System.out.println(conn);
}
//方式2:对方式1的迭代:在如下程序中不出现第三方API,使得程序具有更好的移值性
@Test
public void testConnection2() throws Exception{
//1.获取Driver实现类对象,使用反射
Class clazz=Class.forName("com.mysql.cj.jdbc.Driver");
Driver driver = (Driver) clazz.newInstance();
//2.提供要连接的数据库
String ur1="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8";
//3.提供连接需要的用户名和密码
Properties info=new Properties();
info.setProperty("user","root");
info.setProperty("password","fsfs");
//获取连接
Connection conn=driver.connect(ur1,info);//有目标的人坐着车出发
System.out.println(conn);
}
//方式3:使用DriverManager替换Driver
@Test
public void testConnection3() throws Exception{
//1.获取Driver实现类对象(反射)
Class clazz=Class.forName("com.mysql.cj.jdbc.Driver");
Driver driver = (Driver) clazz.newInstance();
//提供3个另外连接的基本信息
String ur1="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8";
String user="root";
String password="fsfs";
//注册驱动:java 1.8_0216这个版本不需要再拿驱动和反射,getConnection()方法已经封装了,getConnection()方法已经得到增强,无需再注册驱动了
DriverManager.registerDriver(driver);
//获取连接
Connection conn=DriverManager.getConnection(ur1,user,password);
System.out.println(conn);
}
//方式4:可以只是加载驱动,不用显示的注册驱动过了
@Test
public void testConncet4() throws Exception{
//提供3个另外连接的基本信息
String ur1="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8";
String user="root";
String password="fsfs";
//1.获取Driver实现类对象(反射)
Class.forName("com.mysql.cj.jdbc.Driver");
//相较于方式3可以省略如下操作
// Driver driver = (Driver) clazz.newInstance();
// //注册驱动
// DriverManager.registerDriver(driver);
//获取连接
Connection conn=DriverManager.getConnection(ur1,user,password);
System.out.println(conn);
}
//方式5最终版:将数据库连接的4个基本信息声明在配置文件中,通过读取配置文件的方式,获取连接
//好处:1.实现了数据与代码分离,实现了解耦
// 2.如果需要修改配置文件信息,可以避免程序重新打包
@Test
public void tsetConnect5() throws Exception{
//1.读取配置文件中的4个配置信息
InputStream is= Demo.class.getClassLoader().getResourceAsStream("jdbc.prperties");
Properties pros=new Properties();
pros.load(is);
String user=pros.getProperty("user");
String password=pros.getProperty("password");
String ur1=pros.getProperty("ur1");
String driverClass=pros.getProperty("driverClass");
//2.加载驱动
Class.forName(driverClass);
//获取连接
Connection conn=DriverManager.getConnection(ur1,user,password);
System.out.println(conn);
}
第五种连接方式中jdbc.prperties文件配置
user=root
password=fsfs
ur1=jdbc:mysql://localhost:3306/test?&rewriteBatchedStatements=true
driverClass=com.mysql.cj.jdbc.Driver
public class JDBCUtils {
//获取数据库的连接
public static Connection getConnection() throws Exception{
//1.读取配置文件中的4个配置信息
InputStream is=ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.prperties");
Properties pros=new Properties();
pros.load(is);
String user=pros.getProperty("user");
String password=pros.getProperty("password");
String ur1=pros.getProperty("ur1");
String driverClass=pros.getProperty("driverClass");
//2.加载驱动
Class.forName(driverClass);
//获取连接
Connection conn= DriverManager.getConnection(ur1,user,password);
return conn;
}
//关闭连接和Statement的操作
public static void closeResourse(Connection conn, Statement ps){
try{
if(ps!=null)
ps.close();
}catch (SQLException e){
e.printStackTrace();
}
try{
if(conn!=null)
conn.close();
}catch (SQLException e){
e.printStackTrace();
}
}
public static void closeResourse(Connection conn, Statement ps, ResultSet rs){
try{
if(ps!=null)
ps.close();
}catch (SQLException e){
e.printStackTrace();
}
try{
if(conn!=null)
conn.close();
}catch (SQLException e){
e.printStackTrace();
}try{
if(rs!=null)
rs.close();
}catch (SQLException e){
e.printStackTrace();
}
}
ORM编程思想
ORM编程思想 (object relational mapping)对象关系映射
一个数据表对应一个java类
表中的一条记录对应java类的一个对象
表中的一个字段对应java类的一个属性
public class Customers {
private int id;
private String name;
private String gmail;
public Customers() {
}
private Date birth;
public Customers(int id, String name, String email, Date birth) {
this.id = id;
this.name = name;
this.gmail = email;
this.birth = birth;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGmail() {
return gmail;
}
public void setGmail(String gmail) {
this.gmail = gmail;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
@Override
public String toString() {
return "Customers{" +
"id=" + id +
", name='" + name + ''' +
", email='" + gmail + ''' +
", birth=" + birth +
'}';
}
对order表
public class Order {
int order_id;
String order_name;
Date order_date;
public Order() {
}
public int getId() {
return order_id;
}
public void setId(int id) {
this.order_id = id;
}
public String getName() {
return order_name;
}
public void setName(String name) {
this.order_name = name;
}
public Date getDate() {
return order_date;
}
public void setDate(Date date) {
this.order_date = date;
}
public Order(int id, String name, Date date) {
this.order_id = id;
this.order_name = name;
this.order_date = date;
}
@Override
public String toString() {
return "Order{" +
"id=" + order_id +
", name='" + order_name + ''' +
", date=" + order_date +
'}';
}
}
图解
从customers表中修改一条数据
@Test
public void testUpdate() {
Connection conn= null;
PreparedStatement ps = null;
try {
//1.获取数据库连接
conn = JDBCUtils.getConnection();
//2.预编译sql语句,返回PreparedStatement的实例
String sql="update customers set name =? where id= ?";
ps = conn.prepareStatement(sql);
//3.填充占位符:setObject():通用操作
ps.setObject(1,"莫扎特");
ps.setObject(2,18);
//4.执行
ps.execute();
} catch (Exception e) {
e.printStackTrace();
} finally {
//5.资源的关闭
JDBCUtils.closeResourse(conn,ps);
}
}
从customers添加一条数据
//从customers添加一条数据
@Test
public void testInsert() throws Exception{
//1.读取配置文件中的4个配置信息
InputStream is= 连接方式.Demo.class.getClassLoader().getResourceAsStream("jdbc.prperties");
Properties pros=new Properties();
pros.load(is);
String user=pros.getProperty("user");
String password=pros.getProperty("password");
String ur1=pros.getProperty("ur1");
String driverClass=pros.getProperty("driverClass");
//2.加载驱动
Class.forName(driverClass);
//3.获取连接
Connection conn= DriverManager.getConnection(ur1,user,password);
// System.out.println(conn);
//4.预编译sql语句
String sql="insert into customers(name,email,birth) values(?,?,?)";// ? 是占位符
PreparedStatement ps= conn.prepareStatement(sql);
//填充占位符 注:下标从1开始
ps.setString(1,"哪吒");
ps.setString(2,"nezha@gmail.com");
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
java.util.Date date=sdf.parse("1001-11-01");
ps.setDate(3,new java.sql.Date(date.getTime()));
//执行操作
ps.execute();
}
对所有表通用的增删改操作
@Test
public void testCommentUpdate() {
// String sql="delete from customers where id=?";
// update(sql,3);
String sql = "update `order` set order_name=? where order_id=?";
update(sql, "DD", "2");
}
//通用的增删改操作
public void update(String sql, Object... args) {
Connection conn = null;
PreparedStatement ps = null;
try {
//1.获取数据库连接
conn = JDBCUtils.getConnection();
//2.预编译sql语句,返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
//4.执行
ps.execute();
} catch (Exception e) {
e.printStackTrace();
} finally {
//5.资源关闭
JDBCUtils.closeResourse(conn, ps);
}
}
对customer表查询的一种
@Test
public void testQuery() throws Exception{
Connection conn = null;
PreparedStatement ps= null;
ResultSet resultSet= null;
try {
conn = JDBCUtils.getConnection();
String sql="select id,name,email,birth from customers where id=?";
ps = conn.prepareStatement(sql);
ps.setObject(1,1);
//执行并返回结果集
resultSet = ps.executeQuery();
//处理结果集
if(resultSet.next()){//next:判断结果集的下一条是否有数据,有则返回true并指针下移,返回false指针不会下移
//获取当前这个数据的各个字段
int id=resultSet.getInt(1);
String name=resultSet.getNString(2);
String email=resultSet.getNString(3);
Date birth=resultSet.getDate(4);
Customers customers=new Customers(id,name,email,birth);
System.out.println(customers);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
JDBCUtils.closeResourse(conn,ps,resultSet);
}
}
对customer表通用的查询
//对customer通用的查询
public Customers queryForCustomers(String sql,Object ... args) {
Connection conn= null;
PreparedStatement ps= null;
ResultSet rs= null;
try {
conn = JDBCUtils.getConnection();
ps = conn.prepareStatement(sql);
//填充占位符
for(int i=0;i
getColumnLabel()
针对于表的字段名与类的属性名不相同的情况:
1.必须声明sql时,使用类的属性名来命名字段的别名
2.使用ResultSetmetaData时,需要使用getColumnLabel( )来替换getColumnName()
获取列的别名。
说明:如果sql中没有给字段其别名,getColumnLabel()获取的就是列名
对Order的通用查询
@Test
public void test() {
String sql = "select order_id,order_name,order_date from `order` where order_id=?";
try {
Order order = queryForOrder(sql, 2);
System.out.println(order);
} catch (Exception e) {
e.printStackTrace();
}
}
public Order queryForOrder(String sql, Object... args) throws Exception {
//建立连接
Connection con = JDBCUtils.getConnection();
//预编译sql语句
PreparedStatement ps = con.prepareStatement(sql);
//填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
//获取结果集
ResultSet res = ps.executeQuery();
//获取结果集中的元数据,即修饰表中的字段的属性 如:类型等
ResultSetmetaData rsmd = res.getmetaData();
if (res.next()) {
Order order = new Order();
//处理结果集一行数据的每一个列
for (int i = 0; i < rsmd.getColumnCount(); i++) {
//获取别名
String columnName = rsmd.getColumnLabel(i + 1);
//获取该行中列名所对应的值
Object columnValue = res.getObject(i + 1);
//利用反射获取类中指定的属性将其对应的列名赋上其对应指定的值
Field field = Order.class.getDeclaredField(columnName);
field.setAccessible(true);
field.set(order, columnValue);
//返回赋值后的对象
}
JDBCUtils.closeResourse(con, ps, res);
return order;
}
return null;
}
对不同的表的单行数据通用查询操作
public T queryForOrder(Class clazz,String sql, Object... args) throws Exception {
//建立连接
Connection con = JDBCUtils.getConnection();
//预编译sql语句
PreparedStatement ps = con.prepareStatement(sql);
//填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
//获取结果集
ResultSet res = ps.executeQuery();
//获取结果集中的元数据,即修饰表中的字段的属性 如:类型等
ResultSetmetaData rsmd = res.getmetaData();
if (res.next()) {
T t=clazz.newInstance();
//处理结果集一行数据的每一个列
for (int i = 0; i < rsmd.getColumnCount(); i++) {
//获取别名
String columnName = rsmd.getColumnLabel(i + 1);
//获取该行中列名所对应的值
Object columnValue = res.getObject(i + 1);
//利用反射获取类中指定的属性将其对应的列名赋上其对应指定的值
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t, columnValue);
//返回赋值后的对象
}
JDBCUtils.closeResourse(con, ps, res);
return t;
}
return null;
}
对不同的表的多行数据通用查询操作
public List queryForOrderTest(Class clazz, String sql, Object... args) throws Exception {
//建立连接
Connection con = JDBCUtils.getConnection();
//预编译sql语句
PreparedStatement ps = con.prepareStatement(sql);
//填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
//获取结果集
ResultSet res = ps.executeQuery();
//获取结果集中的元数据,即修饰表中的字段的属性 如:类型等
ResultSetmetaData rsmd = res.getmetaData();
//创建集合对象
ArrayList list=new ArrayList<>();
while(res.next()) {
T t=clazz.newInstance();
//处理结果集一行数据的每一个列
for (int i = 0; i < rsmd.getColumnCount(); i++) {
//获取别名:推荐使用
String columnName = rsmd.getColumnLabel(i + 1);
//获取该行中列名所对应的值
Object columnValue = res.getObject(i + 1);
//利用反射获取类中指定的属性将其对应的列名赋上其对应指定的值
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t, columnValue);
//返回赋值后的对象
}
JDBCUtils.closeResourse(con, ps, res);
list.add(t);
}
return list;
}
获取字段值的两种方式
//方式2:使用别名
int id=rs.getInt("id");
String name=rs.getNString("name");
String email=rs.getNString("email");
Date birth=rs.getDate("birth");
向数据表中插入二进制文件
@Test
public void test() throws Exception{
//建立连接
Connection con= JDBCUtils.getConnection();
String sql="insert into customers(name,email,birth,photo) values(?,?,?,?);";
PreparedStatement ps=con.prepareStatement(sql);
ps.setObject(1,"fs");
ps.setObject(2,"aaa@aa.com");
ps.setObject(3,"2002-01-02");
FileInputStream fis=new FileInputStream("D:\OneDrive\桌面\photo\fs.jpg");
ps.setBlob(4,fis);
ps.execute();
JDBCUtils.closeResourse(con,ps);
fis.close();
}
查询下载数据表customers中Blob类型的字段
//查询数据表customers中Blob类型的字段
@Test
public void test2()throws Exception{
Connection conn=JDBCUtils.getConnection();
String sql="select id,name,email,birth,photo from customers where id=?;";
PreparedStatement ps=conn.prepareStatement(sql);
ps.setInt(1,21);
ResultSet rs=ps.executeQuery();
if(rs.next()){
//方式2:使用别名
int id=rs.getInt("id");
String name=rs.getNString("name");
String email=rs.getNString("email");
Date birth=rs.getDate("birth");
Customers cust=new Customers(id,name,email,birth);
System.out.println(cust);
//将Blob类型的字段下载下来,以文件的方式保存在本地
Blob photo=rs.getBlob("photo");
InputStream is=photo.getBinaryStream();
FileOutputStream fos=new FileOutputStream("fs.jpg");
byte[] by=new byte[1024];
int len;
while((len=is.read(by))!=-1){
fos.write(by,0,len);
}
JDBCUtils.closeResourse(conn,ps,rs);
}
}
四种批量插入数据的操作(第四种最优)
@Test
public void test1() throws Exception{
Connection conn= JDBCUtils.getConnection();
String sql="insert into goods(name) values(?)";
PreparedStatement ps=conn.prepareStatement(sql);
long start=System.currentTimeMillis();
for(int i=0;i<20000;i++){
ps.setObject( 1,"name_"+i);
ps.execute();
}
long end=System.currentTimeMillis();
System.out.println(start-end);
JDBCUtils.closeResourse(conn,ps);
}
@Test
public void test2()throws Exception{
Connection conn= JDBCUtils.getConnection();
//设置不允许自动提交数据
conn.setAutoCommit(false);
String sql="insert into goods(name) values(?)";
PreparedStatement ps=conn.prepareStatement(sql);
long start=System.currentTimeMillis();
for(int i=0;i<20000;i++){
ps.setObject( 1,"name_"+i);
//攒"sql"
ps.addBatch();
if(i%500==0){
//执行batch
ps.executeBatch();
//清空batch
ps.clearBatch();
}
}
//提交数据
conn.commit();
long end=System.currentTimeMillis();
System.out.println(end-start);
JDBCUtils.closeResourse(conn,ps);
}
数据库事务
1.什么叫数据库事务?
事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态。
一组逻辑操作单元:一个或多个DML操作。
2.事务处理的原则﹔保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的事务都被提交(commit),那么这些修改就永久地保存
下来;要么数据库管理系统将放弃所作的所有修改,整个事务回滚(rollback)到最初状态。
3.数据一旦提交,就不可回滚
4.哪些操作会导致数据的自动提交?
DDL操作一旦执行,都会自动提交。
set autocommit = false 对DDL操作失效>DML默认情况下,一旦执行,就会自动提交。
我们可以通过set autocommit = false的方式取消DML操作的自动提交。>默认在关闭连接时,会自动的提交数据
准备工作
public class Customers {
private int id;
private String name;
private String email;
public Customers() {
}
private Date birth;
public Customers(int id, String name, String email, Date birth) {
this.id = id;
this.name = name;
this.email = email;
this.birth = birth;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String gmail) {
this.email = gmail;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
@Override
public String toString() {
return "Customers{" +
"id=" + id +
", name='" + name + ''' +
", email='" + email + ''' +
", birth=" + birth +
'}';
}
public class JDBCUtils {
//获取数据库的连接
public static Connection getConnection() throws Exception{
//1.读取配置文件中的4个配置信息
InputStream is=ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.prperties");
Properties pros=new Properties();
pros.load(is);
String user=pros.getProperty("user");
String password=pros.getProperty("password");
String ur1=pros.getProperty("ur1");
String driverClass=pros.getProperty("driverClass");
//2.加载驱动
Class.forName(driverClass);
//获取连接
Connection conn= DriverManager.getConnection(ur1,user,password);
return conn;
}
//关闭连接和Statement的操作
public static void closeResourse(Connection conn, Statement ps){
try{
if(ps!=null)
ps.close();
}catch (SQLException e){
e.printStackTrace();
}
try{
if(conn!=null)
conn.close();
}catch (SQLException e){
e.printStackTrace();
}
}
public static void closeResourse(Connection conn, Statement ps, ResultSet rs){
try{
if(ps!=null)
ps.close();
}catch (SQLException e){
e.printStackTrace();
}
try{
if(conn!=null)
conn.close();
}catch (SQLException e){
e.printStackTrace();
}try{
if(rs!=null)
rs.close();
}catch (SQLException e){
e.printStackTrace();
}
}
public class User {
private String user;
private String password;
private int balance;
@Override
public String toString() {
return "User{" +
"name='" + user + ''' +
", password='" + password + ''' +
", balance=" + balance +
'}';
}
public String getName() {
return user;
}
public void setName(String name) {
this.user = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
public User(String name, String password, int balance) {
this.user = name;
this.password = password;
this.balance = balance;
}
public User() {
}
针对于数据表user_table来说:AA用户给BB用户转账100
普通写法
@Test
public void test1c() throws Exception {
String sql1 = "update user_table set balance = balance - 100 where user =?";
update(sql1, "AA");
String sql2 = "update user_table set balance = balance + 100 where user =?";
update(sql2, "BB");
}
//通用的增删改操作
public int update(String sql, Object... args) {
Connection conn = null;
PreparedStatement ps = null;
try {
//1.获取数据库连接
conn = JDBCUtils.getConnection();
//2.预编译sql语句,返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
//4.执行
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
//5.资源关闭
JDBCUtils.closeResourse(conn, ps);
}
return 0;
}
考虑数据库事务以后的转账操作(在数据库中查看操作后结果)
@Test
public void test2() throws Exception {
Connection conn = JDBCUtils.getConnection();
System.out.println(conn.getAutoCommit());//true
conn.setAutoCommit(false);
String sql1 = "update user_table set balance = balance - 100 where user =?";
update(conn, sql1, "AA");
//模拟网络异常
//System.out.println(10/0);
String sql2 = "update user_table set balance = balance + 100 where user =?";
update(conn, sql2, "BB");
//提交数据
conn.commit();
//修改其为自动提交数据
//主要针对于使用数据库连接池的使用
conn.setAutoCommit(true);
//关闭连接
JDBCUtils.closeResourse(conn, null);
System.out.println("转账成功");
}
//通用的增删改操作---version2.0(考虑上事务)
public int update(Connection conn, String sql, Object... args) {
PreparedStatement ps = null;
try {
//1.预编译sql语句,返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
//2.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);//小心参数声明错误
}
//3.执行
//返回所影响的几行操作
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
//4.资源关闭
JDBCUtils.closeResourse(null, ps);
}
return 0;
}
数据库的并发问题
脏读: 对于两个事务 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段。之后, 若 T2 回滚, T1读取的内容就是临时且无效的。不可重复读: 对于两个事务T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段。之后, T1再次读取同一个字段, 值就不同了。幻读: 对于两个事务T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行。之后, 如果 T1 再次读取同一个表, 就会多出几行。
数据库的隔离级别
Oracle 支持的 2 种事务隔离级别:READ COMMITED, SERIALIZABLE。 Oracle 默认的事务隔离级别为: READ COMMITED
Mysql 支持 4 种事务隔离级别。Mysql 默认的事务隔离级别为: REPEATABLE READ
在MySql中设置隔离级别
查看当前的隔离级别:
5.0:SELECT @@tx_isolation;
8.0:select @@transaction_isolation
设置当前 mySQL 连接的隔离级别:
set transaction isolation level read committed;
设置数据库系统的全局的隔离级别:
set global transaction isolation level read committed;
创建mysql数据库用户:
create user tom identified by 'abc123';
授予权限
#授予通过网络方式登录的tom用户,对所有库所有表的全部权限,密码设为abc123.
5.0:grant all privileges on *.* to tom@'%' identified by 'abc123';
8.0:grant all on test.* to tom;
#给tom用户使用本地命令行方式,授予atguigudb这个库下的所有表的插删改查的权限。
5.0:grant select,insert,delete,update on atguigudb.* to tom@localhost identified by 'abc123';
8.0:grant select,insert,update,delete on test.* to tom;
在idea中测试
@Test
public void test3() throws Exception{
Connection conn=JDBCUtils.getConnection();
//取消自动提交数据
conn.setAutoCommit(false);
//设置数据库的隔离级别
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
System.out.println(conn.getTransactionIsolation());
String sql="select user ,password,balance from user_table where user=?;";
User user=getInstance(conn,User.class,sql,"CC");
System.out.println(user);
}
@Test
public void test4()throws Exception{
Connection conn=JDBCUtils.getConnection();
//取消自动提交数据
conn.setAutoCommit(false);
String sql="update user_table set balance=? where user=?";
update(conn,sql,5000,"CC");
conn.commit();
}
//通用的查询操作,用于返回数据表中的一条记录(version 2.0,考虑上事务)
public T getInstance(Connection conn,Class clazz, String sql, Object... args) throws Exception {
//预编译sql语句
PreparedStatement ps = conn.prepareStatement(sql);
//填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
//获取结果集
ResultSet res = ps.executeQuery();
//获取结果集中的元数据,即修饰表中的字段的属性 如:类型等
ResultSetmetaData rsmd = res.getmetaData();
if (res.next()) {
T t = clazz.newInstance();
//处理结果集一行数据的每一个列
for (int i = 0; i < rsmd.getColumnCount(); i++) {
//获取别名
String columnName = rsmd.getColumnLabel(i + 1);
//获取该行中列名所对应的值
Object columnValue = res.getObject(i + 1);
//利用反射获取类中指定的属性将其对应的列名赋上其对应指定的值
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t, columnValue);
//返回赋值后的对象
}
JDBCUtils.closeResourse(null, ps, res);
return t;
}
return null;
}
//通用的增删改操作---version2.0(考虑上事务)
public int update(Connection conn, String sql, Object... args) {
PreparedStatement ps = null;
try {
//1.预编译sql语句,返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
//2.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);//小心参数声明错误
}
//3.执行
//返回所影响的几行操作
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
//4.资源关闭
JDBCUtils.closeResourse(null, ps);
}
return 0;
}
DAO访问数据信息的类和接口
定义一个用来被继承的对数据库进行基本操作的Dao
public abstract class baseDAO {
private Class clazz;
{
Type genericSuperclass = this.getClass().getClass().getGenericSuperclass();
ParameterizedType paramType=(ParameterizedType)genericSuperclass;
Type[] TypeArguments = paramType.getActualTypeArguments();
clazz=(Class) TypeArguments[0];
}
//通用的增删改操作---version2.0(考虑上事务)
public int update(Connection conn, String sql, Object... args) {
PreparedStatement ps = null;
try {
//1.预编译sql语句,返回PreparedStatement的实例
ps = conn.prepareStatement(sql);
//2.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);//小心参数声明错误
}
//3.执行
//返回所影响的几行操作
return ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
//4.资源关闭
JDBCUtils.closeResourse(null, ps);
}
return 0;
}
//通用的查询操作,用于返回数据表中的一条记录(version 2.0,考虑上事务)
public T getInstance(Connection conn,String sql, Object... args) {
//预编译sql语句
PreparedStatement ps = null;
ResultSet res = null;
ResultSetmetaData rsmd = null;
try {
ps = conn.prepareStatement(sql);
//填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
//获取结果集
res = ps.executeQuery();
//获取结果集中的元数据,即修饰表中的字段的属性 如:类型等
rsmd = res.getmetaData();
if (res.next()) {
T t = clazz.newInstance();
//处理结果集一行数据的每一个列
for (int i = 0; i < rsmd.getColumnCount(); i++) {
//获取别名
String columnName = rsmd.getColumnLabel(i + 1);
//获取该行中列名所对应的值
Object columnValue = res.getObject(i + 1);
//利用反射获取类中指定的属性将其对应的列名赋上其对应指定的值
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t, columnValue);
//返回赋值后的对象
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResourse(null, ps, res);
}
return null;
}
//通用的查询操作,用于返回数据表中的多条记录构成的集合(version 2.0考虑上事务)
public List getForList(Connection conn, String sql, Object... args) {
PreparedStatement ps = null;
ResultSet res = null;
ResultSetmetaData rsmd = null;
ArrayList list= null;
try {
//预编译sql语句
ps = conn.prepareStatement(sql);
//填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i + 1, args[i]);
}
//获取结果集
res = ps.executeQuery();
//获取结果集中的元数据,即修饰表中的字段的属性 如:类型等
rsmd = res.getmetaData();
//创建集合对象
list = new ArrayList<>();
while(res.next()) {
T t=clazz.newInstance();
//处理结果集一行数据的每一个列
for (int i = 0; i < rsmd.getColumnCount(); i++) {
//获取别名
String columnName = rsmd.getColumnLabel(i + 1);
//获取该行中列名所对应的值
Object columnValue = res.getObject(i + 1);
//利用反射获取类中指定的属性将其对应的列名赋上其对应指定的值
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(t, columnValue);
//返回赋值后的对象
}
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.closeResourse(null, ps, res);
}
return null;
}
//用于查询通用值的特殊方法
public E getValue(Connection conn,String sql,Object ... args) {
PreparedStatement ps= null;
ResultSet rs= null;
try {
ps = conn.prepareStatement(sql);
for(int i=0;i
CustomerDAO(规范继承类)
public interface CustomerDAO {
//将cust对象添加达到数据库中
void insert(Connection conn, Customers cust);
//针对指定的id,删除表中的一条记录
void deleteById(Connection conn,int id);
//针对内存中cust对象去修改数据表中指定数据
void update(Connection conn,Customers cust);
//针对指定的id查询的到对应的Customers对象
Customers getCustomersById(Connection conn,int id);
//查询表中所有记录构成的集合
List getAll(Connection conn);
//返回数据表中的数据的条目数
Long getCount(Connection conn);
//返回数据表中最大的生日
Date getMaxBirth(Connection conn);
}
CustomersDAOImpl
public class CustomersDAOImpl extends baseDAO implements CustomerDAO {
@Override
public void insert(Connection conn, Customers cust) {
String sql="insert into customers(name ,email,birth) values(?,?,?)";
update(conn,sql,cust.getName(),cust.getEmail(),cust.getBirth());
}
@Override
public void deleteById(Connection conn, int id) {
String sql="delete from customers where id=?";
update(conn,sql,id);
}
@Override
public void update(Connection conn, Customers cust) {
String sql="update customers set name=?,email=?,birth=? where id=?";
update(conn,sql,cust.getName(),cust.getEmail(),cust.getBirth(),cust.getId());
}
@Override
public Customers getCustomersById(Connection conn, int id) {
String sql="select id ,name,email,birth from customers where id=?";
Customers cust=getInstance(conn,sql,id);
return cust;
}
@Override
public List getAll(Connection conn) {
String sql="select name,email,birth from customers";
List list = getForList(conn, sql);
return list;
}
@Override
public Long getCount(Connection conn) {
String sql="select count(*) from customers";
return getValue(conn,sql);
}
@Override
public Date getMaxBirth(Connection conn) {
String sql="select max(birth) from customers";
return getValue(conn,sql);
}
}
对其进行测试CustomersDAOImplTest
public class CustomersDAOImplTest {
private CustomersDAOImpl dao=new CustomersDAOImpl();
@Test
public void insert() throws Exception{
Connection conn= JDBCUtils.getConnection();
Customers cust=new Customers(1,"于小飞","xiaofei@163.com",new Date(4545454545454L));
dao.insert(conn,cust);
System.out.println("添加成功");
JDBCUtils.closeResourse(conn,null);
}
@Test
public void deleteById() throws Exception{
Connection conn= JDBCUtils.getConnection();
dao.deleteById(conn,13);
System.out.println("删除成功");
JDBCUtils.closeResourse(conn,null);
}
@Test
public void update() throws Exception{
Connection conn= JDBCUtils.getConnection();
Customers cust=new Customers(18,"贝多芬","beiduofen@163.com",new Date(4565454545454L));
dao.update(conn,cust);
System.out.println("更改成功");
JDBCUtils.closeResourse(conn,null);
}
@Test
public void getCustomersById() throws Exception{
Connection conn= JDBCUtils.getConnection();
Customers cust=dao.getCustomersById(conn,19);
System.out.println(cust);
JDBCUtils.closeResourse(conn,null);
}
@Test
public void getAll() throws Exception{
Connection conn= JDBCUtils.getConnection();
List list =dao.getAll(conn);
list.forEach(System.out::println);
JDBCUtils.closeResourse(conn,null);
}
@Test
public void getCount() throws Exception{
Connection conn= JDBCUtils.getConnection();
long count=dao.getCount(conn);
System.out.println("表中记录数据为:"+count);
JDBCUtils.closeResourse(conn,null);
}
@Test
public void getMaxBirth() throws Exception{
Connection conn= JDBCUtils.getConnection();
Date date=dao.getMaxBirth(conn);
System.out.println(date);
JDBCUtils.closeResourse(conn,null);
}
}
数据可连接池
略



