Java知识体系html
目的:为了更好的认识java体系java
新建java_home变量(安装目录),值为:C:\Program Files\Java\jdk1.6.0_14;此值为JDK的安装位置。程序员
新建classpath变量(类加载路径),值为:.;%java_home%\lib;%java_home%\lib\tools.jarweb
修改path变量(使得系统能够在任何路径下识别java命令),值为:%java_home%\bin;%java_home%\jre\bin面试
Public:任何地方能够访问正则表达式
Private:只有自身能够访问算法
Protected:同一个包和子类能够访问spring
默认:只有同一个包内能够访问sql
垃圾回收是Java语言的一大特性,方便了编程,是以消耗性能为代价的。而垃圾在这里只无用的对象。而C++是须要程序员本身写析构函数来释放内存的,麻烦,也有可能忘记而致使内存泄露。数据库
Java语言对内存的分配管理是经过JVM内部机制决定的。程序员能够不关心其处理。
Java虚拟机中有个称之为垃圾回收器的东西,实际上这个东西也许真正不存在,或者是已经集成到JVM中了,但这可有可无,咱们仍然能够称为为垃圾回收器。
垃圾回收器的做用是查找和回收(清理)无用的对象。以便让JVM更有效的使用内存。
垃圾回收器的运行时间是不肯定的,由JVM决定,在运行时是间歇执行的。虽然能够经过System.gc()来强制回收垃圾,可是这个命令下达后没法保证JVM会当即响应执行,但经验代表,下达命令后,会在短时间内执行你的请求。JVM一般会感到内存紧缺时候去执行垃圾回收操做。
垃圾回收过于频繁会致使性能降低,过于稀疏会致使内存紧缺。这个JVM会将其控制到最好,不用程序员担忧。但有些程序在短时间会吃掉大量内存,而这些恐怖的对象很快使用结束了,这时候也许有必要强制下达一条垃圾回命令,这是颇有必要的,以便有更多可用的物理内存。
从上面了解到,没有用的对象就是垃圾。准确的说,当没有任何线程访问一个对象时,该对象就符合垃圾回收的条件。
对于String,存在一个字符串池,这个不属于本文讨论的范围,字符串池中的垃圾回收,算法和这里所讨论的垃圾回收彻底是两码事。可是不得不说的是,字符串的胡乱拼接,每每致使性能急剧降低,尤为是在庞大的循环语句中,拼接字符串就是在让程序慢性自杀。这也是不少Java程序员容易犯的毛病。
字符串既然是池,就是为了缓冲,为了有更高的命中率,所以垃圾回收的频率也许会比JVM对象垃圾回收器要低不少。
垃圾回收器仅仅能作的是尽量保证可用内存的使用效率,让可用内存获得高效的管理。程序员能够影响垃圾回收的执行,但不能控制。
虽然程序员没法控制JVM的垃圾回收机制。可是能够经过编程的手段来影响,影响的方法是,让对象符合垃圾回收条件。
分别说来有一下几种:
一、将无用对象赋值为null.
二、从新为引用变量赋值。好比:
Person p = new Person("aaa");
p = new Person("bbb");
这样,new Person("aaa")这个对象就是垃圾了——符合垃圾回收条件了。
三、让相互联系的对象称为“岛”对象
Person p1 = new Person("aaa");
Person p2 = new Person("bbb");
Person p3 = new Person("ccc");
p1=p2; p2=p3; p3=p1;
p1=null; p2=null; p3=null;
在没有对p一、p二、p3置null以前,它们之间是一种三角恋关系。分别置null,三角恋关系依然存在,可是三个变量不在使用它们了。三个Person对象就组成了一个孤岛,最后死在堆上——被垃圾回收掉。
实际上这里的强制,是程序员的意愿、建议,何时执行是JVM的垃圾回收器说了算。
调用垃圾回收也不必定能保证未使用的对象必定能从内存中删除。
惟一能保证的是,当你内存在极少的状况,垃圾回收器在程序抛出OutofMemaryException以前运行一次。
TCP是面向链接的通讯协议,TCP提供两台计算机之间的可靠的无差异错的数据传输。
UDP是无链接通讯协议,UDP不保证可靠的数据的传输,但可以向若干个目标发送数据,接受发自若干个源的数据。
实现多线程:继承自Thread类,能够重写run()方法去覆盖去Thread中的run()方法;实现Runnable接口并实现它的run()方法。
线程的优先级表明该线程的重要程度(不是绝对的),当有多个线程同时处于可执行状态并等待得到 CPU 时间时,线程调度系统根据各个线程的优先级来决定给谁分配 CPU 时间,优先级高的线程有更大的机会得到 CPU 时间,优先级低的线程也不是没有机会,只是机会要小一些罢了。
1.start()是启动一个线程。
2.join()是直到执行完(或强制执行一段时间)当前的线程后才往下执行主线程或其余的线程
3.stop()是中止当前的线程。
阻塞线程比较多
sleep() 方法:sleep() 容许 指定以毫秒为单位的一段时间做为参数,它使得线程在指定的时间内进入阻塞状态,不能获得CPU 时间,指定的时间一过,线程从新进入可执行状态。可能给其余线程执行的机会(自私,睡着了,不释放锁,时间到了才放锁)
suspend() 和 resume() 方法:两个方法配套使用,suspend()使得线程进入阻塞状态,而且不会自动恢复,必须其对应的resume() 被调用,才能使得线程从新进入可执行状态。
yield() 方法:yield() 使得线程放弃当前分得的 CPU 时间,可是不使线程阻塞,即线程仍处于可执行状态,随时可能再次分得 CPU 时间。调用 yield() 的效果等价于调度程序认为该线程已执行了足够的时间从而转到另外一个线程。不过yield()只能使同等级别的线程获取执行的机会(公平竞争,释放你们再次选举)。而sleep(1000)使同级别或不一样级别的都有可能。
wait() 和 notify() 和notifyAll()方法是Object中定义的方法:
必须在synchronized代码块中使用,在synchronized代码被执行期间,线程能够调用对象的wait()方法,释放对象的锁标志,进入等待的状态,而且能够调用notify()或者notifyAll()方法通知正在等待的其余线程。notify()通知的是等待队列中的第一个线程,notifyAll()通知的是等待队列中的全部数量。
几个方法配套使用,wait() 使得线程进入阻塞状态,它有两种形式,一种容许 指定以毫秒为单位的一段时间做为参数,另外一种没有参数,前者当对应的 notify() 被调用或者超出指定时间时线程从新进入可执行状态,后者则必须对应的 notify() 被调用。
wait()在object类里定义;sleep()在Thread类里定义。
wait()方法只能放在同步方法或同步块中,表示资源同步时,线程须要等待。
sleep()方法可放在任何位置,表示当前线程睡眠。
wait()方法会释放对象锁;sleep()不会释放对象锁。
wait()方法要等待唤醒以后,线程才会继续执行。
sleep()则是休眠一段时间,线程自动恢复执行.
sleep()必须捕获异常,而wait(),notify()和notifyAll()不须要捕获异常
notify() 方法致使解除阻塞的线程是从因调用该对象的 wait() 方法而阻塞的线程中随机选取的,咱们没法预料哪个线程将会被选择,因此编程时要特别当心,避免因这种不肯定性而产生问题。
notifyAll() 方法将把因调用该对象的 wait() 方法而阻塞的全部线程一次性所有解除阻塞。固然,只有得到锁的那一个线程才能进入可执行状态。
run()方法:在本线程内调用该Runnable对象的run()方法,能够重复屡次调用;
start()方法:启动一个线程,调用该Runnable对象的run()方法,不能屡次启动一个线程;
start()方法是启动(即开辟)一个线程的方法,所以线程的启动必须经过此方法,
而run()方法,只是Thread类的一个方法,它自己并不能开辟线程。
运行时异常和Error是catch不了的。
throw和throws的区别
throw是抛出一个具体的异常类,产生一个异常。
throws则是在方法名后标出该方法会产生何种异常须要方法的使用者捕获并处理。
自定义异常必须继承Exception类。
略
略
* 0次或屡次
+ 至少1次
? 0或1次
{n} 指定n次
{n,} 至少n次
{n,m} 匹配n-m次
属性是私有,方法的公开的。
本身的数据和方法只让可信的类或者对象操做,对不可信的进行信息隐藏。
它能够使用现有类的全部功能,并在无需从新编写原来的类的状况下对这些功能进行扩展。
实现多态有2种方式:静态多态(编译时的多态,在同一个类中)和动态多态(一个对象能够有多种类型)。
是容许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值以后,父对象就能够根据当前赋值给它的子对象的特性以不一样的方式运做。
一句话:容许将子类对象赋值给父类的引用或父类指向子类的对象。
封装能够隐藏实现细节,使得代码模块化。
继承能够扩展已存在的代码模块(类)。
封装和继承属于代码重用。
多态是为了接口重用。
继承很好理解,下图表示(实线):
实现也很好理解,下图表示(虚线):
依赖(代码中一般表示的是局部变量、方法参数、返回值),下图表示:
关联(代码中一般表示的类的属性),下图表示:
聚合(关联的一种,此时总体与部分之间是可分离的,他们能够具备各自的生命周期,部分能够属于多个总体对象,也能够为多个总体对象共享;has-a,空心菱形),下图表示
组合(关联的一种,表示更强的关系,总体与部分是不可分的,总体的生命周期结束也就意味着部分的生命周期结束,contains-a,实心菱形),下图表示
抽象类里面能够有非抽象方法
但接口里只能有抽象方法
声明方法的存在而不去实现它的类被叫作抽像类(abstract class),它用于要建立一个体现某些基本行为的类,并为该类声明方法,但不能在该类中实现该类的状况。不能建立abstract 类的实例。然而能够建立一个变量,其类型是一个抽像类,并让它指向具体子类的一个实例。不能有抽像构造函数或抽像静态方法。Abstract 类的子类为它们父类中的全部抽像方法提供实现,不然它们也是抽像类为。取而代之,在子类中实现该方法。知道其行为的其它类能够在类中实现这些方法。
接口(interface)是抽像类的变体。在接口中,全部方法都是抽像的。多继承性可经过实现这样的接口而得到。接口中的全部方法都是抽像的,没有一个有程序体。接口只能够定义static final成员变量。接口的实现与子类类似,除了该实现类不能从接口定义中继承行为。当类实现特殊接口时,它定义(即将程序体给予)全部这种接口的方法。而后,它能够在实现了该接口的类的任何对像上调用接口的方法。因为有抽像类,它容许使用接口名做为引用变量的类型。一般的动态联编将生效。引用能够转换到接口类型或从接口类型转换,instanceof 运算符能够用来决定某对象的类是否实现了接口。
在一个类中定义另一个类,这个类就叫作内部类或内置类 (inner class) 。
内部类分为成员内部类、静态嵌套类、方法内部类、匿名内部类。
内部类仍然是一个独立的类,在编译以后会内部类会被编译成独立的.class文件,可是前面冠之外部类的类命和$符号。内部类不能用普通的方式访问。内部类是外部类的一个成员,所以内部类能够自由地访问外部类的成员变量,不管是不是private的。
咱们为何使用内部类?
A、在内部类(inner class)中,能够随意的访问外部类的成员,这可让咱们更好地组织管理咱们的代码,加强代码的可读性。
B、内部类能够用于建立适配器类,适配器类是用于实现接口的类。使用内部类来实现接口,能够更好地定位与接口关联的方法在代码中的位置。
C、内部类的更多用法(匿名inner class实现一个接口,不过切记最后那个分号;实现继承和接口实现时出现同名方法的问题;实现多重继承的功能)
这种动态的获取信息及动态调用方法的机制在Java中称为“反射”(reflection)。
Reflection 是Java被视为动态(或准动态)语言的一个关键性质。
在JDK中,主要由如下类来实现Java反射机制,这些类都位于java.lang.reflect包中:
Class类:表明一个类;
Field 类:表明类的成员变量(成员变量也称为类的属性);
Method类:表明类的方法;
Constructor 类:表明类的构造方法;
Array类:提供了动态建立数组,以及访问数组的元素的静态方法;
在java.lang.Object 类中定义了getClass()方法,所以对于任意一个Java对象,均可以经过此方法得到对象的类型。Class类是Reflection API 中的核心类,它有如下方法:
getName():得到类的完整名字;
getFields():得到类的public类型的属性(包括继承的类的public属性);
getDeclaredFields():得到类的全部属性;
getMethods():得到类的public类型的方法; (包括继承的类的public方法);
getDeclaredMethods():得到类的全部方法;
getMethod(String name, Class[] parameterTypes):得到类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型;
getConstructors():得到类的public类型的构造方法;
getConstructor(Class[] parameterTypes):得到类的特定构造方法,parameterTypes 参数指定构造方法的参数类型;
newInstance():经过类的不带参数的构造方法建立这个类的一个对象;
经过默认构造方法建立一个新对象:
Object objectCopy=classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
以上代码先调用Class类的getConstructor()方法得到一个Constructor 对象,它表明默认的构造方法,而后调用Constructor对象的newInstance()方法构造一个实例。
得到对象的全部属性:
Field fields[]=classType.getDeclaredFields();
Class 类的getDeclaredFields()方法返回类的全部属性,包括public、protected、默认和private访问级别的属性。
Java容许咱们从多种途径为一个class生成对应的Class object
(一)运用getClass()方法
(二)运用Class.getSuperclass()方法
(三)运用静态方法Class.forName(),这个最经常使用
(四)运用.class语法
(五)运用原始包装类的TYPE方法
浅负责也叫浅克隆,深复制也叫深克隆。
克隆通常经过实现Cloneable中的clone方法来实现浅克隆和深克隆,可是深克隆有一个问题,若是引用对象有不少,或者说引用套引用不少重,那么太麻烦了。
业界经常使用的方法是使用串行化而后反串行化的方法来实现深克隆。因为串行化后,对象写到流中,全部引用的对象都包含进来了,因此反串行化后,等于生成了一个彻底克隆的对象。绝! 这个方法的要求是对象(包括被引用对象)必须事先了Serializable接口,不然就要用transient关键字将其排除在复制过程当中。
需求捕获、系统分析与设计、系统实现、测试、维护
视图(View)是一个或多个图组成的对系统某个角度的抽象
图(Diagram)是模型元素集的图形表示
模型元素(Model Element)表明面向对象中的类、对象、接口、消息和关系的概念
通用机制(General Mechanism)用于表示其余信息,如注释、模型元素的语义等
视图由图组成,UML提供9种不一样的视图,其中视图的包括
用例视图(强调系统功能)也称用户模型视图
用例图
逻辑视图(展示系统的静态或结构组成特征)也称为结构模型视图或静态视图
类图、对象图
并发视图(体现系统的动态或行为特征)也称为行为模型视图或动态视图
时序图、协做图、状态图、活动图
组件视图(体现系统实现的结构和行为特征)也成为实现模型视图
组件图
配置视图(体现系统实现的环境的结构和行为特征)也称环境模型视图或物理视图
配置图
UML内容概括为2大类:静态建模机制和动态建模机制
静态建模机制包括用例图、类图、对象图、组件图、配置图、包等
动态建模机制包括时序图、协做图、状态图、活动图、消息等
主要说说事物和关系
事物包括以下
结构事物:用例(椭圆)、类(矩形)、接口(圆心)、协做(虚椭圆)、活动类(粗线矩形)、组件、节点(资源:电脑)
行为事物:交互(一般画成带箭头的信息)、状态机(对象的一个或多个状态的集合)
组织事物:包
辅助事物:注释
关系:关联、依赖、泛化、实现
类(矩形:名称、属性、操做、职责)
其中表示+、-、#是public、private、protected
关系
依赖(有方向的虚线):使用依赖、抽象依赖、受权依赖、绑定依赖
泛化(父类和子类的关系):描述了类之间的“is a kind of ”的关系,用子类指向父类的空心三角形箭头(实线)表示该关系。泛
化使得多态成为可能。
关联:描述一组具备相同结构特征、行为特征、关系和语义的连接。
关联的修饰有:名称(描述关系的性质)、角色(职责)、多重性(0..n)、聚合(总体和部分,即has a的关系)、
组合(是更强的关系,是另一种聚合,总体有管理部分特有的职责而且有一致的生命周期)等。
实现:实现规格说明和其实现间的关系。它表示不继承结构而只继承行为。大多数状况下,实现关系用来规定接口和实现接口的类或
组件之间的关系。(带空心的箭头表示,线是虚线)
描述类、接口、协做以及它们之间关系的图。
类图包括7个元素:类、接口、协做、依赖关系、泛化关系、实现关系以及关联关系
类图的做用:对系统的词汇建模、对简单的协做建模、对逻辑数据库模式建模
表示某一刻一组对象以及它们之间关系的图
用例(Use Case):对一个系统或一个应用的一种单一的使用方式所做的描述。
参与者(Actor):系统外部的一个实体(能够说任何事物或人)
参与者之间的关系(泛化关系、包含关系、扩展关系)
描述对象之间传递消息的时间顺序,他用来表示用例中的行为顺序,是强调消息时间的交互图
包括4个元素:对象、生命线、激活、消息
强调参与交互的各对象结构的信息。协做图是一种类图。
协做图中包含3个元素:对象、链、消息。
如图:
经过类对象的生命周期创建模型来描述对象随时间变化的动态行为。
它包括:状态和转换
注意:初始状态:实圆心、终止状态:半实圆心。
状态机、状态、转化
其中状态包括:名字、入口/出口动做、内部转换、延迟事件、子状态。
转换包括:源状态、目标状态、出发事件、监护条件、动做
描述一个过程或操做的步骤。描述状态外,更突出活动
动做状态、活动状态、动做流、分支、分叉和汇合、泳道、对象流。
描述软件组件及组件之间的关系。
包括:组件、接口、关系(依赖、泛化、关联、实现)。
显示软件系统运行的物理硬件。
包括节点(单个物理对象)、组件和关系(依赖和关联)
(略)
MVC模式的目的就是实现Web系统的职能分工。 Model层实现系统中的业务逻辑,一般能够用JavaBean或EJB来实现。 View层用于与用户的交互,一般用JSP来实现。 Controller层是Model与View之间沟通的桥梁,它能够分派用户的请求并选择恰当的视图以用于显示,同时它也能够解释用户的输入并将它们映射为模型层可执行的操做。
优势:低耦合、高可复用性和可适用性、较低的生命周期成本、快速部署、可维护性、有利于软件工程化管理。
缺点:原理较复杂花费时间思考、程序分红三个部分须要管理更多的文件。
Struts: Apache的,最流行的MVC组件
Struts2 :Apache用Struts 和 WebWork的组合出来的新产品,目前上升势头强劲
WebWork: 这个但是老牌的MVC组件,后来组合成了Struts2, 不过自身仍在发展
Spring MVC:SpringFramework本身整合本身Spring的优点推出的MVC组件,用户也很多。
JSF: 这个是一个规范,Sun的和 Apache的都有各自的实现。用户量很大,被众多IDE支持。
Model1是将业务、控制、视图都放在jsp页面、Model2是由业务(javaBean)、控制(ActionServlet)、视图(Jsp)组成。
Struts 2是Struts的下一代产品,是在 struts 和WebWork的技术基础上进行了合并的全新的Struts 2框架。其全新的Struts 2的体系结构与Struts 1的体系结构的差异巨大。Struts 2以WebWork为核心,采用拦截器的机制来处理用户的请求,这样的设计也使得业务逻辑控制器可以与Servlet API彻底脱离开,因此Struts 2能够理解为WebWork的更新产品。虽然从Struts 1到Struts 2有着太大的变化,可是相对于WebWork,Struts 2只有很小的变化。
目的:是为了帮助咱们减小在运用MVC设计模型来开发Web应用的时间。
Struts2的体系与Struts1体系的差异很是大,由于Struts2使用了WebWork的设计核心,而不是Struts1的设计核心。Struts2中大量使用拦截器来处理用户的请求,从而容许用户的业务逻辑控制器与Servlet API分离。
Struts2框架的大概处理流程以下:
1、加载类(FilterDispatcher)
2、读取配置(struts配置文件中的Action)
3、派发请求(客户端发送请求)
4、调用Action(FilterDispatcher从struts配置文件中读取与之相对应的Action )
5、启用拦截器(WebWork拦截器链自动对请求应用通用功能,如验证)
6、处理业务(回调Action的execute()方法)
7、返回响应(经过execute方法将信息返回到FilterDispatcher)
8、查找响应(FilterDispatcher根据配置查找响应的是什么信息如:SUCCESS、ERROER,将跳转到哪一个jsp页面)
9、响应用户(jsp--->客户浏览器端显示)
10、struts2标签库(相比struts1的标签库,struts2是大大增强了,对数据的操做功能很强大)。
在Action的实现方面:Struts1要求必须统一扩展自Action类,而Struts2中能够是一个普通的POJO。
线程模型方面:Struts1的Action工做在单例模式,一个Action的实例处理全部的请求。Struts2的Action是一个请求对应一个实例。没有线程安全方面的问题。
Servlet依赖方面:Struts1的Action依赖于Servlet API,好比Action的execute方法的参数就包括request和response对象。这使程序难于测试。Struts2中的Action再也不依赖于Servlet API,有利于测试,而且实现TDD。
封装请求参数:Struts1中强制使用ActionForm对象封装请求的参数。Struts2能够选择使用POJO类来封装请求的参数,或者直接使用Action的属性。
表达式语言方面:Struts1中整合了EL,可是EL对集合和索引的支持不强,Struts2整合了OGNL(Object Graph NavigationLanguage)。
绑定值到视图技术:Struts1使用标准的JSP,Struts2使用“ValueStack”技术。
参见文档:http://www.blogjava.net/freeman1984/archive/2011/02/16/344447.html
类型转换:Struts1中的ActionForm基本使用String类型的属性。Struts2中使用OGNL进行转换,能够更方便的使用。
数据校验:Struts1中支持覆盖validate方法或者使用Validator框架。Struts2支持重写validate方法或者使用XWork的验证框架。
Action执行控制的对比:Struts1支持每个模块对应一个请求处理,可是模块中的全部Action必须共享相同的生命周期。Struts2支持经过拦截器堆栈为每个Action建立不一样的生命周期。
<filter>
<filter-name>action2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>action2</filter-name>
<url-pattern>*.action</url-pattern>
</filter-mapping>
Hibernate是一个对象关系映射框架,它对JDBC进行了很是轻量的对象封装,使得java程序员能够为所欲为地使用对象编程思惟来操纵数据库。
Assigned(依赖用户)
Assigned方式由用户生成主键值,而且要在save()以前指定不然会抛出异常
特色:主键的生成值彻底由用户决定,与底层数据库无关。用户须要维护主键值,在调用session.save()以前要指定主键值。
Hilo(依赖数据表)
Hilo使用高低位算法生成主键,高低位算法使用一个高位值和一个低位值,而后把算法获得的两个值拼接起来做为数据库中的惟一主键。Hilo方式须要额外的数据库表和字段提供高位值来源。默认状况下使用的表是 hibernate_unique_key,默认字段叫做next_hi。next_hi必须有一条记录不然会出现错误。
特色:须要额外的数据库表的支持,能保证同一个数据库中主键的惟一性 ,但不能保证多个数据库之间主键的惟一性。Hilo主键生成方式由Hibernate 维护,因此Hilo方式与底层数据库无关,但不该该手动修改hi/lo算法使用的表的值,不然会引发主键重复的异常。
Increment(依赖数据Sequence)
Increment方式对主键值采起自动增加的方式生成新的主键值,但要求底层数据库的支持Sequence。如Oracle,DB2等。须要在映射文件xxx.hbm.xml中加入Increment标志符的设置。
这个是由Hibernate在内存中生成主键,每次增量为1,不依赖于底层的数据库,所以全部的数据库均可以使用,但问题也随之而来,因为是Hibernate生成的,因此只能有一个Hibernate应用进程访问数据库,不然就会产生主键冲突,不能在集群状况下使用插入数据的时候hibernate会给主键添加一个自增的主键,可是一个hibernate实例就维护一个计数器,因此在多个实例运行的时候不能使用这个方法。
特色:由Hibernate自己维护,适用于全部的数据库,不适合多进程并发更新数据库,适合单一进程访问数据库。不能用于群集环境。
Identity(自增)
Identity当时根据底层数据库,来支持自动增加,不一样的数据库用不一样的主键增加方式。
特色:与底层数据库有关,要求数据库支持Identity,如MySQl中是auto_increment, SQL Server 中是Identity,支持的数据库有MySql、SQL Server、DB二、Sybase和HypersonicSQL。 Identity无需Hibernate和用户的干涉,使用较为方便,但不便于在不一样的数据库之间移植程序。
Sequence(依赖数据Sequence)
Sequence须要底层数据库支持Sequence方式,例如Oracle数据库等
特色:须要底层数据库的支持序列,支持序列的数据库有DB二、PostgreSql、Oracle、SAPDb等在不一样数据库之间移植程序,特别从支持序列的数据库移植到不支持序列的数据库须要修改配置文件
Native(依赖具体的数据库)
Native主键生成方式会根据不一样的底层数据库自动选择Identity、Sequence、Hilo主键生成方式
特色:根据不一样的底层数据库采用不一样的主键生成方式。因为Hibernate会根据底层数据库采用不一样的映射方式,所以便于程序移植,项目中若是用到多个数据库时,能够使用这种方式。
UUID(依赖算法)
UUID使用128位UUID算法生成主键,可以保证网络环境下的主键惟一性,也就可以保证在不一样数据库及不一样服务器下主键的惟一性。
特色;可以保证数据库中的主键惟一性,生成的主键占用比较多的存贮空间
Foreign GUID(外键)
Foreign用于一对一关系中,使用外部表的字段做为主键。
Hibernate 中提供了两级Cache,第一级别的缓存是Session级别的缓存,它是属于事务范围的缓存。这一级别的缓存由hibernate管理的,通常状况下无需进行干预;第二级别的缓存是SessionFactory级别的缓存,它是属于进程范围或群集范围的缓存。这一级别的缓存能够进行配置和更改,而且能够动态加载和卸载。 Hibernate还为查询结果提供了一个查询缓存,它依赖于第二级缓存。
一级缓存的管理
当应用程序调用Session的save()、update()、saveOrUpdate()、get()或load(),以及调用查询接口的 list()、iterate()或filter()方法时,若是在Session缓存中还不存在相应的对象,Hibernate就会把该对象加入到第一级缓存中。当清理缓存时,Hibernate会根据缓存中对象的状态变化来同步更新数据库。 Session为应用程序提供了两个管理缓存的方法: evict(Object obj):从缓存中清除参数指定的持久化对象。 clear():清空缓存中全部持久化对象。
二级缓存的管理
Hibernate的二级缓存策略的通常过程以下:
1) 条件查询的时候,老是发出一条select * from table_name where …. (选择全部字段)这样的SQL语句查询数据库,一次得到全部的数据对象。
2) 把得到的全部数据对象根据ID放入到第二级缓存中。
3) 当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,若是配置了二级缓存,那么从二级缓存中查;查不到,再查询数据库,把结果按照ID放入到缓存。
4) 删除、更新、增长数据的时候,同时更新缓存。
Hibernate的二级缓存策略,是针对于ID查询的缓存策略,对于条件查询则毫无做用。为此,Hibernate提供了针对条件查询的Query Cache。
什么样的数据适合存放到第二级缓存中?
1 不多被修改的数据 2 不是很重要的数据,容许出现偶尔并发的数据 3 不会被并发访问的数据 4 参考数据,指的是供应用参考的常量数据,它的实例数目有限,它的实例会被许多其余类的实例引用,实例极少或者历来不会被修改。
不适合存放到第二级缓存的数据?
1 常常被修改的数据 2 财务数据,绝对不容许出现并发 3 与其余应用共享的数据。
经常使用的缓存插件 Hibernater 的二级缓存是一个插件
下面是几种经常使用的缓存插件:
EhCache:可做为进程范围的缓存,存放数据的物理介质能够是内存或硬盘,对Hibernate的查询缓存提供了支持。
OSCache:可做为进程范围的缓存,存放数据的物理介质能够是内存或硬盘,提供了丰富的缓存数据过时策略,对Hibernate的查询缓存提供了支持。
SwarmCache:可做为群集范围内的缓存,但不支持Hibernate的查询缓存。
JBossCache:可做为群集范围内的缓存,支持事务型并发访问策略,对Hibernate的查询缓存提供了支持。
上述4种缓存插件的对比状况列于表9-3中。
表9-3 4种缓存插件的对比状况
缓 存 插 件 |
支 持 只 读 |
支持非严格读写 |
支 持 读 写 |
支 持 事 务 |
EhCache |
是 |
是 |
是 |
|
OSCache |
是 |
是 |
是 |
|
SwarmCache |
是 |
是 |
|
|
JBossCache |
是 |
|
|
是 |
它们的提供器列于表9-4中。
表9-4 缓存策略的提供器
缓 存 插 件 |
提供器(Cache Providers) |
Hashtable(只能测试时使用) |
org.hibernate.cache.HashtableCacheProvider |
EhCache |
org.hibernate.cache.EhCacheProvider |
OSCache |
org.hibernate.cache.OSCacheProvider |
SwarmCache |
org.hibernate.cache.SwarmCacheProvider |
JBossCache |
org.hibernate.cache.TreeCacheProvider |
在默认状况下,Hibernate使用EhCache进行JVM级别的缓存。用户能够经过设置Hibernate配置文件中的hibernate.cache.provider_class的属性,指定其余的缓存策略,该缓存策略必须实现org.hibernate.cache.CacheProvider接口。
配置二级缓存的主要步骤:
1) 选择须要使用二级缓存的持久化类,设置它的命名缓存的并发访问策略。这是最值得认真考虑的步骤。
2) 选择合适的缓存插件,而后编辑该插件的配置文件。
缓存做用范围
事务范围(一级缓存——Session级别)
事务存在与内存中,事务范围内的对象都有一个对象ID(OID),它对应数据库表中的代理主键(没有实际意义仅用来标示记录号,相对于有实际意义的天然主键)
进程范围(二级缓存——SessionFactory级别)
缓存内的数据被进程中的事务共享操做数据时需运用所机制
集群范围(二级缓存——SessionFactory级别)
一级缓存Session
减小访问数据库的频率
Transaction tx = session . beginTransaction();
//第一次
Bird b1 = (Bird) session . get(Bird . class , new long(1));
//第二次
Bird b2 = (Bird) session . get(Bird . class , new long(1));
tx . commit();
session第一次加载对象时先从缓存中查找,如没找到在从数据库中查找并加载到缓存中;第二次加载时直接从缓存中加载而不需再次访问数据库。
保证数据库中的相关记录与缓存中相应对象保持同步
Session缓存中的数据在提交数据库以前进行了几回修改,当数据提交时只执行一次最后一次修改的update操做。
配置二级缓存步骤
选择须要使用二级缓存的持久化类,设置它的二级缓存并发访问策略(只读,只写,可读可写)。
选择合适的缓存插件,每一种缓存插件都有自带的配置文件,须要手动配置。
例如配置ehcache缓存
一、在Hibernate配置文件中,指定ehcache适配器
Hibernate . cfg . xml添加元素
//二级缓存存在的类
<property name=”hibernate . cache . provider_class”>org . hibernate . cache . EhCacheProvide</Property>
/查询时是否使用二级缓存
<property name=”hibernate . cache . use_query_cache”>true</Property>
二、在映射文件中配置<cache>元素
XXX . hbm . xml
//指定并发访问策略
<cache usage=”read-write”/>
三、编写ehcache . xml,并将此文件放在src文件夹下
eacache . xml
<?xml version=”1.0” encoding=”UTF-8”?>
<ehcache>
<diskStore path=”java . io . tempdir”/>
<!--
maxElementsInMemory:容许缓存可存储的总记录数
eternal:当前缓存是否永远不过时
overflowToDisk:当前缓存中数据达到最大值时,是否把缓存数据写入硬盘
timeToIdleSeconds:当前缓存最大闲置时间,超过该时间则销毁缓存
timeToLiveSeconds:设置超时时间,当缓存建立后达到该时间就自动销毁
-->
<defaultCache
maxElementsInMemory=”5”
eternal=”false”
overflowToDisk=”true”
timeToIdleSeconds=”15”
timeToLiveSeconds=”120”
/>
</ehcache>
Hibernate映射分为:内置映射和自定义映射。
使用<property>定义属性
使用<many-to-one>配置多对一映射
使用<one-to-one>配置一对一映射(主键关联和惟一外键关联)
Hibernate的核心接口一共有5个,分别为:Session、SessionFactory、Transaction、Query和Configuration。这5个核心接口在任何开发中都会用到。经过这些接口,不只能够对持久化对象进行存取,还可以进行事务控制。
Session接口
Session接口负责执行被持久化对象的CRUD操做(CRUD的任务是完成与数据库的交流,包含了不少常见的SQL语句。)。但须要注意的是Session对象是非线程安全的。同时,Hibernate的session不一样于JSP应用中的HttpSession。这里当使用session这个术语时,其实指的是Hibernate中的session,而之后会将HttpSession对象称为用户session。
SessionFactory接口
SessionFactory接口负责初始化Hibernate。它充当数据存储源的代理,并负责建立Session对象。这里用到了工厂模式。须要注意的是SessionFactory并非轻量级的,由于通常状况下,一个项目一般只须要一个SessionFactory就够,当须要操做多个数据库时,能够为每一个数据库指定一个SessionFactory。
Configuration接口
Configuration接口负责配置并启动Hibernate,建立SessionFactory对象。在Hibernate的启动的过程当中,Configuration类的实例首先定位映射文档位置、读取配置,而后建立SessionFactory对象。
Transaction接口
Transaction接口负责事务相关的操做。它是可选的,开发人员也能够设计编写本身的底层事务处理代码。
Query和Criteria接口
Query和Criteria接口负责执行各类数据库查询。它能够使用HQL语言或SQL语句两种表达方式。
注意:Configuration实例时一个启动期间的对象,一旦SessionFactory建立完成它就被丢弃。
自由态(未曾进行持久化,未与任何Session相关联)
持久态(仅与一个Session相关联)
游离态(已经进行了持久化,但未与当前任何Session相关联)
Transient(自由态)状态最大的特征是:
* 在数据库中没有与之匹配的数据
* 没有归入session的管理
Persistent(持久态)状态最大的特征是:
* 在数据库中有与之匹配的数据
* 归入了session的管理
* 在清理缓存(脏数据检查)的时候,会和数据库同步
Detached(游离态)状态最大的特征是:
* 在数据库中有与之匹配的数据
* 没有归入session的管理
状态转换:
save()方法保存对象 引起INSERT
load()方法装载对象 若是没有匹配的数据库记录,load()方法可能会抛出没法恢复的异常。
get()方法装载对象 会当即访问数据库,若是没有对象的记录,则返回null。
flush()方法强制提交刷新。
update()方法提交游离状态的对象。
delete()方法移除持久化对象。
refresh ()方法强制装载对象。
不带参数查询
Query query = session.createQuery(“from User”);
带参数查询
Query query = session.createQuery(“from User where username=:username”);
Query.setLockMode(“user”,LockMode.upgrade); 加锁
Query.setString(“username”,”admin”);
也可以使用setParameterList()设置该参数
List names = new ArrayList();
names.add(“admin”);
names.add(“test”);
Query query = session.createQuery(“from User where username in(:unameList)”);
Query.setString(“unameList”,names);
还能够带问号
Query query = session.createQuery(“from User where username=?”);
Query.setString(0,”admin”);
setProperties()方法:
在Hibernate中能够使用setProperties()方法,将命名参数与一个对象的属性值绑定在一块儿,以下程序代码:
Customer customer=new Customer();
customer.setName(“pansl”);
customer.setAge(80);
Query query=session.createQuery(“from Customer c where c.name=:name and c.age=:age ”);
query.setProperties(customer);
取得List结果集
List list =query.list();
取得迭代结果集
Iterator iter=query.iterate();
或
Iterator iter=query.list().iterator();
list()和iterator()区别:查询机制不一样
list方法仅查询一次,查询所有数据
iterator可能会查询屡次,首先从数据库中检索出全部符合条件的记录的id字段(仅有id),而后扫描缓存,若是缓存中包含所有数据则无需再查询数据库,直接引用。若是缓存中不包含任何数据须要再次查询数据库。
大多数状况下使用list进行查询,当对象包含大量属性,或者要加载的大部分数据已经在缓存中,可以使用iterator;
取得一个对象
Query query = session.createQuery(“from User where username=:username”);
Query.setString(“username”,”admin”);
User user = (User)query.uniqueResult();
标量查询(查某个具体的字段或是统计函数等)
略
分页查询
Query query = session.createQuery(“from User”);
query.setFirstResult(10);//设置起始范围,从第11数据开始取
query.setMaxResults(20);//设置结束范围,最多能取20条数据(若是有的话)
List list =query.list();
建立SQL查询
Query query = session.createSQLQuery(“select {user.*} from User {user} where username=?”);
其余同样(别名须要大括号括起来)
建立Criteria实例
Criteria criteria = session.createCriteria(User.class);
criteria.setMaxResults(50);
List users= criteria.list();
添加查询条件
criteria.add(Restrictions.like(“username”,”%admin%”));
criteria.add(Restrictions.between(“ID”,1,10));
添加排序
criteria.add(Order.asc(“username”));
示例查询
User user=new User();
user.setUsernem(“admin”);
criteria.add(Example.create(user));
一、读取并解析配置文件
Configuration config=new Configuration() . configure();
二、读取并解析映射信息,并建立SessionFactory
SessionFactory sessionfactory=config . buildSessionFactory();
三、打开session
this . session=sessionfactory . openSession();
四、打开一个事务(增删改必选,查询可选)
tran=this.session . beginTransaction();
五、持久化操做
this . session . save(login);(增长操做)
this . session . update(login);(修改操做)
this . session . dalete(login)(删除操做)
六、提交事务
tran . commit();
七、关闭session
This . session.close();
基于JTA的事物管理
JTA提供了跨Session的事物管理能力
JTA事物管理则由JTA容器实现,JTA容器对当前加入事务的众多Connection 进行调度
JTA的事务周期可横跨多个JDBC Connection生命周期
JTA事务是由JTA Container维护,而参与事务的Connection无需对事务管理进行干涉
LockMode.NONE : 无锁机制
LockMode.WRITE : Hibernate在Insert和Update记录时会自动获取
LockMode.READ :Hibernate在读取记录时会自动获取
LockMode.UPGRADE : 利用数据库的for update子句加锁
LockMode.UPGRADE_NOWAIT : Oracle的特定实现,利用Oracle的for update nowait子句实现加锁
Criteria.setLockMode
Query.setLockMode
Session.lock
Session不是线程安全的,因此让多个线程共享一个Session发生数据冲突,因此Session须要管理
ThreadLocal模式: ThreadLocal并非一个线程的本地实现,即他不是一个线程(Thread),而是一个线程局部变量(Thread local variable)。它的做用就是为每个使用这个变量的线程都提供一个变量值的副本,而且每个线程均可以独立的改变本身的副本,而不会和其余线程的副本产生冲突。
openSession()和getCurrentSession()
建立session的方法除了有openSession()以外,getCurrentSession()也能够建立Session;
getCurrentSession()建立的Session会绑定到当前线程,而openSession()不会
getCurrentSession()建立的Session会在事务提交或回滚后自动关闭,而openSession()则须要手动关闭(调用Session的close()方法)
使用getCurrentSession()的方法
当使用本地事务时(JDBC事务)
须要在hibernate.cfg.xml文件的<session-factory>节点中添加
<property name=”hibernate.current_session_context_class”>thread</property>
当使用全局事务时(JTA事务)
须要在hibernate.cfg.xml文件的<session-factory>节点中添加
<property name=”hibernate.current_session_context_class”>jta</property>
持久化对象不经过外键创建对象之间的关联关系,而是经过属性
持久化对象之间的关联关系的种类:一对一,一对多(多对一),多对多
持久化类之间关联关系的方向:单向关联,双向关联
一对多(在年级的xml中)
<set name=”students”>
<key column=”gid”/> //年级 <!—此处gid为Student表中的gid外键字段-->
<one-to-many class=”com . pb . hibernate . pb . Students”/>
</set>
多对一(在学生中)
<many-to-one name=”grade” class=”hib3.po.Grade” cascade=”save-update”>
<column name=”gid”/>
</many-to-one>
配置反转属性:inverse,将维护关联关系的任务反转,交给对方完成
inverse只能用于集合属性中,即关联关系中“一”的一方;反转关联关系给“多”的一方。
即:
<set name=”students” inverse=“true”>
<key column=”gid”/>
<one-to-many class=”com . pb . hibernate . pb . Students”/>
</set>
一对一关联
在People类中添加License类的属性,在License类中添加People的属性
People . hbm . xml中经过<one-to-one>标签实现映射
License . hbm . xml中经过<many-to-one>标签实现映射
People类 License类
pid :int lid :int
pname :String 1:1 ldesc :String
pgender :String people :People
license :License
People类People . hbm . xml(主动方)
<one-to-one name=”license” class=”hib3.po.License” cascade=”all” lazy=”false”/>
注:在<one-to-one>标签中通常吧cascade属性值设为all,lazy属性设为false
License类License . hbm . xml(被动方)
<many-to-one name=”people” class=”hib3.po.People” unique=”true” lazy=”false”>
<!--外键设置在被动方表中-->
<column name=”pid”/>
</many-to-one>
注:unique意为惟一,hibernate经过unique属性将多对一关系限制为一对一;
多对多关联关系
一、不建立中间表的持久化类,只建立两端表的持久化类,在映射文件中使用<many-to-many>标签设置映射
Student.hbm.xml
<set name=”courses” table=”sc” cascade=”save-update”>
<!-- Student与中间表发生关联的字段是“sid”-->
<key column=”sid”>
<!—中间表与Course类发生关联的字段是“cid”-->
<many-to-many class=”hib3.po.Course” column=”cid”>
</set>
Course.hbm.xml
<set name=”students” table=”sc” cascade=”save-update” inverse=“true”>
<!-- Course与中间表发生关联的字段是“cid”-->
<key column=”cid”>
<!—中间表与Student类发生关联的字段是“sid”-->
<many-to-many class=”hib3.po.Student” column=”sid”>
</set>
注:在其中一方添加inverse属性,只有一方维护关联关系
二、建立中间表,两端表的持久化类,针对中间表的持久化类分别和两端表的持久化类建立一对多的关联关系
分别是HQL查询 ,对象化查询Criteria方法,动态查询DetachedCriteria,例子查询,sql查询,命名查询。
HQL查询
经过Query接口使用HQL语言进行查询,HQL是hibernate本身的一套查询语言,于SQL语法不一样,具备跨数据库的优势。
对象化查询方式
经过Criteria等接口和类进行查询。
动态分离查询DetachedCriteria
面向对象操做,分离业务与底层,不须要字段属性摄入到Dao实现层
SQL查询方式
使用原声SQL语言进行查询。
命名查询方式
适用状况:万能方法,有点像ibatis轻量级框架的操做,方便维护。 缺点:不面向对象。基于hql和sql,有必定缺陷。
属性查询有直接查询和经过构造方法查询。
fetch关键字
用来表示家在对象时查询语句的形式和加载时机。有3个取值:
select:加载关联对象时经过select语句实现。默认
subselect:经过带子查询语句实现
join:执行当即检索,采用迫切左外链接检索全部关联对象,lazy属性将被忽略
注:select和subselect适用于当即检索和延迟检索,jion仅适用于当即检索
select和subselect决定加载关联对象时查询语句的形式,join决定加载时机
HQL语句混杂在代码中会破坏代码可读性
Hibernate容许在影射配置文件中定义字符串形式的查询语句,便是命名查询。
命名查询——映射配置文件
<hibernate-mapping>
<class name=”hib3.po.Login” table=”LOGIN”>
<!--。。。。。省略其余配置-->
</class>
<query name=”loginUser”>
<![CDATA[
from Login login where login.username=:username and login.password=:password]]>
</query>
</hibernate-mapping>
以<![CDATA[HQL]]>方式保存HQL语句
在程序中使用Session的getNamedQuery(“hql语句名”)方法获取配置文件中的hql语句。
<!--设置JDBC单次批量处理数目(通常10-50)-->
<property name=”hibernate . jdbc . batch_size”>20</property>
(Session缓存为Hibernate一级缓存,是事务范围内的缓存;Session外置缓存是Hibernate二级缓存,是应用范围内的缓存,即全部事物共享二级缓存。二级缓存默认关闭)
<property name=”hibernate . cache . use_second_level_cache”>false</property>
批量更新处理:使用可滚动结果集(ScrollableResults)
//ScrollableResults对象中存储的是游标,但须要此对象时才会到数据库中加载
ScrollableResults logins = session . createQuery(“from Login”) . scroll(ScrollMode . forward_only);
使用CallableStatement调用存储过程
CallableStatement cst = session . connection() . prepareCall(“{call login_insert(?,?,?)}”)
cst . setString(1,“aaa”);
cst . setString(2,“22222”);
cst . setLong (3,“21”);
cst . executeUpadte();
调用带有返回结果集的存储过程
Session session = new Configuration() . configure() . buildSessionFactory() . openSession();
Transaction tran = session . beginTransaction();
CallableStatement cst = session . connection() . prepareCall(“{call login_getlist(?)}”)
cst . registerOutParameter(1 , oracle . jdbc . OracleType . cursor);
cst . execute();
ResultSet rs=(ResultSet)cst . getObject(1);
while(rs.next()){
……
}
使用命名SQL调用存储过程
使用命名SQL的插入方法(更新功能相似)
使用注解方式来注释类,属性
能够省略类——表映射文件(hbm . xml文件)
Hibernate注解的优点
简化对象关系映射的开发
经过注解方式能够自动生成数据库表
使用注解进行对象映射步骤
一、为每个实体Bean使用@Entity注解
二、使用@ID指定实体Bean的标识属性
三、使用@Table指明当前实体Bean对应数据库那张表
四、使用@GeneratedValue指定主键生成类型
五、修改Hibernate配置文件
Spring是一个开源框架,它由Rod Johnson建立。它是为了解决企业应用开发的复杂性而建立的。Spring使用基本的JavaBean来完成之前只可能由EJB完成的事情。然而,Spring的用途不只限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用均可以从Spring中受益。
简单来讲,Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。
◆轻量——从大小与开销两方面而言Spring都是轻量的。完整的Spring框架能够在一个大小只有1MB多的JAR文件里发布。而且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。
◆控制反转——Spring经过一种称做控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会经过被动的方式传递进来,而不是这个对象本身建立或者查找依赖对象。你能够认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。
◆面向切面——Spring提供了面向切面编程的丰富支持,容许经过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该作的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。
◆容器——Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你能够配置你的每一个bean如何被建立——基于一个可配置原型(prototype),你的bean能够建立一个单独的实例或者每次须要时都生成一个新的实例——以及它们是如何相互关联的。然而,Spring不该该被混同于传统的重量级的EJB容器,它们常常是庞大与笨重的,难以使用。
◆框架——Spring能够将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了不少基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。
全部Spring的这些特征使你可以编写更干净、更可管理、而且更易于测试的代码。它们也为Spring中的各类模块提供了基础支持。
◆方便解耦,简化开发
经过Spring提供的IoC容器,咱们能够将对象之间的依赖关系交由Spring进行控制,避免硬编码所形成的过分程序耦合。有了Spring,用户没必要再为单实例模式类、属性文件解析等这些很底层的需求编写代码,能够更专一于上层的应用。
◆AOP编程的支持
经过Spring提供的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能能够经过AOP轻松应付。
◆声明式事务的支持
在Spring中,咱们能够从单调烦闷的事务管理代码中解脱出来,经过声明式方式灵活地进行事务的管理,提升开发效率和质量。
◆方便程序的测试
能够用非容器依赖的编程方式进行几乎全部的测试工做,在Spring里,测试再也不是昂贵的操做,而是随手可作的事情。
◆方便集成各类优秀框架
Spring不排斥各类优秀的开源框架,相反,Spring能够下降各类框架的使用难度,Spring提供了对各类优秀框架(如Struts,Hibernate、Hessian、Quartz)等的直接支持。
◆下降Java EE API的使用难度
Spring对不少难用的Java EE API(如JDBC,JavaMail,远程调用等)提供了一个薄薄的封装层,经过Spring的简易封装,这些Java EE API的使用难度大为下降。
Spring核心容器(提供框架的基本功能,核心容器的组件是BeanFactory,是工厂模式的体现,使用IOC(控制反转)模式将应用程序的配置和依赖性规范与实际的应用代码分开)。
Spring上下文 是一个配置文件(applicationContext.xml),向框架提供上下文信息。包括的服务如JNDI,调度等。
Spring AOP 面向方面的编程,主要用于支持事务管理。
Spring Dao 提供异常层次结构,可管理异常处理和不一样数据库供应商抛出的错误信息。
Spring ORM 集成了多个ORM框架,提供ORM的对象关系工具。
Spring Web 集成了MVC的框架,如Struts等
Spring MVC 自身提供的MVC框架,高度的可配置性。
定义:由容器来控制业务和业务之间的依赖关系,而非传统的经过代码来直接操控,此处控制权的转移就是所谓的反转。
实现策略:依赖查找(相似与JDNI的实现)、依赖注入(DI)
其中依赖注入是一种更加合适的方法,让容器去全权负责依赖查询,受管组件只须要暴露JavaBean的setter方法或者带参数的构造子或者接口,使容器能够在初始化时组装对象的依赖关系。
好处是:查询依赖操做和应用代码分离;受控对象不会使用到容器特定的API,这样咱们的受控对象能够搬出容器单独使用。
依赖注入有三种:又有接口注入(Interface Injection),设值注入(Setter Injection)和构造子注入(Constructor Injection)三种方式。
4种Bean封装机制:
BeanWrapper机制
BeanFactory机制
ApplicationContext机制:由BeanFactory扩展而来。
WebContext机制
配置XML元数据:服务层对象、DAO数据访问层对象、Action表示层对象、Hibernate SessionFactory对象、JMS Queue对象等。
举例:
Object obj=Class.forName("domain.Test").newInstance();
BeanWrapperwrapper=newBeanWrapperImpl(obj);//BeanWrapper实现了Wrapper接口
wrapper.setPropertyValue("testN","sasa");
Testte=(Test)obj;
System.out.println(te.getTestN());
实例化容器
Resource r = new FileSystemResource(“beans.xml”);
BeanFactory factory = new XmlBeanFactory(r);
或者
ClassPathResource r = new ClassPathResource(“beans.xml”);
BeanFactory factory = new XmlBeanFactory(r);
或者
ApplicationContext ctx = null;
ctx = new ClassPathXmlApplicationContext(new String[]{“beans1.xml”,”beans2.xml”});
BeanFactory factory = (BeanFactory)ctx;
注意:以上都是相对路径。
BeanFactory提供的方法
Boolean containsBean(String);
Object getBean(String)
Object getBean(String,Class)
Class getType(String name)
Boolean isSingleton(String)
String[] getAliases(String) 经过bean名称的全部别名
配置<bean>的属性
id、class、factory-method(必须静态的方法)、factory-bean(使用是class属性必须为空)、scope(singleton、prototype、request、session、global session)、depends-on (指定依赖的bean))、lazy-init(true、false)、init-method(初始化回调)、destory-method(bean析构回调)、parent(继承bean)
配置<bean>的子元素
<property>(主要是针对属性注入)
简单:name value
集合:
Properties 使用<props><prop key>value</prop></props>
List 使用<list><value>value</value></list>
Map 使用
<map>
<entry>
<key>
<value>value</value>
</key>
<value>value</value>
</entry>
</map>
Set 使用<set><value>value</value></set>
注意:map的key或value值,或set的value值不能是如下元素:bean、ref、idref、list、set、map、props、value、null。
配置<constructor-arg>元素
一、用type指定输入参数的java类型,value指定输入参数的值
二、用ref属性指定其余的bean
三、使用<ref>子元素指向另外一个bean
上下文
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext*.xml</param-value>
</context-param>
监听
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
字符集的设置
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
1、简介
AOP也就是面向切面编程,做为面向对象编程的补充。面向切面编程(AOP)和面向对象编程(OOP)互为补充,面向对象编程将程序分解成各个层次的对象,而面向切面编程将程序运行过程分解成各个切面,能够这样理解,面向对象编程从静态角度考虑程序结构,面向切面编程是从动态角度考虑运行过程。AOP专门用来处理具备横切性质的系统服务,如事务管理、安全检查、缓存、对象池管理等。
2、概念
切面(Aspect):业务流程运行的某个特定步骤,也就是应用运行过程当中的关注点,关注点能够横切多个对象,因此经常也称为横切关注点。
链接点(Joinpoint):程序运行过程当中明确的点,如方法的调用,或者异常的抛出。Spring AOP中,链接点老是方法的调用,指的就是Method。
加强处理(Advice):AOP框架在特定的切入点执行的加强处理。处理有”around”、”before”、”after”等类型。
切入点(Pointcut):能够插入加强处理的链接点。简而言之,当某个链接点知足指定要求时,该链接点将被添加加强处理,该链接点也就变成了切入点。
一组Joinpoint,就是说一个Advice可能在多个地方织入。
引入:将方法或者字段添加到被处理的类中。Spring容许引入新的接口到任何被处理的对象。
目标对象(Target):被AOP框架进行加强处理的对象,也被称为加强的对象。若是AOP框架是经过运行时代理来实现的,那么这个对象是一个被代理的对象。
AOP代理:AOP框架建立的对象,简单地说代理就是对目标对象的增强。Spring中的AOP代理能够是JDK动态代理,也能够是DBLIB代理。前者为实现接口的目标对象的代理,后者为不实现接口的目标对象的代理。
织入(Weaving):将加强处理添加到目标对象中,并建立一个被加强的对象(AOP代理)的过程就是织入。织入有两种方式:编译时加强(AspectJ)和运行时加强(DGLIB)。
3、AOP 种类
一、静态织入:指在编译时期就织入Aspect代码,AspectJ好像是这样作的。
二、动态织入:在运行时期织入,Spring AOP属于动态织入,动态织入又分静动两种,静则指织入过程只在第一次调用时执行;动则指根据代码动态运行的中间状态来决定如何操做,每次调用Target的时候都执行(性能较差)。
四、Spring AOP 代理原理
Spring AOP 是使用代理来完成的,Spring 会使用下面两种方式的其中一种来建立代理:
一、JDK动态代理,特色只能代理接口,性能相对较差,须要设定一组代理接口。
二、CGLIB 代理,可代理接口和类(final method除外),性能较高(生成字节码)。
5、经常使用加强处理
@Before("execution(* kuozhan.before.*.*(..))")
@AfterReturning(pointcut="execttion(* kuozhan.after.*.*(..))",returning="rvt")
@AfterThrowing(pointcut="execution(* lee.*.*(..))", throwing="ex")
@After("execution(* lee.*.*(..))")
@Around("execution(* lee.*.*(..))")
设计模式(Design pattern)是一套被反复使用、多数人知晓的、通过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、可维护性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石同样。
开闭原则(对扩展开放,对修改关闭)
如简单工厂模式,只要再建立一个子类就能够了,而后在工厂的方法中返回一个新的子类对象就能够了。
里氏替换原则(任何基类能够出现的地方,子类必定能够出现)
若是调用的是父类的话,那么换成子类也彻底能够运行。Java编译程序会检查程序是否符合里氏代换原则。还记得java继承的一个原则吗?子类override方法的访问权限不能小于父类对应方法的访问权限。能够说:里氏代换原则是继承复用的一个基础。
合成复用原则(少用继承,多用合成/聚合)
在面向对象的设计里,有两种基本的办法能够在不一样的环境中复用已有的设计和实现,即经过合成/聚合或经过继承。
合成/聚合复用:
优势:支持包装、黑箱复用看不见内部的细节。依赖较少。这种复用能够在运行时间内动态进行,新对象能够动态地引用与成分对象类型相同的对象。
缺点:是经过使用这种复用建造的系统会有较多的对象须要管理
继承复用:
优势:新的实现较为容易,由于超类的大部分功能均可以经过继承关系自动进入子类,修改或扩展继承而来的实现较为容易。
缺点:继承复用破坏包装,由于继承将超类的实现细节暴露给子类。由于超类的内部细节经常是对子类透明的,所以这种复用是透明的复用,又称“白箱”复用。若是超类的实现发生改变,那么子类的实现也不得不发生改变。从超类忌辰而来的实现是静态的,不可能在运行时间内发生改变,所以没有足够的灵活性。
通常来讲,对违反里氏代换原则的设计进行重构时,能够采起两种办法:一是加入一个抽象超类;二是将继承关系改写为合成/聚合关系。
“IS-A”是严格的分类学意义上的定义,意思是一个类是另外一个类的“一种”。而“HAS-A”则不一样,它表示某一个角色具备某一项责任。
依赖倒置原则(抽象不该该依赖于细节,细节应当依赖于抽象)
要针对接口编程,而不是针对实现编程。
传递参数,或者在组合聚合关系中,尽可能引用层次高的类。
主要是在构造对象时能够动态的建立各类具体对象,固然若是一些具体类比较稳定,就没必要在弄一个抽象类作它的父类,这样有多此一举的感受。
接口隔离原则(使用多个专门的接口比使用单一的总接口要好)
一个类对另一个类的依赖性应当是创建在最小的接口上的。
一个接口表明一个角色,不该当将不一样的角色都交给一个接口。没有关系的接口合并在一块儿,造成一个臃肿的大接口,这是对角色和接口的污染。
“不该该强迫客户依赖于它们不用的方法。接口属于客户,不属于它所在的类层次结构。”这个说得很明白了,再通俗点说,不要强迫客户使用它们不用的方法,若是强迫用户使用它们不使用的方法,那么这些客户就会面临因为这些不使用的方法的改变所带来的改变。
迪米特原则(最少知识原则。不要和陌生人说话)
迪米特法则的各类表述
① 只与你直接的朋友们通讯;
② 不要跟“陌生人”说话;
③ 每个软件单位对其余的单位都只有最少的知识,并且局限于那些与本单位密切相关的软件单位。
狭义的迪米特法则
☆ 若是两个类没必要彼此直接通讯,那么这两个类就不该当发生直接的相互做用。若是其中的一个类须要调用另一个类的某一个方法,能够经过第三者转发这个调用。
在系统里造出大量的小方法,这些方法仅仅是传递间接的调用,与系统的商务逻辑无关。
遵循类之间的迪米特法则会是一个系统的局部设计简化,由于每个局部都不会和远距离的对象有直接的关联。可是,这也会形成系统的不一样模块之间的通讯效率下降,也会使系统的不一样模块之间不容易协调。
门面模式和调停者模式实际上就是迪米特法则的应用。
广义的迪米特法则在类的设计上的体现:
优先考虑将一个类设置成不变类。
尽可能下降一个类的访问权限。
谨慎使用Serializable。
尽可能下降成员的访问权限。
注意:在进行设计的时候,咱们尽可能从抽象类继承,而不是从具体类继承。若是从继承等级树来看,全部叶子节点应当是具体类,而全部的树枝节点应当是抽象类或者接口。固然这个只是一个通常性的指导原则,使用的时候还要具体状况具体分析。
模式名称:一个助记名,它用一两个词来描述模式的问题、解决方案和效果。
问题:描述了应该在什么时候使用模式。
解决方案:描述了设计的组成成分,它们之间的相互关系及各自的职责和协做方式。
效果:描述了模式应用的效果及使用模式应权衡的问题。
提供一个建立一系列相关或相互依赖对象的接口,而无须指定它们的具体类。客户类和工厂类分开。消费者任什么时候候须要某种产品,只需向工厂请求便可。消费者无须修改就能够接纳新产品。缺点是当产品修改时,工厂类也要作相应的修改。
生成器模式也称为建造者模式。将一个复杂对象的构建与他的表示相分离,使得一样的构建过程能够建立不一样的表示。将产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生成具备不一样的内部表象的产品对象。建造模式使得产品内部表象能够独立的变化,客户没必要知道产品内部组成的细节。建造模式能够强制实行一种分步骤进行的建造过程。
核心工厂类再也不负责全部产品的建立,而是将具体建立的工做交给子类去作,成为一个抽象工厂角色,仅负责给出具体工厂类必须实现的接口,而不接触哪个产品类应当被实例化这种细节。
简单工厂模式
经过给出一个原型对象来指明所要建立的对象的类型,而后用复制这个原型对象的方法建立出更多同类型的对象。原始模型模式容许动态的增长或减小产品类,产品类不须要非得有任何事先肯定的等级结构,原始模型模式适用于任何的等级结构。缺点是每个类都必须配备一个克隆方法。
在Java中Prototype模式变成clone()方法的使用,因为Java的纯洁的面向对象特性,使得在Java中使用设计模式变得很天然,二者已经几乎是浑然一体了。这反映在不少模式上,如Iterator遍历模式。
保证一个类只有一个实例,并提供一个访问它的全局访问点
提供一个方法顺序访问一个聚合对象的各个元素,而又不须要暴露该对象的内部表示。
定义对象间一对多的依赖关系,当一个对象的状态发生改变时,全部依赖于它的对象都获得通知自动更新(以前写的天气预报)。
定义一个操做中的算法的骨架,而将一些步骤延迟到子类中,TemplateMethod使得子类能够不改变一个算法的结构便可以重定义该算法得某些特定步骤。
将一个请求封装为一个对象,从而使你能够用不一样的请求对客户进行参数化,对请求排队和记录请求日志,以及支持可撤销的操做。Command模式是很是简单而又优雅的一种设计模式,它的根本目的在于将“行为请求者”与“行为实现者”解耦。
容许对象在其内部状态改变时改变他的行为。对象看起来彷佛改变了他的类。主要解决的是当控制一个对象状态的条件表达式过于复杂时的状况。把状态的判断逻辑转移到表示不一样状态的一系列类中,能够把复杂的判断逻辑简化。
意图:容许一个对象在其内部状态改变时改变它的行为
适用场景:
1.一个对象的行为取决于它的状态,而且它必须在运行时刻根据状态改变它的行为。
2.一个操做中含有庞大的多分支结构,而且这些分支决定于对象的状态。
策略模式和状态模式是双胞胎,它们有相同的类图,可是它们的意图不一样。策略模式是围绕能够互换的算法来成功建立业务的,然而状态模式是经过改变对象内部的状态来帮助对象控制本身的行为。
定义一系列的算法,把他们一个个封装起来,并使他们能够互相替换,本模式使得算法能够独立于使用它们的客户。
优势:
一、 提供了一种替代继承的方法,并且既保持了继承的优势(代码重用)还比继承更灵活(算法独立,能够任意扩展)。
二、 避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展。
三、 遵照大部分GRASP原则和经常使用设计原则,高内聚、低偶合。
缺点:
一、 由于每一个具体策略类都会产生一个新类,因此会增长系统须要维护的类的数量。
见例子:
1. 抽象策略
package com.eekq.strategy;
public interface IStrategy {
/**策略方法*/
public abstract double add();
}
2. 具体策略,这里我以两个具体策略为例
package com.eekq.strategy;
public class ConcreteStrategy1 implements IStrategy {
/**示意性算法*/
public double add() {
// TODO 自动生成方法存根
System.out.println(this.getClass().getName() + "的加法运算");
return 0;
}
}
package com.eekq.strategy;
public class ConcreteStrategy2 implements IStrategy {
public double add() {
// TODO 自动生成方法存根
System.out.println(this.getClass().getName() + "的加法运算");
return 0;
}
}
3.环境角色
package com.eekq.strategy;
public class Context {
/**环境角色类*/
private IStrategy strategy;
public Context(IStrategy strategy) {
this.strategy = strategy;
}
/**策略方法*/
public double add() {
this.strategy.add();
return 0;
}
}
4.客户端调用
package com.eekq.strategy;
public class Main {
/**
*@paramargs
*/
public static void main(String[] args) {
// TODO 自动生成方法存根
Context context = new Context(new ConcreteStrategy1());
context.add();//执行算法1
context = new Context(new ConcreteStrategy2());
context.add();//执行算法2
}
}
5.执行结果:
com.eekq.strategy.ConcreteStrategy1的加法运算
com.eekq.strategy.ConcreteStrategy2的加法运算
6.总结
优势:动态改变算法或行为
缺点:客户端必须知道全部的策略类,并自行决定使用哪个策略类,必须对每个算法了解
使多个对象都有机会处理请求,从而避免请求的送发者和接收者之间的耦合关系 。一个链能够是一条线,一个树,也能够是一个环。以下图所示,责任链是一个树结构的一部分。在责任链模式里,不少对象由每个对象对其下家的引用而链接起来造成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪个对象最终处理这个请求,这使得系统能够在不影响客户端的状况下动态地从新组织链和分配责任。
// "Handler"
abstract class Handler
{
// Fields
protected Handler successor;
// Methods
public void SetSuccessor( Handler successor )
{
this.successor = successor;
}
abstract public void HandleRequest( int request );
}
// "ConcreteHandler1"
class ConcreteHandler1 : Handler
{
// Methods
override public void HandleRequest( int request )
{
if( request >= 0 && request < 10 )
Console.WriteLine("{0} handled request {1}",
this, request );
else
if( successor != null )
successor.HandleRequest( request );
}
}
中介者又叫调停者,用一个中介对象封装一些列的对象交互。
表示一个做用于某对象结构中的各元素的操做,它使你能够在不改变各元素类的前提下定义做用于这个元素的新操做。访问者模式用于对不一样节点进行操做的状况(如List的中的多个元素并不相同),若是将操做放在节点中,会增长节点的复杂性,并不易维护,若是将操做放在调用函数中,则会出现多个判断语句,对不一样的节点作不一样的操做,增长了节点和调用函数之间的耦合。为了解决这样的问题,在调用函数(对节点进行操做的函数)和节点之间增长了vistor的类,在vistor中实现对各个节点的操做。
给定一个语言,定义他的文法的一个表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
在不破坏对象的前提下,捕获一个对象的内部状态,并在该对象以外保存这个状态。
将对象组合成树形结构以表示部分总体的关系,Composite使得用户对单个对象和组合对象的使用具备一致性。
为子系统中的一组接口提供一致的界面,fa?ade提供了一高层接口,这个接口使得子系统更容易使用。
为其余对象提供一种代理以控制对这个对象的访问
将一类的接口转换成客户但愿的另一个接口,Adapter模式使得本来因为接口不兼容而不能一块儿工做那些类能够一块儿工做。
动态地给一个对象增长一些额外的职责,就增长的功能来讲,Decorator模式相比生成子类更加灵活。
将抽象部分与它的实现部分相分离,使他们能够独立的变化。
用来尽量减小内存使用量以及分享资讯给尽量多的类似物件;它适合用于当大量物件只是重复于是致使没法使人接受的使用大量内存。一般物件中的部分状态是能够分享。常见作法是把它们放在外部数据结构,当须要使用时再将它们传递给享元。
JDBC(Java Database Connectivity): JDBC API为访问不一样的数据库提供了一种统一的途径,象ODBC同样,JDBC对开发者屏蔽了一些细节问题,另外,JDCB对数据库的访问也具备平台无关性。
一、JNDI(Java Name and Directory Interface): JNDI API被用于执行名字和目录服务。它提供了一致的模型来存取和操做企业级的资源如DNS和LDAP,本地文件系统,或应用服务器中的对象。
二、JNDI(Java Naming and Directory Interface)是一个应用程序设计的API,为开发人员提供了查找和访问各类命名和目录服务的通用、统一的接口,相似JDBC都是构建在抽象层上。
三、JNDI可访问的现有的目录及服务有:
DNS、XNam 、Novell目录服务、LDAP(Lightweight Directory Access Protocol 轻型目录访问协议)、 CORBA对象服务、文件系统、Windows XP/2000/NT/Me/9x的注册表、RMI、DSML v1&v二、NIS。
四、优势:
包含了大量的命名和目录服务,使用通用接口来访问不一样种类的服务;
能够同时链接到多个命名或目录服务上;
创建起逻辑关联,容许把名称同Java对象或资源关联起来,而没必要知道对象或资源的物理ID。
五、JNDI与JDBC
JNDI提供了一种统一的方式,能够用在网络上查找和访问服务。经过指定一个资源名称,该名称对应于数据库或命名服务中的一个记录,同时返回数据库链接创建所必须的信息。
JNDI主要有两部分组成:应用程序编程接口和服务供应商接口。应用程序编程接口提供了Java应用程序访问各类命名和目录服务的功能,服务供应商接口提供了任意一种服务的供应商使用的功能。
代码示例:
try{
Context cntxt = new InitialContext();
DataSource ds = (DataSource) cntxt.lookup("jdbc/dpt");
}
catch(NamingException ne){
...
}
六、JNDI与JMS
消息通讯是软件组件或应用程序用来通讯的一种方法。JMS就是一种容许应用程序建立、发送、接收、和读取消息的JAVA技术。
代码示例:
try{
Properties env = new Properties();
InitialContext inictxt = new InitialContext(env);
TopicConnectionFactory connFactory = (TopicConnectionFactory) inictxt.lookup("TTopicConnectionFactory");
...
}
catch(NamingException ne){
...
}
访问特定目录:举个例子,人是个对象,他有好几个属性,诸如这我的的姓名、电话号码、电子邮件地址、邮政编码等属性。
经过getAttributes()方法
Attribute attr = directory.getAttributes(personName).get("email");
String email = (String)attr.get();
经过使用JNDI让客户使用对象的名称或属性来查找对象:
foxes = directory.search("o=Wiz,c=US", "sn=Fox", controls);
经过使用JNDI来查找诸如打印机、数据库这样的对象,查找打印机的例子: Printer printer = (Printer)namespace.lookup(printerName); printer.print(document);
浏览命名空间:
NamingEnumeration list = namespace.list("o=Widget, c=US"); while (list.hasMore()) {
NameClassPair entry = (NameClassPair)list.next(); display(entry.getName(), entry.getClassName());
}
EJB(Enterprise JavaBean): J2EE技术之因此赢得某体普遍重视的缘由之一就是EJB。它们提供了一个框架来开发和实施分布式商务逻辑,由此很显著地简化了具备可伸缩性和高度复杂的企业级应用的开发。EJB规范定义了EJB组件在什么时候如何与它们的容器进行交互做用。容器负责提供公用的服务,例如目录服务、事务管理、安全性、资源缓冲池以及容错性。但这里值得注意的是,EJB并非实现J2EE的惟一途径。正是因为J2EE的开放性,使得有的厂商可以以一种和EJB平行的方式来达到一样的目的。
分类:分别是会话Bean(Session Bean),实体Bean(Entity Bean)和消息驱动Bean(MessageDriven Bean)。
1.Session Bean用于实现业务逻辑,它能够是有状态的,也能够是无状态的。每当客户端请求时,容器就会选择一个Session Bean来为客户端服务。Session Bean能够直接访问数据库,但更多时候,它会经过Entity Bean实现数据访问。
2.Entity Bean是域模型对象,用于实现O/R映射,负责将数据库中的表记录映射为内存中的Entity对象,事实上,建立一个Entity Bean对象至关于新建一条记录,删除一个Entity Bean会同时从数据库中删除对应记录,修改一个Entity Bean时,容器会自动将Entity Bean的状态和数据库同步。
3.MessageDriven Bean是EJB2.0中引入的新的企业Bean,它基于JMS消息,只能接收客户端发送的JMS消息而后处理。MDB其实是一个异步的无状态Session Bean,客户端调用MDB后无需等待,马上返回,MDB将异步处理客户请求。这适合于须要异步处理请求的场合,好比订单处理,这样就能避免客户端长时间的等待一个方法调用直到返回结果。
RMI(Remote Method Invoke): 正如其名字所表示的那样,RMI协议调用远程对象上方法。它使用了序列化方式在客户端和服务器端传递数据。RMI是一种被EJB使用的更底层的协议。
举例:
//接口RmiSample
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface RmiSample extends Remote {
public int sum(int a, int b) throws RemoteException;
}
//实现类RmiSampleImpl
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class RmiSampleImpl extends UnicastRemoteObject implements RmiSample {
protected RmiSampleImpl() throws RemoteException {
super();
// TODO Auto-generated constructor stub
}
@Override
public int sum(int a, int b) throws RemoteException {
// TODO Auto-generated method stub
return a+b;
}
}
//服务器RmiSampleServer
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
public class RmiSampleServer {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try {LocateRegistry.createRegistry(8083) ;
RmiSampleImpl Server = new RmiSampleImpl();
// 将该对象实例与名称“SAMPLE-SERVER”捆绑
Naming.rebind("rmi://localhost:8083/SAMPLE-SERVER" , Server);
} catch (java.net.MalformedURLException me) {
System.out.println("Malformed URL: " + me.toString());
} catch (RemoteException re) {
System.out.println("Remote exception: " + re.toString());
}
}
}
//客户端
import java.rmi.Naming;
import java.rmi.RemoteException;
public class RmiSampleClient {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
String url = "rmi://localhost:8083/SAMPLE-SERVER";
RmiSample RmiObject = (RmiSample) Naming.lookup(url);
System.out.println(" 1 + 2 = " + RmiObject.sum(1, 2));
} catch (RemoteException exc) {
System.out.println("Error in lookup: " + exc.toString());
} catch (java.net.MalformedURLException exc) {
System.out.println("Malformed URL: " + exc.toString());
} catch (java.rmi.NotBoundException exc) {
System.out.println("NotBound: " + exc.toString());
}
}
}
Java IDL/CORBA: 在Java IDL的支持下,开发人员能够将Java和CORBA集成在一块儿。他们能够建立Java对象并使之可在CORBA ORB中展开, 或者他们还能够建立Java类并做为和其它ORB一块儿展开的CORBA对象的客户。后一种方法提供了另一种途径,经过它Java能够被用于将你的新的应用和旧的系统相集成。
CORBA的核心是开放软件总线——ORB,它提供了网络环境无关性、操做系统无关性和开发语言无关性的公共平台。在面向对象的应用环境中,CORBA对象(本文所说的“对象”是面向对象系统中的术语,是指一个实体)的请求者没必要知道它所请求的对象是在哪里,是如何实现的,而是由ORB来负责跨平台的运做管理,无须应用系统的开发者干预。CORBA所具备的跨平台、分布式、面向对象的这些优势使它具备广阔的应用前景,日益受到软件行业的重视。
CORBA是一个中间件规范并非一个实体软件。软件开发者经过使用第三方的ORB工具或IDL语言来定义CORBA对象,实现ORB功能。
JSP(Java Server Pages): JSP页面由HTML代码和嵌入其中的Java代码所组成。服务器在页面被客户端所请求之后对这些Java代码进行处理,而后将生成的HTML页面返回给客户端的浏览器。
Servlet是一种小型的Java程序,它扩展了Web服务器的功能。做为一种服务器端的应用,当被请求时开始执行,这和CGI Perl脚本很类似。Servlet提供的功能大多与JSP相似,不过实现的方式不一样。JSP一般是大多数HTML代码中嵌入少许的Java代码,而servlets所有由Java写成而且生成HTML。
XML是一种能够用来定义其它标记语言的语言。它被用来在不一样的商务过程当中共享数据。XML的发展和Java是相互独立的,可是,它和Java具备的相同目标正是平台独立性。经过将Java和XML的组合,您能够获得一个完美的具备平台独立性的解决方案。
JMS是用于和面向消息的中间件相互通讯的应用程序接口(API)。它既支持点对点的域,有支持发布/订阅(publish/subscribe)类型的域,而且提供对下列类型的支持:经承认的消息传递,事务型消息的传递,一致性消息和具备持久性的订阅者支持。JMS还提供了另外一种方式来对您的应用与旧的后台系统相集成。
JMS即Java消息服务(Java Message Service)应用程序接口是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通讯。
JMS 使您可以经过消息收发服务(有时称为消息中介程序或路由器)从一个 JMS 客户机向另外一个 JMS客户机发送消息。消息是 JMS 中的一种类型对象,由两部分组成:报头和消息主体。报头由路由信息以及有关该消息的元数据组成。消息主体则携带着应用程序的数据或有效负载。根据有效负载的类型来划分,能够将消息分为几种类型,它们分别携带:简单文本 (TextMessage)、可序列化的对象 (ObjectMessage)、属性集合 (MapMessage)、字节流 (BytesMessage)、原始值流 (StreamMessage),还有无有效负载的消息 (Message)。
JMS提供者
链接面向消息中间件的,JMS接口的一个实现。提供者能够是Java平台的JMS实现,也能够是非Java平台的面向消息中间件的适配器。
JMS客户
生产或消费基于消息的Java的应用程序或对象。
JMS生产者
建立并发送消息的JMS客户。
JMS消费者
接收消息的JMS客户。
JMS消息
包括能够在JMS客户之间传递的数据的对象
JMS队列
一个容纳那些被发送的等待阅读的消息的区域。队列暗示,这些消息将按照顺序发送。一旦一个消息被阅读,该消息将被从队列中移走。
JMS主题
一种支持发送消息给多个订阅者的机制。
ConnectionFactory 接口(链接工厂)
用户用来建立到JMS提供者的链接的被管对象。JMS客户经过可移植的接口访问链接,这样当下层的实现改变时,代码不须要进行修改。 管理员在JNDI名字空间中配置链接工厂,这样,JMS客户才可以查找到它们。根据消息类型的不一样,用户将使用队列链接工厂,或者主题链接工厂。
Connection 接口(链接)
链接表明了应用程序和消息服务器之间的通讯链路。在得到了链接工厂后,就能够建立一个与JMS提供者的链接。根据不一样的链接类型,链接容许用户建立会话,以发送和接收队列和主题到目标。
Destination 接口(目标)
目标是一个包装了消息目标标识符的被管对象,消息目标是指消息发布和接收的地点,或者是队列,或者是主题。JMS管理员建立这些对象,而后用户经过JNDI发现它们。和链接工厂同样,管理员能够建立两种类型的目标,点对点模型的队列,以及发布者/订阅者模型的主题。
MessageConsumer 接口(消息消费者)
由会话建立的对象,用于接收发送到目标的消息。消费者能够同步地(阻塞模式),或异步(非阻塞)接收队列和主题类型的消息。
MessageProducer 接口(消息生产者)
由会话建立的对象,用于发送消息到目标。用户能够建立某个目标的发送者,也能够建立一个通用的发送者,在发送消息时指定目标。
Message 接口(消息)
是在消费者和生产者之间传送的对象,也就是说从一个应用程序传送到另外一个应用程序。一个消息有三个主要部分:
消息头(必须):包含用于识别和为消息寻找路由的操做设置。
一组消息属性(可选):包含额外的属性,支持其余提供者和用户的兼容。能够建立定制的字段和过滤器(消息选择器)。
一个消息体(可选):容许用户建立五种类型的消息(文本消息,映射消息,字节消息,流消息和对象消息)。
消息接口很是灵活,并提供了许多方式来定制消息的内容。
Session 接口(会话)
表示一个单线程的上下文,用于发送和接收消息。因为会话是单线程的,因此消息是连续的,就是说消息是按照发送的顺序一个一个接收的。会话的好处是它支持事务。若是用户选择了事务支持,会话上下文将保存一组消息,直到事务被提交才发送这些消息。在提交事务以前,用户能够使用回滚操做取消这些消息。一个会话容许用户建立消息生产者来发送消息,建立消息消费者来接收消息。
1. 消息通讯模型
JMS 支持两种消息通讯模型:点到点(point-to-point)(PTP)模型和发布/订阅(Pub/Sub)模型。除了下列不一样以外,这两种消息通讯模型很是地类似:
PTP 模型规定了一个消息只能有一个接收者;Pub/Sub 模型容许一个消息能够有多个接收者。
2. 消息组成
消息传递系统的中心就是消息。
一条 Message 分为三个组成部分:
头(header)是个标准字段集,客户机和供应商都用它来标识和路由消息。
属性(property)支持把可选头字段添加到消息。若是您的应用程序须要不使用标准头字段对消息编目和分类,您就能够添加一个属性到消息以实现这个编目和分类。提供 set<Type>Property(...) 和 get<Type>Property(...) 方法以设置和获取各类 Java 类型的属性,包括 Object。JMS 定义了一个供应商选择提供的标准属性集。
消息的主体(body)包含要发送给接收应用程序的内容。每一个消息接口特定于它所支持的内容类型。
JMS 为不一样类型的内容提供了它们各自的消息类型,可是全部消息都派生自 Message 接口。
StreamMessage:包含 Java 基本数值流,用标准流操做来顺序的填充和读取。
MapMessage:包含一组名/值对;名称为 string 类型,而值为 Java 的基本类型。
TextMessage:包含一个 String。
ObjectMessage:包含一个 Serializable Java 对象;能使用 JDK 的集合类。
BytesMessage:包含未解释字节流: 编码主体以匹配现存的消息格式。
XMLMessage: 包含XML内容。扩展TextMessage,XMLMessage 类型的使用,使得消息过滤很是便利。
3. 消息确认模式
非事务性会话中,应用程序建立的会话有5 种确认模式,而在事务性会话中,确认模式被忽略。
五种确认模式说明:
AUTO_ACKNOWLEDGE:自动确认模式。一旦接收方应用程序的方法调用从处理消息处返回,会话对象就会确认消息的接收。
CLIENT_ACKNOWLEDGE:客户端确认模式。会话对象依赖于应用程序对被接收的消息调用一个acknowledge()方法。一旦这个方法被调用,会话会确认最后一次确认以后全部接收到的消息。这种模式容许应用程序以一个调用来接收,处理并确认一批消息。注意:在管理控制台中,若是链接工厂的Acknowledge Policy(确认方针)属性被设置为"Previous"(提早),可是你但愿为一个给定的会话确认全部接收到的消息,那么就用最后一条消息来调用acknowledge()方法。
DUPS_OK_ACKNOWLEDGE:容许副本的确认模式。一旦接收方应用程序的方法调用从处理消息处返回,会话对象就会确认消息的接收;并且容许重复确认。在须要考虑资源使用时,这种模式很是有效。注意:若是你的应用程序没法处理重复的消息的话,你应该避免使用这种模式。若是发送消息的初始化尝试失败,那么重复的消息能够被从新发送。
NO_ACKNOWLEDGE:不确认模式。不确认收到的消息是须要的。消息发送给一个NO_ACKNOWLEDGE 会话后,它们会被WebLogic 服务器当即删除。在这种模式下,将没法从新得到已接收的消息,并且可能致使下面的结果:1. 消息可能丢失;和(或者)另外一种状况:2. 若是发送消息的初始化尝试失败,会出现重复消息被发送的状况。
MULTICAST_NO_ACKNOWLEDGE:IP组播下的不确认模式,一样无需确认。发送给一个MULTICAST_NO_ACKNOWLEDGE会话的消息, 会共享以前所述的NO_ACKNOWLEDGE 确认模式同样的特征。这种模式支持但愿经过IP 组播方式进行消息通讯的应用程序,并且无需依赖会话确认提供的服务质量。注意:若是你的应用程序没法处理消息的丢失或者重复,那么你应该避免使用这种模式。若是发送消息的初始化尝试失败的话,重复的消息可能会被再次发送。
注:在上表的5 种确认模式中,AUTO_ACKNOWLEDGE ,DUPS_OK_ACKNOWLEDGE 和CLIENT_ACKNOWLEDGE 是JMS 规范定义的,NO_ACKNOWLEDGE 和MULTICAST_NO_ACKNOWLEDGE是WebLogic JMS 提供的。
//获取初始化容器
private static InitialContext getInitialContext() throws NamingException {
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
env.put(Context.PROVIDER_URL, PROVIDER_URL);
return new InitialContext(env);
}
具体列子,在weblogic中配置JMS服务器
一、新建JMS Servers
二、新建JMS Model
三、新建工厂
四、新建队列(点对点)(或新建发布/订阅)
代码:
点对点:写一个服务类(发送类),以及一个客户端类(接受类)
//代码略
订阅/发布模式
//发布者部分代码
ctx = new InitialContext();
// 1.经过JNDI查询ConnectionFactory。JMS中链接工厂分QueueConnectionFactory和
// TopicConnectionFactory两种,Weblogic不区分这两种类型。
TopicConnectionFactory factory = (TopicConnectionFactory) ctx
.lookup("ConnectionFactoryTest");
// 2.经过工厂创建链接connection
connection = factory.createTopicConnection();
// 3.建立session
session = connection.createTopicSession(false,
TopicSession.AUTO_ACKNOWLEDGE);
// 4.建立Topic,必须新建一个Topic JNDI。
Topic topic = (Topic) ctx.lookup("TopicTest");
// 5.建立TopicPublisher
TopicPublisher tp = session.createPublisher(topic);
// 构造消息体
TextMessage tm = session.createTextMessage();
tm.setText("csdn tczxg");
tm.setStringProperty("user", "嘿嘿,JMS挺简单的123。。。");
tp.publish(tm);
// 必须关闭链接
connection.close();
//接收者(订阅者)部分代码
ctx = new InitialContext();
// 1.经过JNDI查询ConnectionFactory
TopicConnectionFactory factory = (TopicConnectionFactory) ctx
.lookup("ConnectionFactoryTest");
// 2.经过工厂创建链接connection
connection = factory.createTopicConnection();
// 3.建立session
session = connection.createTopicSession(false,
TopicSession.AUTO_ACKNOWLEDGE);
// 4.建立Topic
Topic topic = (Topic) ctx.lookup("TopicTest");
// 5.建立订阅者
TopicSubscriber ts = session.createSubscriber(topic);
connection.start();
// 构造消息体
Message message = ts.receive();
if (message instanceof TextMessage) {
TextMessage tm = (TextMessage) message;
String user = tm.getStringProperty("user");
System.out.println(tm.getText() + ", user : " + user);
}
connection.close();
JTA定义了一种标准的API,应用系统由此能够访问各类事务监控。
JTS是CORBA OTS事务监控的基本的实现。JTS规定了事务管理器的实现方式。该事务管理器是在高层支持Java Transaction API (JTA)规范,而且在较底层实现OMG OTS specification的Java映像。JTS事务管理器为应用服务器、资源管理器、独立的应用以及通讯资源管理器提供了事务服务。
JavaMail是用于存取邮件服务器的API,它提供了一套邮件服务器的抽象类。不只支持SMTP服务器,也支持IMAP服务器。
JAF是一个专用的数据处理框架,它用于封装数据,并为应用程序提供访问和操做数据的接口。JAF的主要做用在于让java应用程序知道如何对一个数据源进行查看、编辑和打印等操做。
Web Services是由企业发布的完成其特定商务需求的在线应用服务,其余公司或应用软件可以经过Internet来访问并使用这项在线服务。
它是一种构建应用程序的广泛模型,能够在任何支持网络通讯的操做系统中实施运行;它是一种新的web 应用程序分支,是自包含、自描述、模块化的应用,能够发布、定位、经过web调用。Web Service是一个应用组件,它逻辑性的为其余应用程序提供数据与服务.各应用程序经过网络协议和规定的一些标准数据格式(Http,XML,Soap)来访问Web Service,经过Web Service内部执行获得所需结果.Web Service能够执行从简单的请求到复杂商务处理的任何功能。一旦部署之后,其余Web Service应用程序能够发现并调用它部署的服务。
XFire是新一代的Java Web服务引擎,XFire使得在JavaEE应用中发布Web服务变得垂手可得。和其余Web服务引擎相比,XFire的配置很是简单,能够很是容易地和Spring集成,它使得Java开发人员终于能够得到和.Net开发人员同样的开发效率。
举例:首先应用相关的xfile的jar文件
一、Web.xml中配置
<servlet>
<servlet-name>XFireServlet</servlet-name>
<servlet-class>org.codehaus.xfire.transport.http.XFireConfigurableServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>XFireServlet</servlet-name>
<url-pattern>/servlet/XFireServlet/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>XFireServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
二、service.xml中配置
<beans>
<service xmlns="http://xfire.codehaus.org/config/1.0">
<name>SayHiService</name>
<namespace>http://cn.com.pansky/SayHiService</namespace>
<serviceClass>com.xfire.SayHiService</serviceClass>
<implementationClass>com.xfire.SayHiServiceImpl</implementationClass>
</service>
</beans>
三、接口
public interface SayHiService {
public String sayHi(String name);
}
四、实现类
public class SayHiServiceImpl implements SayHiService {
@Override
public String sayHi(String name) {
if (name == null) {
return "连名字也不愿告诉我吗?";
}
return name + ", 你吃了吗?没吃回家吃去吧。";
}
public String 不告诉你() {
return "个人名字不告诉你!";
}
}
五、客户端调用类
public class tt {
/**
* @param args
*/
public static void main(String[] args) {
String serviceURL = "http://localhost:8082/TestWebservice/services/SayHiService";
Service serviceModel = new ObjectServiceFactory().create(
SayHiService.class, null, "http://cn.com.pansky/SayHiService",
null);
XFireProxyFactory serviceFactory = new XFireProxyFactory();
try {
SayHiService service = (SayHiService) serviceFactory.create(
serviceModel, serviceURL);
Client client = Client.getInstance(service);
// client.addOutHandler(new OutHeaderHandler());
// disable timeout
client.setProperty(CommonsHttpMessageSender.HTTP_TIMEOUT, "1");
String hello = service.sayHi("张山疯");
System.out.println("服务器对[张山疯] 的回答是:" + hello);
hello = service.sayHi(null);
System.out.println("服务器胡言乱语说:" + hello);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
脏读又称无效数据的读出,是指在数据库访问中,事务T1将某一值修改,而后事务T2读取该值,此后T1由于某种缘由撤销对该值的修改,这就致使了T2所读取到的数据是无效的。脏读就是指当一个事务正在访问数据,而且对数据进行了修改,而这种修改尚未提交到数据库中,这时,另一个事务也访问这个数据,而后使用了这个数据。由于这个数据是尚未提交的数据,那么另一个事务读到的这个数据是脏数据,依据脏数据所作的操做多是不正确的。
第一类更新丢失(回滚丢失)
第二类更新丢失(覆盖丢失)
两个事务都同时更新一行数据,可是后来提交的事务所作的修改会覆盖前面提交的事务的修改或者后面的事务回滚了前面的事务提交的数据,致使结果不是用户想要的结果。这是由于系统没有执行任何的锁操做,所以并发事务并无被隔离开来。
1、解决思路
基本两种思路,一种是悲观锁,另一种是乐观锁; 简单的说就是一种(悲观锁)假定这样的问题是高几率的,最好一开始就锁住,省得更新总是失败;另一种(乐观锁)假定这样的问题是小几率的,最后一步作更新的时候再锁住,省得锁住时间太长影响其余人作有关操做。
2、解决方案
一、悲观锁
a.传统的悲观锁法(不推荐):
在弹出修改页面初始化时(这种状况下通常会去从数据库查询出来),在这个初始化查询中使用select ...for update nowait, 经过添加for update nowait语句,将这条记录锁住,避免其余用户更新,从而保证后续的更新是在正确的状态下更新的。而后在保持这个连接的状态下,在作更新提交。固然这个有个前提就是要保持连接,就是要对连接要占用较长时间,这个在如今web系统高并发高频率下显然是不现实的。
b.如今的悲观锁法(推荐优先使用):
在修改这个页面作提交时先查询下,固然这个查询必须也要加锁(select ...for update nowait),有人会说,在这里作个查询确认记录是否有改变不就好了吗,是的,是要作个确认,只是你不加for update就不能保证你在查询到更新提交这段时间里这条记录没有被其余会话更新过,因此这种方式也须要在查询时锁定记录,保证在这条记录没有变化的基础上再作更新,如有变化则提示告知用户。
二、乐观锁
a.旧值条件(前镜像)法:
就是在sql更新时使用旧的状态值作条件,SQL大体以下 Update table set col1 = newcol1value, col2 = newcol2value…. where col1 = oldcol1value and col2 = oldcol2value….,在上面的例子中咱们就能够把当前工资做为条件进行更新,若是这条记录已经被其余会话更新过,则本次更新了0行,这里咱们应用系统通常会作个提示告知用户从新查询更新。这个取哪些旧值做为条件更新视具体系统实际状况而定。(这种方式有可能发生阻塞,若是应用其余地方使用悲观锁法长时间锁定了这条记录,则本次会话就须要等待,因此使用这种方式时最好统一使用乐观锁法。)
b.使用版本列法(推荐优先使用):
其实这种方式是一个特殊化的前镜像法,就是不须要使用多个旧值作条件,只须要在表上加一个版本列,这一列能够是NUMBER或 DATE/TIMESTAMP列,加这列的做用就是用来记录这条数据的版本(在表设计时通常咱们都会给每一个表增长一些NUMBER型和DATE型的冗余字段,以便扩展使用,这些冗余字段彻底能够做为版本列用),在应用程序中咱们每次操做对版本列作维护便可。在更新时咱们把上次版本做为条件进行更新。
综上所述,咱们对丢失更新问题建议采起上面的悲观锁b方法或乐观锁b方法(蓝色字体已标注),其实这两种方式的本质都同样,都是在更新提交时作一次查询确认在更新提交,我我的以为都是乐观的作法,区别在于悲观锁b方法是经过select..for update方式,这个可能会致使其余会话的阻塞,而乐观锁b方法须要多一个版本列的维护。
我的建议:在用户并发数比较少且冲突比较严重的应用系统中选择悲观锁b方法,其余状况首先乐观锁版本列法。
一个事务对同一行数据重复读取两次,可是却获得了不一样的结果。它包括如下状况:
(1) 事务T1读取某一数据后,事务T2对其作了修改,当事务T1再次读该数据时获得与前一次不一样的值。
(2) 幻读(Phantom Reads):事务在操做过程当中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据或者缺乏了第一次查询中出现的数据(这里并不要求两次查询的SQL语句相同)。这是由于在两次查询过程当中有另一个事务插入数据或删除数据形成的。
为了不上面出现的几种状况,在标准SQL规范中,定义了4个事务隔离级别,不一样的隔离级别对事务的处理不一样。
也称为读未提交(Read Uncommitted):容许脏读取,但不容许更新丢失。若是一个事务已经开始写数据,则另一个数据则不容许同时进行写操做,但容许其余事务读此行数据。该隔离级别能够经过“排他写锁”实现。
也称为读提交(Read Committed):容许不可重复读取,但不容许脏读取。这能够经过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务容许其余事务继续访问该行数据,可是未提交的写事务将会禁止其余事务访问该行。
可重复读取(Repeatable Read):禁止不可重复读取和脏读取,可是有时可能出现幻影数据。这能够经过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但容许读事务),写事务则禁止任何其余事务。
序列化(Serializable):提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。若是仅仅经过“行级锁”是没法实现事务序列化的,必须经过其余机制保证新插入的数据不会被刚执行查询操做的事务访问到。
隔离级别越高,越能保证数据的完整性和一致性,可是对并发性能的影响也越大。对于多数应用程序,能够优先考虑把数据库系统的隔离级别设为Read Committed。它可以避免脏读取,并且具备较好的并发性能。尽管它会致使不可重复读、虚读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,能够由应用程序采用悲观锁或乐观锁来控制。
隔离级别 |
更新丢失
|
脏读取 |
重复读取 |
幻读 |
未受权读取(排他写锁) |
N |
Y |
Y |
Y |
受权读取(瞬间共享读锁、排他写锁) |
N |
N |
Y |
Y |
可重复读取(共享读锁、排他写锁) |
N |
N |
N |
Y |
串行(行级锁) |
N |
N |
N |
N |
原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)。
在数据库中有两种基本的锁类型:排它锁(Exclusive Locks,即X锁)和共享锁(Share Locks,即S锁)。当数据对象被加上排它锁时,其余的事务不能对它读取和修改。加了共享锁的数据对象能够被其余事务读取,但不能修改。数据库利用这两种基本的锁类型来对数据库的事务进行并发控制。
根据保护的对象不一样,Oracle数据库锁能够分为如下几大类:DML锁(data locks,数据锁),用于保护数据的完整性;DDL锁(dictionary locks,字典锁),用于保护数据库对象的结构,如表、索引等的结构定义;内部锁和闩(internal locks and latches),保护 数据库的内部结构。
DML锁的目的在于保证并发状况下的数据完整性,。在Oracle数据库中,DML锁主要包括TM锁和TX锁,其中TM锁称为表级锁,TX锁称为事务锁或行级锁。
当Oracle 执行DML语句时,系统自动在所要操做的表上申请TM类型的锁。当TM锁得到后,系统再自动申请TX类型的锁,并将实际锁定的数据行的锁标志位进行置位。这样在事务加锁前检查TX锁相容性时就不用再逐行检查锁标志,而只需检查TM锁模式的相容性便可,大大提升了系统的效率。TM锁包括了SS、SX、S、X 等多种模式,在数据库中用0-6来表示。不一样的SQL操做产生不一样类型的TM锁。
在数据行上只有X锁(排他锁)。在 Oracle数据库中,当一个事务首次发起一个DML语句时就得到一个TX锁,该锁保持到事务被提交或回滚。当两个或多个会话在表的同一条记录上执行 DML语句时,第一个会话在该条记录上加锁,其余的会话处于等待状态。当第一个会话提交后,TX锁被释放,其余会话才能够加锁。
当Oracle数据库发生TX锁等待时,若是不及时处理经常会引发Oracle数据库挂起,或致使死锁的发生,产生ORA-60的错误。这些现象都会对实际应用产生极大的危害,如长时间未响应,大量事务失败等。
1、悲观封锁
锁在用户修改以前就发挥做用:
Select ..for update(nowait)
Select * from tab1 for update
用户发出这条命令以后,oracle将会对返回集中的数据创建行级封锁,以防止其余用户的修改。
若是此时其余用户对上面返回结果集的数据进行dml或ddl操做都会返回一个错误信息或发生阻塞。
2、乐观锁
乐观的认为数据在select出来到update进取并提交的这段时间数据不会被更改。这里面有一种潜在的危险就是因为被选出的结果集并无被锁定,是存在一种可能被其余用户更改的可能。所以Oracle仍然建议是用悲观封锁,由于这样会更安全。
表1 Oracle的TM锁类型 |
|||
锁模式 |
锁描述 |
解释 |
SQL操做 |
0 |
none |
|
|
1 |
NULL |
空 |
Select |
2 |
SS(Row-S) |
行级共享锁,其余对象只能查询这些数据行 |
Select for update、Lock for update、Lock row share |
3 |
SX(Row-X) |
行级排它锁,在提交前不容许作DML操做 |
Insert、Update、Delete、Lock row share |
4 |
S(Share) |
共享锁 |
Create index、Lock share |
5 |
SSX(S/Row-X) |
共享行级排它锁 |
Lock share row exclusive |
6 |
X(Exclusive) |
排它锁 |
Alter table、Drop able、Drop index、Truncate table 、Lock exclusive |
1、内部级封锁
内部级封锁是用于保护ORACLE内部结构,由系统内部实现,用户不能访问,所以咱们没必要对此作过多的了解。
2、DDL级封锁(字典/语法分析封锁)
DDL级封锁也是由ORACLE RDBMS来控制,它用于保护数据字典和数据定义改变时的一致性和完整性。它是系统在对SQL定义语句做语法分析时自动地加锁,无需用户干予。字典/语法分析封锁共分三类:
(1)、字典操做锁:用于对字典操做时,锁住数据字典,此封锁是独占的,从而保护任何一个时刻仅能对一个字典操做。
(2)、字典定义锁:用于防止在进行字典操做时又进行语法分析,这样能够避免在查询字典的同时改动某个表的结构。
(3)、表定义锁:用于 一个SQL语句正当访问某个表时,防止字典中与该表有关的项目被修改。
3、DML级封锁
DML级封锁用于控制并发事务中的数据操纵,保证数据的一致性和完整性,其封锁对象能够是表或行。
对用户的数据操纵,Oracle能够自动为操纵的数据进行封锁,但若是有操纵受权,则为知足并发操纵的须要另外实施封锁。DML封锁可由一个用户进程以显式的方式加锁,也可经过某些SQL语句隐含方式实现。
DML锁有以下三种封锁方式:
(1)、共享封锁方式(SHARE)
(2)、独占封锁方式(EXCLUSIVE)
(3)、共享更新封锁(SHARE UPDATE)
一、共享方式的表封锁
共享方式的表封锁是对表中的全部数据进行封锁,该锁用于保护查询数据的一致性,防止其它用户对已封锁的表进行更更新。其它用户只能对该表再施加共享方式的锁,而不能再对该表施加独占方式的封锁,共享更新锁能够再施加,但不容许持有共享更新封锁的进程作更新。共享该表的全部用户只能查询表中的数据,但不能更新。
二、独占方式表封锁
独占方式表封锁是用于封锁表中的全部数据,拥有该独占方式表封锁的用户,便可以查询该表,又能够更新该表,其它的用户不能再对该表施加任何封锁(包括共享、独占或共享更新封锁)。其它用户虽然不能更新该表,但能够查询该表。
三、共享更新封锁方式
共享更新封锁是对一个表的一行或多行进行封锁,于是也称做行级封锁。表级封锁虽然保证了数据的一致性,但却减弱了操做数据的并行性。行级封锁确保在用户取得被更新的行到该行进行更新这段时间内不被其它用户所修改。于是行级锁便可保证数据的一致性又能提升数据操做的迸发性。
SOAP:简单对象访问协议,简单对象访问协议(SOAP)是一种轻量的、简单的、基于 XML 的协议,它被设计成在 WEB 上交换结构化的和固化的信息。 SOAP 能够和现存的许多因特网协议和格式结合使用,包括超文本传输协议( HTTP),简单邮件传输协议(SMTP),多用途网际邮件扩充协议(MIME)。它还支持从消息系统到远程过程调用(RPC)等大量的应用程序。
SOAP 包括四个部分:
SOAP 封装:它定义了一个框架 , 该框架描述了消息中的内容是什么,谁应当处理它以及它是可选的仍是必须的。
SOAP 编码规则:它定义了一种序列化的机制,用于交换应用程序所定义的数据类型的实例。
SOAP RPC 表示:它定义了用于表示远程过程调用和应答的协定。
SOAP 绑定:定义了一种使用底层传输协议来完成在节点间交换SOAP封装的约定。
SOAP 消息基本上是从发送端到接收端的单向传输,但它们经常结合起来执行相似于请求 / 应答的模式。全部的 SOAP 消息都使用 XML 编码。一条 SOAP 消息就是一个包含有一个必需的 SOAP 的封装包,一个可选的 SOAP 标头和一个必需的 SOAP 体块的 XML 文档。
协议结构
SOAP 消息格式:
SOAP 标头
<SOAP-ENV: Envelope Attributes>
<SOAP-ENV:Body Attributes>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
目前主要在web服务中运用。
如:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<funName xmlns="http://tempuri.org/">
<parm1>value1</parm1>
<parm2>value2</parm2>
</funName>
</soap:Body>
</soap:Envelope>
SOAP 通信协议使用 HTTP 来发送XML 格式的信息。HTTP与RPC 的协议很类似,它简单、 配置普遍,而且对防火墙比其它协议更容易发挥做用。HTTP 请求通常由 Web 服务器软件(如 IIS 和Apache)来处理, 但愈来愈多的应用服务器产品正在支持HTTP。XML 做为一个更好的网络数据表达方式( NDR)。SOAP 把 XML 的使用代码化为请求和响应参数编码模式, 并用HTTP 做传输。具体地讲, 一个SOAP 方法能够简单地看做遵循SOAP编码规则的HTTP请求和响应, 一个 SOAP 终端则能够看做一个基于HTTP 的URL, 它用来识别方法调用的目标。像CORBA/ IIOP同样, SOAP不须要具体的对象绑定到一个给定的终端, 而是由具体实现程序来决定怎样把对象终端标识符映像到服务器端的对象。
SOAP是一种基于XML的协议,它用于在分布式环境中发送消息,并执行远程过程调用。使用SOAP,不用考虑任何特定的传输协议(尽管一般选用HTTP协议), 就能使数据序列化。用SOAP来构建平台与语言中性的互操做系统是一个好的选择。总之,SOAP和 Web服务已为在XML上构建分布式应用程序基础结构所需的一切都考虑好了。经过解决COM和Java组件对象模型之间的冲突,SOAP把多个平台在访问数据时所出现的 不兼容性问题减至最少。
forward仅是容器中控制权的转向,在客户端浏览器地址栏中不会显示出转向后的地址;
redirect则是彻底的跳转,浏览器将会获得跳转的地址,并从新发送请求连接。这样,从浏览器的地址栏中能够看到跳转后的连接地址。
因此,forward更加高效,在forward能够知足须要时,尽可能使用forward()方法,而且,这样也有助于隐藏实际的连接。