web day16(log4j日志框架、事务)

1.log4j概述
log4j log for java专门为Java提供的日志框架
是目前公司中Java语言收集日志的主流日志框架
a.特点:
收集的优先级
收集数据的目的地 文件 console
收集数据的展示形式 HTML pattern
2.如何使用log4j
a.在程序中导入log4j 的jar包
b.书写配置文件
log4j的配置文件无需学习书写,只需掌握阅读方式,及使用方式即可,可以找到一份较完整的配置文件,在其中按照自己的要求做出相应的修改,即可变成自己所需的配置文件。
i.在配置文件中,顶部的管道可以删除,但是如果管道存在,则必须添加对应的配置信息,选择输出的目的地。
c.日志数据的优先级
所谓日志的优先级就是日志的收集级别。指定收集级别之后,log4j会将当前级别或以上级别的内容同时收集。

off最高级别,用于关闭所有的日志文件
fatal指出每个严重的错误事件将会导致应用程序的退出
error指出虽然发生错误事件,但仍然不影响系统的继续运行。
warn表明会出现潜在的错误情形
info一般和在粗粒度级别上,强调应用程序的运行全程
debug一般用于细粒度级别上,对调试应用程序非常有帮助。
all最低等级,用于打开所有日志文件

d.数据输出的目的地
所谓日志输出的目的地,指的是日志内容向何处输出,输出方向可以是console,也可以是一个文件

org.apache.log4j.ConsoleAppender控制台
org.apache.log4j.FileAppender文件
org.apache.log4j.DailyRollingFileAppender每天产生一个日志文件
org.apache.log4j.RollingFileAppender文件大小到达指定尺寸的时候产生一个新的文件
org.apache.log4j.WriterAppender将日志信息以流格式发送到任意指定的地方

e.数据输出格式
所谓日志输出格式,就是日志以何种形式向目的地输出,这种形式可以实HTML结构,也可以是自定义结构

org.apache.log4j.HTMLLayout以HTML表格形式布局
org.apache.log4j.PatternLayout可以灵活的指定布局格式
org.apache.log4j.SimpleLayout包含日志信息的级别和信息字符串
org.apache.log4j.TTCCLayout包含日志产生的时间,线程,类别等等信息

f.pattern配置

%p输出优先级,即debug,info,warn,error,fatal
%r输出自应用启动到输出该log信息耗费的毫秒数
%c输出所属的类目,通常就是所在类的全名
%t输出产生该日志事件的线程名
%n输出一个回车换行符,window平台为“rn” unix平台为“n”
%d输出日志时间点的日期和时间,默认格式为ISO8859-1,也可以在其后指定格式
%l输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数
%m输出的日志信息内容

3.创建日志对象
引入jar包
配置文件log4j.properties
Logger logger=Logger.getLogger(Demo1.class);
日志信息需要通过Logger对象输出
4.代码实现

package cn.tedu.log4j;

import org.apache.log4j.Logger;

//log4j日志文件测试
public class Demo1 {
	public static Logger logger=Logger.getLogger(Demo1.class);
	public static void main(String[] args) {
		logger.fatal("这是一个致命的日志");
		logger.error("这是一个错误的日志");
		logger.warn("这是一个警告日志");
		logger.info("这是一个普通日志");
		logger.debug("这是一个测试日志");
		System.out.println("完成.....");
	}
}

5.修改EasyMall
导入jar包
配置文件log4j.properties
修改代码(所在的监听器里边修改代码,由控制台输出转为文件)

事务
1.事务概述:
在一个事件中,有多个组成单元,这些单元要么全部成功,要么全部失败,这个事件就可以称之为一个事务
2.案例:
转账:
update user set money = money-100 where name=‘a’;
update user set money = money+100 where name=‘b’;
为了保证两句sql同时成功或失败,需要将他们写入数据库的一个事务中
3.在sql端书写事务
数据库中事务里的sql语句,可以选择提交或回滚,如果提交,数据库中的数据才会发生修改,如果回滚,数据库中的数据,会恢复到修改之前的状态。

start transaction开启事务
commit提交事务
rollback回滚事务

4.使用jdbc控制事务
在jdbc中,一个sql语句就是一个事务,现在需要多条sql位于一个事务中,所以需要开启事务

