【事务、类加载器、动态代理】
事务
如果一个业务操作中多次访问了数据库,必须保证每条SQL语句都执行成功。如果其中有一条执行失败,所有已经执行过的代码必须回滚。回到没有执行前的状态。称为事务。要么所有的SQL语句全部执行成功,要么全部失败
一、事务的特性
1.原子性--------事务中的最小处理单位,要么全部一致成功,要么全部一起失败;
2. 一致性--------事务中每次查询的数据都是相同的,一致的,比如说:数据库中,张三有1000元,李四有1000元,数据是 2000元,下一次查询数据必须保持一致还是2000元;
3. 隔离性--------事务与事务中互不影响,相互独立执行;
4.** 持久性**--------事务一旦执行成功,数据提交将会永久保存到数据库;
事务的隔离级别
- 脏读---------一个事务读取另一个事务中未提交的数据
- 不可重复读------一个事务多次读取同一条记录,出现读取数据不一致的情况。一般因为另一个事务更新了这条
记录而引发的。(也就是说每次读取数据都是不一致。) - 幻读-------一个事务多次统计记录的行数,出现统计个数不一致的情况。因为另一个事务插入或删除表中
的记录。(每一次读取数据条数都发生了变化)
设置隔离级别
- 四种隔离级别:
-
四种隔离级别起的作用:
2.1. Serializable (串行化): 可以避免所有事务产生的并发访问的问题 效率及其低下
2.2. Repeatable read (可重复读):会引发幻读的问题
2.3. Read committed (读已提交):会引发不可重复读和幻读的问题
2.4. Read uncommitted (读未提交): 所有事务中的并发访问问题都会发生 -
安全和性能对比
隔离级别越高,安全性就越高。但性能越低。 -
MySQL相关的命令:
类加载器
所有的代码都是运行在内存中的,在Java中,所有的Java类都是通过类加载器加载到内存进行执行的
1、启动类加载器BootstrapClassLoader: 是嵌在JVM内核中的加载器,该加载器是用C++语言写的,主要负则加
载JAVA_HOME/lib下的类库,启动类加载器无法被应用程序直接使用。
2、扩展类加载器Extension ClassLoader: 该加载器器是用JAVA编写,且它的父类加载器是Bootstrap,是由
sun.misc.Launcher$ExtClassLoader实现的,主要加载JAVA_HOME/lib/ext目录中的类库。开发者可以这几使
用扩展类加载器。
3、系统类加载器App ClassLoader: 系统类加载器,也称为应用程序类加载器,负责加载应用程序classpath目录
下的所有jar和class文件(第三方jar)。它的父加载器为Ext ClassLoader。
类加载器获取资源
通过类加载器可以获取src下的任意一个资源,此类加载器必须是 App ClassLoader 类加载器
public class Demo1 {
public static void main(String[] args) {
InputStream is = Demo1.class.getClassLoader().getResourceAsStream("jdbc.properties");
}
}
动态代理
代理有两种模式,静态代理和动态代理。
如果需要委托类处理某一业务,那么我们就可以先在代理类中统一处理然后在调用具体实现类。
按照代理的创建时期,代理类可以分为两种:
静态代理 :由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
动态代理 :在程序运行时运用反射机制动态创建而成。
代理例子:租房。
实现同一接口:(第三方接口)
interface Service{ //接口
void save();
void update();
void delete();
}
目标对象(房东)
class ServiceImpl implements Service{ //目标对象(被代理对象)
@Override
public void save() {
System.out.println("保存数据");
}
@Override
public void update() {
System.out.println("修改数据");
}
@Override
public void delete() {
System.out.println("删除数据");
}
}
测试类:(调用者----租房的用户)
public class Demo {
public static void main(String[] args) throws Exception {
/*
最终返回一个代理对象,此代理对象对目标对象的所有方法都进行了代理
最终执行代理对象的任何方法都会执行InvocationHandler中的invoke方法
*/
Service o = (Service) Proxy.newProxyInstance(
/*
传入目标对象的类加载器
*/
ServiceImpl.class.getClassLoader(),
/*
传入目标对象的所有实现接口的字节码对象
代理对象就是根据此接口字节码对象来指定代理方法,实现动态方法代理
要不然目标对象增加一个方法,动态代理需要手动添加一个代理方法
*/
ServiceImpl.class.getInterfaces(),
new InvocationHandler() {
@Override
/*
proxy是代理对象
method是目标对象的方法
args是目标对象方法的参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("增强的代码");
// 因为method.invoke执行的是目标对象的具体方法,因此必须传入目标对象
Object invoke = method.invoke(new ServiceImpl());
System.out.println("增强的代码");
return invoke;
}
}
);
// 代理对象执行save方法,最终会执行InvocationHandler中的invoke方法
o.save();
}
}
小结: JDK提供的动态代理是基于接口的,把目标对象所实现的所有接口的字节码对象传入方法中,JDK能在内存中
动态的帮我们创建一个对象,该对象实现的目标对象实现的所有方法。即保证目标对象的所有方法代理对象都能代理
到