java基础问题1
AOP和IOC的作用
AOP :面向切面编程 ,它主要关注的是程序的执行过程。AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。
具体解析:在java方法调用时,AOP机制能自动进行方法拦截,允许在方法调用之前,调用后,以及执行异常时添加特定的代码来完成需要的功能。
作用:1)消除编码模块之间的耦合性。 2)可以在任意阶段,向已有功能模块中填加新功能,且不侵入原有功能,是低侵入式设计。3)各步骤之间的良好隔离性 , 源代码的无关性。
IOC:控制反转,IOC和DI 他们的依赖关系只在使用的时候才建立。简单来说就是不需要NEW一个对象了。
Spring 的AOP和IOC都是为了解决系统代码耦合度过高的问题。使代码重用度高、易于维护。
SpringMVC和Struts2的区别
1)springmvc的入口是一个servlet即前端控制器(DispatchServlet),而struts2入口是一个filter过虑器(StrutsPrepareAndExecuteFilter)。
(2)springmvc是基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例),struts2是基于类开发,传递参数是通过类的属性,只能设计为多例。
(3)Struts采用值栈存储请求和响应的数据,通过OGNL存取数据,springmvc通过参数解析器是将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过reques域传输到页面。Jsp视图解析器默认使用jstl。
泛型擦除
代码编译成class文件就会消失,泛型类型都变成了原生类型,在使用的地方进行强制转换(泛型转译)
为什么要选择RabbitMQ?
1.除了Qpid,RabbitMQ是唯一一个实现了AMQP标准的消息服务器;
2.可靠性,RabbitMQ的持久化支持,保证了消息的稳定性;
3.高并发,RabbitMQ使用了Erlang开发语言,Erlang是为电话交换机开发的语言,天生自带高并发光环,和高可用特性;
4.集群部署简单,正是应为Erlang使得RabbitMQ集群部署变的超级简单;
5.社区活跃度高,根据网上资料来看,RabbitMQ也是首选;
你在项目中哪个业务场景使用到服务间的调用
交友模块 添加好友,在交友模块中调用用户模块的更改粉丝数与关注数的方法。
IO的方式通常分为几种,同步阻塞的BIO、同步非阻塞的NIO、异步非阻塞的AIO。
一、BIO
在JDK1.4出来之前,我们建立网络连接的时候采用BIO模式,需要先在服务端启动一个ServerSocket,然后在客户端启动Socket来对服务端进行通信,默认情况下服务端需要对每个请求建立一堆线程等待请求,而客户端发送请求后,先咨询服务端是否有线程相应,如果没有则会一直等待或者遭到拒绝请求,如果有的话,客户端会线程会等待请求结束后才继续执行。
二、NIO
NIO的最重要的地方是当一个连接创建后,不需要对应一个线程,这个连接会被注册到多路复用器上面,所以所有的连接只需要一个线程就可以搞定,当这个线程中的多路复用器进行轮询的时候,发现连接上有请求的话,才开启一个线程进行处理,也就是一个请求一个线程模式。
在NIO的处理方式中,当一个请求来的话,开启线程进行处理,可能会等待后端应用的资源(JDBC连接等),其实这个线程就被阻塞了,当并发上来的话,还是会有BIO一样的问题。
三、AIO
与NIO不同,当进行读写操作时,只须直接调用API的read或write方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。 即可以理解为,read/write方法都是异步的,完成后会主动调用回调函数。
BIO是一个连接一个线程。
NIO是一个请求一个线程。
AIO是一个有效请求一个线程。
传统的jdbc连接数据库
注册驱动、创建connection、创建statement、手动设置参数、结果集检索
spring 全局统一处理异常
一、使用注解 @controllerAdvice 和 @ExceptionHandler
二、spring自定义异常拦截:
实现了接口 HandlerExceptionResolver 同时加上@component注解进行异常全局统一处理
struts2的流程是怎样的?
一个请求在Struts2框架中的处理大概分为以下几个步骤:
1、客户端初始化一个指向Servlet容器(例如Tomcat)的请求;
2、这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin);
3、接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请求是否需要调用某个Action;
4、如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy;
5、ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类;
6、ActionProxy创建一个ActionInvocation的实例。
7、ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
8、一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2框架中继承的标签。在这个过程中需要涉及到ActionMapper。
线程安全怎么处理?
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。所谓线程安全无非是要控制多个线程对某个资源的有序访问或修改。
1)同步代码块
synchronized(obj){
….
//此处的代码块就是同步代码块
}
2)同步方法
用synchronized修饰的实例方法(非static方法)。
3)同步锁(Lock)
通过显式定义同步锁对象来实现同步,同步锁由Lock充当。
锁提供了对共享资源的独占访问,每次只能有一个线程对Lock加锁,线程开始访问共享资源之前应先获得Lock对象。
在实现线程安全的控制中,比较常用是ReentrantLock(可重入锁)。使用该Lock对象可以显示地加锁、释放锁,通常使用ReentrantLock的代码格式如下:
class X{
//定义锁对象
private final ReentrantL1ock lock=new ReentrantLock();
//定义需要保证线程安全的方法
public void m(){
//lock.lock();
try{
//需要保证线程安全的代码
//…method body
}
//使用finally块来保证释放锁
finally{
lock.unlock();
}
}
volatile关键字
volatile是java提供的一种同步手段,只不过它是轻量级的同步,为什么这么说,因为volatile只能保证多线程的内存可见性,不能保证多线程的执行有序性。而最彻底的同步要保证有序性和可见性,例如synchronized。任何被volatile修饰的变量,都不拷贝副本到工作内存,任何修改都及时写在主存。因此对于Valatile修饰的变量的修改,所有线程马上就能看到,但是volatile不能保证对变量的修改是有序的。
抽象类和接口的区别
Java抽象类:
使用关键字abstract修饰的类叫做抽象类。
用abstract来修饰的方法叫做抽象方法。
特点:
1含有抽象方法的类必须被声明为抽象类(不管是否还包含其他一般方法)(否则编译通不过);
2抽象类可以没有抽象方法,可以有普通方法。
3抽象类必须被继承,抽象方法必须被重写:
若子类还是一个抽象类,不需要重写;否则必须要重写(override)。
抽象类不能被实例化(不能直接构造一个该类的对象);
抽象方法:
在类中没有方法体(抽象方法只需声明,而不需实现某些功能);
抽象类中的抽象方法必须被实现;
如果一个子类没有实现父类中的抽象方法,则子类也变成了一个抽象类;
虚方法:
虚函数的存在是为了多态。
Java中没有虚函数的概念。它的普通函数就相当于c++的虚函数,动态绑定是java的默认行为。如果java中不希望某个函数具有虚函数特性,可以加上final关键字变为非虚函数。
Java接口:
是一系列方法的声明。
1只有声明没有实现;
2在不同的类中有不同的方法实现;
共同点
1、接口和抽象类都不能被实例化,他们都位于继承树的顶端,用于被其他类实现和继承。
2、接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类都必须实现这些方法。
不同点
1、接口里只能包含抽象方法和默认方法,不能为普通方法提供方法实现;抽象类则完全可以包含普通方法。
2、接口里不能定义静态方法,抽象类里可以定义静态方法。
3、接口里只能定义静态常量,不能定义普通成员变量;抽象类里则既可以定义普通成员变量,也可以定义静态常量。
4、接口里不包含构造器,抽象类里可以包含构造器,抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。
5、接口里不能包含初始化块,但抽象类则完全可以包含初始化块。
6、一个类最多只能有一个直接父类,包括抽象类;但一个类可以直接实现多个接口,通过实现多个接口可以弥补Java单继承的不足。
mysql常用关键字
distinct去重复:查询出某个字段不重复的记录。可用distinct来返回不重复字段的条数count(distinct id)
limit分页:先使用where等查询语句,配合limit使用,效率才高。在sql语句中,limt关键字是最后才用到的。
以下条件的出现顺序一般是:where->group by->having->order by->limit
offset跳过查询中间几条数据:selete * from testtable limit 2 offset 1;
注意:
1.数据库数据计算是从0开始的
2.offset X是跳过X个数据,limit Y是选取Y个数据
3.limit X,Y 中X表示跳过X个数据,读取Y个数据
join:
left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录
right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录
inner join(等值连接) 只返回两个表中联结字段相等的行
mysql创建存储过程
create procedure sp_name()
begin
.........
end
mysql调用存储过程
1.基本语法:call sp_name()
注意:存储过程名称后面必须加括号,哪怕该存储过程没有参数传递
mysql删除存储过程
1.基本语法:
drop procedure sp_name//
2.注意事项
(1)不能在一个存储过程中删除另一个存储过程,只能调用另一个存储过程
集合类没有实现Cloneable和Serializable接口的原因
Collection接口指定一组对象,对象即为它的元素。如何维护这些元素由Collection的具体实现决定。例如,一些如List的Collection实现允许重复的元素,而其它的如Set就不允许。很多Collection实现有一个公有的clone方法。然而,把它放到集合的所有实现中也是没有意义的。这是因为Collection是一个抽象表现。重要的是实现。
当与具体实现打交道的时候,克隆或序列化的语义和含义才发挥作用。所以,具体实现应该决定如何对它进行克隆或序列化,或它是否可以被克隆或序列化。
在所有的实现中授权克隆和序列化,最终导致更少的灵活性和更多的限制。特定的实现应该决定它是否可以被克隆和序列化。
hashCode()和equals()方法的重要性体现在什么地方?
HashMap使用Key对象的hashCode()和equals()方法去决定key-value对的索引。当我们试着从HashMap中获取值的时候,这些方法也会被用到。如果这些方法没有被正确地实现,在这种情况下,两个不同Key也许会产生相同的hashCode()和equals()输出,HashMap将会认为它们是相同的,然后覆盖它们,而非把它们存储到不同的地方。同样的,所有不允许存储重复数据的集合类都使用hashCode()和equals()去查找重复,所以正确实现它们非常重要。
equals()和hashCode()的实现应该遵循以下规则:
(1)如果o1.equals(o2),那么o1.hashCode() == o2.hashCode()总是为true的。
(2)如果o1.hashCode() == o2.hashCode(),并不意味着o1.equals(o2)会为true。
Java中的HashMap使用hashCode()和equals()方法来确定键值对的索引,当根据键获取值的时候也会用到这两个方法。如果没有正确的实现这两个方法,两个不同的键可能会有相同的hash值,因此,可能会被集合认为是相等的。而且,这两个方法也用来发现重复元素。所以这两个方法的实现对HashMap的精确性和正确性是至关重要的。
存储的时候先hashCode-----再equals
Java中HashMap的工作原理是?
Java中的HashMap是以键值对(key-value)的形式存储元素的。HashMap需要一个hash函数,它使用hashCode()和equals()方法来向集合/从集合添加和检索元素。当调用put()方法的时候,HashMap会计算key的hash值,然后把键值对存储在集合中合适的索引上。如果key已经存在了,value会被更新成新值。HashMap的一些重要的特性是它的容量(capacity),负载因子(load factor)和扩容极限(threshold resizing)。
1)HashMap有一个叫做Entry的内部类,它用来存储key-value对。
2)上面的Entry对象是存储在一个叫做table的Entry数组中。
3)table的索引在逻辑上叫做“桶”(bucket),它存储了链表的第一个元素。
4)key的hashcode()方法用来找到Entry对象所在的桶。
5)如果两个key有相同的hash值,他们会被放在table数组的同一个桶里面。
6)key的equals()方法用来确保key的唯一性。
7)value对象的equals()和hashcode()方法根本一点用也没有。
重点内容
Put:根据key的hashcode()方法计算出来的hash值来决定key在Entry数组的索引。
Get:通过hashcode找到数组中的某一个元素Entry
Hashcode的实现
hashCode 的常规协定是:
在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。
当equals方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
单例模式及好处
构造函数私有化,用一个静态方法来获取对象实例。
特点:
1)单例类只能有一个实例。
2)单例类必须自己创建自己的唯一实例。
3)单例类必须给所有其他对象提供这一实例。
主要优点:
1)提供了对唯一实例的受控访问。
2)由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
3)允许可变数目的实例。
主要缺点:
1)由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
2)单例类的职责过重,在一定程度上违背了“单一职责原则”。
3)滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
java某些类为什么要实现Serializable接口?
当一个类实现了Serializable接口(该接口仅为标记接口,不包含任何方法定义),表示该类可以序列化.序列化的目的是将一个实现了Serializable接口的对象转换成一个字节序列,可以把该字节序列保存起来(例如:保存在一个文件里),以后可以随时将该字节序列恢复为原来的对象。
序列化可以将内存中的类写入文件或数据库中。比如将某个类序列化后存为文件,下次读取时只需将文件中的数据反序列化就可以将原先的类还原到内存中。也可以将类序列化为流数据进行传输。总的来说就是将一个已经实例化的类转成文件存储,下次需要实例化的时候只要反序列化即可将类实例化到内存中并保留序列化时类中的所有变量和状态。 甚至可以将该字节序列放到其他计算机上或者通过网络传输到其他计算机上恢复,只要该计 算机平台存在相应的类就可以正常恢复为原来的对象。
序列化的实现:将需要被序列化的类实现Serializable接口,该接口没有需要实现的方法,implements Serializable只是为了标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。
java面向对象
面向对象的程序设计方式,是遇到一件事时,思考“我该让谁来做”,然后那个“谁”就是对象,他要怎么做这件事是他自己的事,反正最后一群对象合力能把事就好就行了。
类是很多个具有相同属性和行为特征的对象所抽象出来的,对象是类的一个实例。
然后,围绕类的三个特征来说了:封装、继承和多态。
1、封装
封装是面向对象编程的核心思想,将对象的属性和行为封装起来,而将对象的属性和行为封装起来的载体就是类,类通常对客户隐藏其实现细节,这就是封装的思想。
2、继承
类与类之间同样具有关系,如一个百货公司类与销售员类相联系,类之间这种关系被称为关联。
3.多态
从一定角度来看,封装和继承几乎都是为多态而准备的。这是我们最后一个概念,也是最重要的知识点。
多态是同一个行为具有多个不同表现形式或形态的能力。多态性是对象多种表现形式的体现
类中多个方法的重载叫多态,父子类中方法的覆盖也叫多态。
因此多态有两种体现:一个是方法的重装,一个是方法的覆盖。
多态有方法的多态和对象的多态(一个对象多种形态)。
在提到多态的同时,不得不提到抽象类和接口,因为多态的实现并不依赖具体类,而是依赖于抽象类和接口。
Java中所有类的父类是什么?它都有什么方法,请列举
Object类是所有类、数组、枚举类的父类。它提供了如下几个常用方法:
1)equals():判断指定对象与该对象是否相等。
2)finalize():当系统中没有引用变量引用到该对象时,垃圾回收器调用此方法来清理该对象的资源。
3)getClass():返回该对象的运行时类。
4)Hashcode():返回该对象的hashcode值。
5)toString():返回该对象的字符串表示,当程序使用System.out.println()方法输出一个对象,或者把某个对象和字符串进行连接运算时,系统会自动调用该对象的toString()方法返回该对象的字符串表示。
6)Wait()、notify()、notifyAll():控制线程的停止和运行。
java中有四大修饰符
分别为private,default,protected,public
- private(私有的)
private可以修饰成员变量,成员方法,构造方法,不能修饰类(此刻指的是外部类,内部类不加以考虑)。被private修饰的成员只能在其修饰的本类中访问,在其他类中不能调用,但是被private修饰的成员可以通过set和get方法向外界提供访问方式
- default(默认的)
defalut即不写任何关键字,它可以修饰类,成员变量,成员方法,构造方法。被默认权限修饰后,其只能被本类以及同包下的其他类访问。
- protected(受保护的)
protected可以修饰成员变量,成员方法,构造方法,但不能修饰类(此处指的是外部类,内部类不加以考虑)。被protected修饰后,只能被同包下的其他类访问。如果不同包下的类要访问被protected修饰的成员,这个类必须是其子类。
- public(公共的)
public是权限最大的修饰符,他可以修饰类,成员变量,成员方法,构造方法。被public修饰后,可以再任何一个类中,不管同不同包,任意使用。
联表查询
将String字符串进行排序
思路:
1.利用toCharArray()将字符串转换成字符数组
2.利用Arrays中的排序方法对字符数组排序
3.将字符数组转换成字符串
Dubbo必须依赖的包有哪些?
Dubbo 必须依赖 JDK,其他为可选。
Dubbo 服务暴露的过程。
Dubbo 会在 Spring 实例化完 bean 之后,在刷新容器最后一步发布 ContextRefreshEvent 事件的时候,通知实现了 ApplicationListener 的 ServiceBean 类进行回调 onApplicationEvent 事件方法,Dubbo 会在这个方法中调用 ServiceBean 父类 ServiceConfig 的 export 方法,而该方法真正实现了服务的(异步或者非异步)发布。
dubbo服务调用超时问题解决方案
dubbo在调用服务不成功时,默认是会重试两次的。这样在服务端的处理时间超过了设定的超时时间时,就会有重复请求,比如在发邮件时,可能就会发出多份重复邮件,执行注册请求时,就会插入多条重复的注册数据,那么怎么解决超时问题呢?如下
1.对于核心的服务中心,去除dubbo超时重试机制,并重新评估设置超时时间。
2.业务处理代码必须放在服务端,客户端只做参数验证和服务调用,不涉及业务流程处理
全局配置实例
- <!-- 延迟到Spring初始化完成后,再暴露服务,服务调用超时设置为6秒,超时不重试-->
- <dubbo:provider delay="-1" timeout="6000" retries="0"/>
1、如果超时,但是最终返回了正确结果,只是warn,依旧按照正常流程走下去。
2、dubbo:provider 可以设置超时时间 timout,以及如果超时允许被重连的次数 retries
3、dubbo:reference 可以设置超时时间,以及如果超时 timout,允许重连服务的次数 retries
4、dubbo:reference retries 的默认值和consumer一样,而consumer默认为2次
<dubbo:consumer> retries default.retries int 可选 2
5、说明客户端的配置优先级高于服务端的优先级。
[email protected] 和@Autowired
@Autowired 注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许
null 值,可以设置它的 required 属性为 false。如果我们想使用按照名称(byName)来装配,可以结合
@Qualifier 注解一起使用。如下:
public class TestServiceImpl {
@Autowired
@Qualifier("userDao")
private UserDao userDao;
}
@Resource 默认按照 ByName 自动注入,由 J2EE 提供,需要导入包 javax.annotation.Resource。@Resource
有两个重要的属性:name 和 type,而 Spring 将@Resource 注解的 name 属性解析为 bean 的名字,而 type
属性则解析为 bean 的类型。所以,如果使用 name 属性,则使用 byName 的自动注入策略,而使用 type
属性时则使用 byType 自动注入策略。如果既不制定 name 也不制定 type 属性,这时将通过反射机制使用
byName 自动注入策略。
注:最好是将@Resource 放在 setter 方法上,因为这样更符合面向对象的思想,通过 set、get 去操作属性,
而不是直接去操作属性。
Redis分布式锁
一般就是用 Redisson框架
使用:
数据库事务的四大特性(ACID)
⑴ 原子性(Atomicity)
说的是一个事物内所有操作共同组成一个原子包,要么全部成功,要么全部失败。这是最基本的特性,保证了因为一些其他因素导致数据库异常,或者宕机。
⑵ 一致性(Consistency)
这个是大家误解最深的,很多博客都喜欢用银行转账的例子来讲一直性,所谓的一致性是基于原子性。
原子性只保证了一个事物内的所有操作同一性,大家同生死,不会出现你死了,我还活着。但是,原子性并没有保证大家同一时刻一起生,一起死。计算机指令是有先后顺序的,这样就决定了一个事物的提交,会经历一个时间过程,那么如果事物提交进行到了一半,为了防止这样的情况,数据库事物的一致性就规定了事物提交前后,永远只可能存在事物提交前的状态和事物提交后的状态,从一个一致性的状态到另一个一致性状态,而不可能出现中间的过程态。也就是说事物的执行结果是量子化状态,而不是线性状态。
数据库提交事物会有一个过程,如果提交的时候,存在一个时间差,在提交的第一秒,一个删除过程还没完成到了第三秒才完成,会不会第一秒访问的人和第三秒访问的人得到不同的结果?出现不一致,状态的混沌?这就是一致性得保证的只会有前状态和后状态,绝不会出现中间态。
⑶ 隔离性(Isolation)
事物的隔离性,基于原子性和一致性,因为事物是原子化,量子化的,所以,事物可以有多个原子包的形式并发执行,但是,每个事物互不干扰。由于多个事物可能操作同一个资源,不同的事物为了保证隔离性,会有很多锁方案,当然这是数据库的实现,他们怎么实现的,我们不必深究。
⑷ 持久性(Durability)
持久性,当一个事物提交之后,数据库状态永远的发生了改变,这个事物只要提交了,哪怕提交后宕机,他也确确实实的提交了,不会出现因为
刚刚宕机了而让提交不生效,是要事物提交,他就像洗不掉的纹身,永远的固化了,除非你毁了硬盘。
1、原子性:是不可分割的最小操作单位,要么同时成功,要么同时失败
2、持久性:当事物提交或回滚后,数据库会持久化的保存数据
3、隔离性:多个事物之间,相互独立,互相影响
4、一致性:事物操作前后,数据总量不变
事务的隔离级别
脏读:一个事务读到另一个事务没有提交的数据
不可重复:再同一个事务中,两次读取到的数据不一样
幻读:一个事务操作(DML)数据表中所有记录,另一个事务添加了一条数据,则第一个事务查询不到自己的修改
隔离级别:
1、read uncommitted:读未提交
产生的问题:脏读、不可重复读、幻读
2、read committed:读已提交 (Oracle默认)
产生的问题:不可重复读、幻读
3、repeatable read:可重复读(mySql默认)
产生的问题:幻读
4、seriallizable:串行化
可以解决所有 问题 但是 效率极其慢
数据库表被锁表,select查询会等待
使用with(nolock)
数据读的是被锁之前的数据,表被锁了,肯定会有对表的update,delete操作。
如果对数据的准确性,实时性要求不是很高的话,可以使用这个方法。
sql示例:
SELECT COUNT(UserID) FROM EMPLOYEE WITH (NOLOCK) JOIN WORKING_GROUP WITH (NOLOCK) ON EMPLOYEE.UserID = WORKING_GROUP.UserID
with(nolock)的使用场景
1:数据量特别大的表,牺牲数据安全性来提升性能是可以考虑的;
2:允许出现脏读现象的业务逻辑,反之一些数据完整性要求比较严格的场景就不合适了,像金融方面等。
3:数据不经常修改的表,这样会省于锁定表的时间来大大加快查询速度。
4、当使用NoLock时,它允许阅读那些已经修改但是还没有交易完成的数据。因此如果有需要考虑transaction事务数据的实时完整性时,使用WITH (NOLOCK)就要好好考虑一下。
nolock和with(nolock)的几个小区别
1、SQL05中的同义词,只支持with(nolock);
2、with(nolock)的写法非常容易再指定索引。
3、跨服务器查询语句时 不能用with (nolock) 只能用nolock,同一个服务器查询时 则with (nolock)和nolock都可以用
jwt单点登录流程(第一次登陆)
Spring事务异常回滚,捕获异常不抛出就不会回滚
为什么不回滚呢?是对spring的事务机制就不明白。!!
默认spring事务只在发生未被捕获的 runtimeExcetpion时才回滚。
spring aop 异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚,默认情况下aop只捕获runtimeException的异常,但可以通过.
解决方案:
方案1.例如service层处理事务,那么service中的方法中不做异常捕获,或者在catch语句中最后增加throw new RuntimeException()语句,以便让aop捕获异常再去回滚,并且在service上层(webservice客户端,view层action)要继续捕获这个异常并处理
方案2.在service层方法的catch语句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();语句,手动回滚,这样上层就无需去处理异常(现在项目的做法)
例:
类似这样的方法不会回滚 (一个方法出错,另一个方法不会回滚) :
- if(userSave){
- try {
- userDao.save(user);
- userCapabilityQuotaDao.save(capabilityQuota);
- } catch (Exception e) {
- logger.info("能力开通接口,开户异常,异常信息:"+e);
- }
- }
下面的方法回滚(一个方法出错,另一个方法会回滚):
- if(userSave){
- try {
- userDao.save(user);
- userCapabilityQuotaDao.save(capabilityQuota);
- } catch (Exception e) {
- logger.info("能力开通接口,开户异常,异常信息:"+e);
- throw new RuntimeException();
- }
- }
或者:
- if(userSave){
- try {
- userDao.save(user);
- userCapabilityQuotaDao.save(capabilityQuota);
- } catch (Exception e) {
- logger.info("能力开通接口,开户异常,异常信息:"+e);
- TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
- }
- }
mybatis执行流程
Mybatis基本的执行流程如下图所示:
项目中遇到的问题
1:dubbo超时连接
2:jwt 用户更改手机号,登录不了
3:jqeury版本1.8双边滚动条->1.5解决
4:dubbo占用内存过大(基本上每个provider(服务提供者) 至少占用100M 的内存,有的还会占用300M,对于原始provider 会用上500M内存)
5:业务扩展 表字段扩容 解决方法:
Dubbo 集成 Spring Boot
github项目地址如下
https://github.com/apache/incubator-dubbo-spring-boot-project