conn.setAutoCommit(false)开启事务,默认值为true,代表开启自动提交,一句sql就是一个事务,修改为false,代表关闭自动提交,意味着开启使用,多个sql可以在同一个事务中执行
conn.commit()提交事务
conn.rollback()回滚事务
conn.rollback(sp)回滚到保存点

代码实现:

package cn.tedu.trans;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Savepoint;

//事务测试
public class TransDemo1 {
	
	public static void main(String[] args) {
		Connection conn=null;
		PreparedStatement ps=null;
		ResultSet rs=null;
		Savepoint sp=null;
		try {
			Class.forName("com.mysql.jdbc.Driver");
			conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/day16","root","root");
			//开启事务
			conn.setAutoCommit(false);//默认值为true,代表会自动提交
			//自动提交代表一句sql就是一个事务,执行sql就会立刻提交这个事务,修改数据库中的值
			//设置为false,代表不会自动提交,表示开启事务,需要手动提交或回滚
			ps=conn.prepareStatement("update user set money = money-100 where name=?");
			ps.setString(1, "a");
			ps.executeUpdate();
			
			ps=conn.prepareStatement("update user set money = money+100 where name=?");
			ps.setString(1, "b");
			ps.executeUpdate();
			//添加保存点(这样上边的sql可以执行,并且保留,下边的抛弃)
			sp=conn.setSavepoint();
			ps=conn.prepareStatement("update user set money = money-100 where name=?");
			ps.setString(1, "a");
			ps.executeUpdate();
			
			int i=1/0;//会报错然后不执行下一个sql,需要回滚
			
			ps=conn.prepareStatement("update user set money = money+100 where name=?");
			ps.setString(1, "b");
			ps.executeUpdate();
			conn.commit();//在sql书写完成之后,选择使用commit方法提交事务
		} catch (Exception e) {
			if(conn!=null){
				try {
					if(sp!=null){
						conn.rollback(sp);//报错之后,保存保存点之前的内容,回滚事务,回滚到保存点
						conn.commit();//提交剩下的代码
					}
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
			}
			e.printStackTrace();
		}finally{
			if(rs!=null){
				try {
					rs.close();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}finally{
					rs=null;
				}
			}
			if(ps!=null){
				try {
					ps.close();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}finally{
					ps=null;
				}
				
			}
			if(conn!=null){
				try {
					conn.close();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}finally{
					conn=null;
				}
			}
		}
	}
	
}

事务的四大特性:
a.原子性(Atomicity):原子性是指事务是一个不可分隔的工作单位,,事务中的操作要么都发生要么都不发生。
b.一致性(Consistency):事务前后的完整性必须保持一致
c.隔离性(Isolation):事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干预,并发事务之间的数据要相互隔离。
d.持久性(Durability):持久性是指一个事务一旦被提交,他对数据库中数据的改变就真实的发生了,接下来无论做什么,哪怕是数据库故障也无法撤销这个事务。
2.隔离性
数据库的其它三大特性数据库可以帮我们保证,我隔离性我们需要讨论(根据需求确定)
如果我们数据库的设计者,该如何考虑设计数据库,保证数据库的隔离性。
我们知道数据库的隔离性问题本质上是多线程并发安全问题,所以可以从数据设计读和写来分析。
i.两个事务同时读取数据,这种情况不会产生线程安全问题,因为两个事务对应的线程都未对数据进行修改,任何人读取的数据内容都相同。
ii.两个事务同时写入数据
这种情况一定会出现线程安全问题,因为两个事务对应的线程都会对数据进行修改,第二次修改可能会覆盖第一次修改,所以应该将两个事务严格分开。可以通过修改数据库的隔离级别,来保证两个事务分别执行,避免出现线程安全问题。
iii.一个事务写,一个事务读
在面对一个事务写,一个事务读这种情况时,可以选择修改数据库的隔离级别达到自己想要的效果。如果要求数据库安全性与正确性,则可以选择最高的隔离级别。如果要求数据的写入速度,则可以选择数据库的最低隔离级别。多数情况需要两者谦顾,可以选择中间两个隔离级别。面对数据库中存储的脏读,不可重复读,虚读/幻读这些情况,也可以根据自己的需求,修改数据库的隔离级别。
防止这些问题的隔离级别,推荐使用repeatable read。原因是这个级别,不会出现脏读,和不可重复读。而虚读出现的情况极少。可以认为当前隔离级别是安全且能够保证执行效率的。l

//创建一个数据库
create database day16;
use day16;
//创建表格
cerate table user(
	id int,
	name varchar(20),
	money int
);
insert into user values(1,'a',1000);
insert into user values(2,'b',1000);



cerate table account(
	name varchar(20),
	hq int,
	dq int,
	gq int
);
insert into account values ('a',1000,1000,1000);

//开启事务
start transaction;
update user set money=money-100 where name='a';
update user set money=money+100 where name='b';
//提交
commit;
//设置隔离级别(不是全局(global))临时修改
set transaction isolation level read uncommitted;
2.情景分析:
脏读:一个事务读取到另一个未提交事务的数据,导致数据前后读取不一致。
a:1000买家
b:1000商家
a:start transaction
update user set money=money-100 where name='a';
update user set money=money+100 where name='b';
-------------------------------------------------
b:start transaction
select * from user where name='b';-----1100
----------------------------------------------
a:rollback
b:select * from user where name='b';-----1000





3.情景分析:
内容不一致,这种现象被称为不可重复读(读到的会与之前的不一样,所以不能重复的读)
a:1000 1000 1000
b:start transaction
select hq from account where name='a';-----1000
select dq from account where name='a';-----1000
select gq from account where name='a';-----1000
-------------------------------------------------
a:start transaction;
update account set hq=hq-100 where name='a';
commit;
-------------------------------------------
select hq+dq+gq from account where name='a';---2900
hq 1000
dq 1000
gq 1000
sum  2900



虚读/幻读:在整表操作中,一个事务读取到另一个已经提交事务的数据,导致事务提交前后数据不一致的现象,这种现象称之为虚读/幻读
情景分析:
---------------------------------------------------
a:1000
b:2000
-----------------------------------------------
d:
start transaction;
select sum(money) from user;---3000
select account(money) from user;----2

c:3000
start transaction;
insert into user values('c',3000);
commit;
---------------------------------
d:select avg(money) from user;----1500

4.数据隔离级别:
read uncommitted   会出现脏读现象,以及不可重复读和虚读幻读,数据库安全级别最低,但是效率最高;
read  committed  可以防止脏读,不能防止不可重复读及虚读/幻读,数据库安全级别较低,效率较高
repeatable read  可以防止脏读和不可重复读,不能防止虚读/幻读(mysql的默认隔离级别)安全性较高,效率较低
serializable      可以防止脏读和不可重复读和虚读幻读,他是mysql最高级别的隔离,是一个串行化的处理方式,但是执行效率较低

//查看隔离级别
select @@tx_isolation

级别的选择
安全:serializable>repeatable read>read committed>read uncommitted
性能:read uncommitted>read committed>repeatable read>serializable

i.由于read uncommitted隔离级别过低,安全性保证较差,所以实际开发中使用的较少,serializable的隔离级别过高,安全性虽然较高但是效率极低,对于开发影响较大,所以实际开发中很少用。
ii.read committed和repeatable read两个隔离级别较为实用,对于数据安全性和语句执行效率都有较好的保证,开发时多数会使用俩者之一

5.修改数据库的隔离级别
set global/session  transaction isolation level read uncommitted
set transaction isolation level read uncommitted;临时修改,只对下一次的事务生效
6.查询当前数据库的隔离级别
select @@tx_isolation;
7.数据库中的锁
共享锁和排它锁
a.在非serializable隔离级别之下,查询不加锁,增删改添加排它锁
b.在serializable隔离级别之下,查询添加共享锁,增删改添加排它锁
c.特点:
i.共享锁和共享锁可以共存
ii.共享锁和排它锁不能共存
iii.排它锁和排它锁不能共存
8.~表级锁和行级锁
表级锁:添加锁之后,当前表格,不允许另外的事务操作
行级锁:添加锁之后,当前表格中的某一行或几行,不允许另外的事务操作
9.死锁
两个线程相互等待对方释放资源,这种现象称之为死锁
解决死锁的方案:
i.销毁其中任意一个线程
ii.修改代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值