传智播客JavaWeb听课总结

1、 JavaWeb基础javascript

第一天:css

1.Eclipse详解:html

(1).Bad versionnumber in .class file:编译器版本和运行(JRE)版本不符合。高的JRE版本兼容低版本的编译器版本。前端

(2).当程序有错误的时候,使用Debug as 运行程序。双击语句设置断点。程序运行到此处中止。点击跳入方法的内部代码。点击跳过,执行下一条代码,点击跳出,跳出方法。观察变量的值,选中变量右击 选择watch. 跳入下一个断点。查看断点,调试完后必定要清除断点。结束运行断点的jvm.java

2.HashSet和hashCode和equals方法node

java系统首先调用对象的hashCode()方法得到该对象的哈希吗,而后根据哈希吗找到相应的存储区域,最后取出该存储区域内的每一个元素与该 元素进行比较.两个equals相等,hashCode()相等。须要重写equals,hashCode()方法.更改数据的值,hashCode() 的值也更改了,并未删除.内存泄露.有个东西不在被用,可是未被删除,致使内存泄露.mysql

3.Junit测试框架程序员

(1).在测试类,方法前加注解:@Test,否则出现初始化异常。web

(2).方法before,after前加@Before,@After注解。在测试方法以前和以后运行方法。正则表达式

(3).静态方法beforeClass,afterClass方法前加上注解@BeforeClass,@AfterClass,类加载的时候运行

(4).Assert断言。判断两个对象是否相等。指望值和实际值相等。

4.获得配置文件的路径

经过类加载器 reflect.class.getClassLoader.getResourceAsStream();在class指定目录下查找指定的类文件进行 加载.编译器把src中的.java文件编译成class文件,全部非.java文件,原封不动的搬过去.可是这种方法是只读的.

经过类的信息reflect.class.getResourceAsStream();相对路径

通常用绝对路径,使用配置文件告诉用户路径.

必定要记住要用完整的路径,可是完整的路径不是硬编码的,是计算出来的.

5.反射

(1).反射主要用于框架开发

(2).一个类有多个组成部分,例如:成员变量,方法,构造方法等,反射就是加载类,并解析类的各个组成部分。

(3).加载类使用Class.forName()静态方法,给类的完整名称,包名和类名。

(4).Class提供了解析public的构造方法,方法,字段等方法以及private。字段封装数据,方法执行功能

(5).静态方法无需传递对象,method.invoke()

(6).升级时保持兼容性,main函数的解析有点麻烦,反射解析数组参变量的时候的问题。启动Java程序的main方法的参数是一个字符串数 组,即public static void main(String[] args),经过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按照jdk1.5的语法,整个数组是一个参数,而按照 jdk1.4的语法,数组中的每一个元素对应一个参数,当把一个字符串数组做为参数传递给invoke方法时,javac会到底按照哪一种语法进行处理 呢?jdk1.5确定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成若干个单独的参数。因此,在给main方法传递参数时, 不能使用代码mainMethod.invoke(null,newString[]{"xxx"}),javac只把它当作jdk1.4的语法进行理 解,而不把它当作jdk1.5的语法解释,所以会出现参数类型不对的问题。解决的方法:

mainMethod.invoke(null,newObject[]{new String[]{"xxx"}});

mainMethod.invoke(null,(Object)newString[]{"xxx"});编译器会作特殊处理,编译时不把参数当作数组看待,也就不会把数组打散成若干个参数了.

(7).对数组进行反射:相同的维数(不是数组元素的个数,例如都是一维数组,不关心数组的大小),相同的8种基本数据类型时数组有相同的字节码.

6. 泛型

(1).泛型是对象类型,不能是基本类型,泛型存在源代码级别上,给编译器看的,生成class文件就不存在泛型了

(2).参数类型变量,实际类型变量,泛型类型,参数化的类型

(3).自定义泛型方法:public void method(T args){};public<T,K,V> void method(T a,K b,V c){};

(4).静态方法public static void method(T t){};泛型类不做用静态方法

7.可变参数

(1).可变参数就当作数组,能够使用加强for循环

(2).可变参数列表为最后一个参数

(3).能够给可变参数传递一个数组

(4).可变参数的类型是基本类型仍是对象类型

8.课程介绍

(1).在谷歌心目中,“云”必须具有如下条件:数据都存在网上,而非终端里,软件最终消失,只要你的“云”设备拥有浏览器就能够运行如今的一切, “云”时代的互联网终端设备将不只仅是PC,手机,汽车,甚至手表,只要有简单的操做系统加个浏览器就彻底能够实现,因为数据都在“云”端,企业的IT管 理愈来愈简单,企业和我的用户也不一样在担忧病毒,数据丢失等问题。

(2).李开复描述了这样一个场景,只要你的PC或手机等终端里安装了一个简单的操做系统和完整功能的浏览器,开机后输入本身的用户名和密码,你存在“云”中的应用软件和数据就会同步到终端里。

9.快捷键

(1).配置快捷键:window->preferences->key

(2).Alt+/ :内容提示

Ctrl+1 :快速修复

      Ctrl+Shift+O :快速导入包

      Ctrl+Shift+F :格式化代码

      Alt+方向键 :跟踪代码

      Ctrl+Shift+/ :添加注释

      Ctrl+Shift+\ :取消注释

      Ctrl+Shift+X :更改成大写

      Ctrl+Shift+Y :更改成小写

      Ctrl+Shift+向下键 :复制代码

      Ctrl+Shift+向上,向下  :改变代码行的顺序

      Ctrl+T  :查看继承关系

      Ctrl+Shift+T :查看源代码

      Ctrl+Shift+L :查看全部的快捷键

10.类加载器及其委托机制的深刻分析

(1).Java虚拟机中能够安装多个类加载器,系统默认三个主要类加载器,每一个类负责加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader

(2).类加载器也是Java类,由于其余java类的加载器自己也要被类加载器加载,显然必须有第一个类加载器不是java类,这正是BootStrap(内嵌到JVM的内核中,使用C++语言编写的)

(3).Java虚拟机中的全部类装载器采用具备父子关系的属性结构进行组织,在实例化每一个类转载器对象时,须要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载器

(4).

public class ClassLoaderTest{

public static void main(String[] args){

System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName());

//输出为sun.misc.Lanuncher$AppClassLoader;

System.out.println(System.class.getClassLoader());

//输出为null,由于类System是由BootStrap加载的;

}

}

(5).BootStrap->ExtClassLoader->AppClassLoader

ClassLoader loader= ClassLoaderTest.class.getClassLoader();

while(loader!=null){

System.out.println(loader.getClass().getName());

loader = loader.getParent();//往上顺序打印

}

System.out.println(loader);//最后打印老祖宗

(6).

BootStrap------>JRE/lib/rt.jar

ExtClassLoader----->JRE/lib/ext/*.jar

AppClassLoader------>ClassPath指定的全部jar或目录

用Eclipse的打包工具将ClassLoaderTest打包成itcast.jar,而后放在jre/lib/ext目录下,在 eclipse中运行这个类,运行结果显示为ExtClassLoader,此时的环境状态是classpath目录有 ClassLoaderTest.class,ext/itcast.jar包中也有ClassLoaderTest.class,这时咱们在打印 ClassLoaderTest类的类加载名称,发现是ExtClassLoader,而不是AppClassLoader.

(7).类加载的委托机制:

当Java虚拟机要加载一个类时,到底派出哪一个类加载器去加载呢:

首先当前线程的类加载器去加载线程中的第一个类

若是类A中引用了类B,Java虚拟机将使用加载类A的类加载器来加载类B

还能够直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类

每一个类加载器加载类时,又先委托给其上级类加载器

当全部祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,由于没有getChild方法,即便有,那么多个儿子,找哪个呢?

对着类加载器的层次结构图和委托加载原理

(8).Thread类有一个方法setContextClassLoader(ClassLoader classLoader)//加载当前的类.

(9).每一个类加载都首先会委托送到BootStrap,那么BootStrap很累,这样,那为何很少搞几个BootStrap呢,之因此不这 样作,是由于,便于统一管理,由于全部的类都会找BootStrap,可能这时有几个相同的类进行加载,那么BootStrap,不会屡次将他们的 class文件加载内存中,只会加载一份便可.这样效率就高了.

(10).

public class MyClassLoader{

public static void main(String[]args){

String srcPath=args[0];

String destDir=args[1];//获得目录

String destFileName =srcPath.substring(srcPath.lastIndexOf('/')+1);//获得文件名

String destFilePath=destDir+"\"+destFileName;

FileInputStream fis = newFileInputStream(srcPath);

FileOutputStream fos=new FileOutputStream(destPath);

cypher(fis,fos);

fis.close();

fos.close();

}

private static void cyp(InputStreamips,OutputStream ops){

int b =-1;

while((b=ips.read())!=-1){

ops.write(b^0xff);//对内容进行异或处理

}

}

}

class ClassLoader extends Date{

public String toString(){

return "hello,itcast";

}

}

args[0]:ClassLoader.class的绝对路径

args[1]:itcastlib

有包名的类不能调用无包名的类.

(11).编写本身的类加载器:

知识讲解:

自定义的类加载器必须继承ClassLoader(抽象类)

覆盖findClass方法

defineClass方法:获得class文件转换成字节码

编程步棸:

编写一个文件内容进行简单加密的程序

编写了一个本身的类加载器,可实现对加密过的类进行装载和解密

编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,由于编译器没法识别这个类,程序中能够出了使用ClassLoader.load方法以外,还能够使用设置线程的上下文类加载器或者系统类加载器,而后再使用Class.forName

(12).

模板设计模式:

父类--->loadClass(相同的)

子类1(本身干的)

子类2(本身干的)

覆盖findClass方法(本身干)

(13).

public class MyClassLoader extendsClassLoader{

public MyClassLoader(){

}

public MyClassLoader(String classDir){

this.classDir = classDir;

}

@Override

protected Class<?> findClass(Stringname){

String classFileName = classDir +"\" + name + ".class";

FileInputStream fis = new FileInputStream(classFileName);

ByteArrayOutputStream bos = newByteArrayOutputStream();

cypher(fis,bos);

fis.close();

byte[] bytes = bos.toByteArray();

defineClass(bytes,0,bytes.length);

return super.findClass(name);

}

public static void main(String[] args){

Class clazz = newMyClassLoader("itcastlib").loadClass("ClassLoaderAttachment");

ClassLoaderAttachment d1 =clazz.newInstance();

}

}

(14).windows->showview->problem查看错误.

(15).能够查看Servlet的类加载委托继承树

11. 枚举

(1).枚举的做用限定指定的值,没有枚举前设计一个类,将构造函数设置成私有的,变量设置成静态常量

(2).枚举能够有构造函数(私有的),字段,方法

(3).能够定义set,get方法,获取变量的值。

(4).带有抽象方法的枚举,不能new出新的对象了。在new对象的时候就重写抽象方法,使用匿名内部类

(5).枚举中的每一个枚举值表明枚举类的一个对象。

(6).枚举也能够实现接口,或继承抽象类

(7).JDK5中的switch拓展为除了接受int,short,char,byte外,也能够接受枚举类型。

(8).枚举的方法,name,ordial,valueOf,将字符串转换成枚举值。表单提交数据的时候。values返回枚举的全部的枚举值

12.内省

(1).内省:Introspector,专门操做Bean的属性。

(2).Bean的属性:只有字段提供了set/get方法,就是属性。只要有get/set的方法,就有一个属性,因此属性是有get/set方法决定的

(3).任何类都继承了Object类,又由于Object中有一个class属性

(4).内省的入口类:Introspector,方法getPropertyDescriptors()获取Bean的属性

(5).操做Bean的指定属性

(6).BeanUtils框架操做Bean

(7).在工程中新建一个文件夹,将架包复制到文件夹中,还需另外的架包loging,选中架包,右击build path加入到工程的环境中

(8).BeanUtils使用方便,类型自动转化,只支持8中基本数据类型

(9).给BeanUtils注册类型转换器,ConvertUtils.register()

(10).将Map中的数据整合到对象中,BeanUtils.populate()方法

13.配置Java模板代码

window->preferences->java->Editor->Template:编辑模板代码:line_selection,光标cursor,右击选择source with

14.享元模式

相同的对象只实例化一个,实例:桌面上的图标,word中字符,有不少小的对象,有不少相同的属性,不一样的属性叫作外部行为,相同的属性叫作内部行为integer的缓冲池

15.注解

(1).@SuppressWarning("deprecation")过期注解

(2).@Deprecated注解,表示该方法是否过期,架包升级时添加的注解

(3).注解至关于一种标记,经过反射了解你的类及各类元素上有无何种标记

(4).注解至关于一个类:@interface Annotation{};注解类,应用注解类的类,对应用了注解类的类进行反射操做的类

(5).AnnotationTest.class.getAnnotation(ItcastAnnotation.class)获得类AnnotationTest上的注解ItcastAnnotation,注解上使用注解叫作元注解,元数据,元信息

(6).@Retention(RetentionPolicy.RUNTIME)(保持到运行阶段),@Retention(RetentionPolicy.SOURCE)(保持在源文件阶 段),@Retention(RetentionPolicy.CLASS)(保持在class文件中):源代码->class文件->(类加载)内存中的文件(字节码)

(7).@Override注解保持到SOURCE,@SuppressWarning注解保持到SOURCE,@Deprecated注解保持到RUNTIME(只有将该类调到内存中才知道该类中的方法是否过期了)

(8).@Target({ElementType.METHOD,ElementType.TYPE})注解只能标记到方法上或类,接口等类型上 (9).注解的属性:String color();类有个color属性,还有一个特殊的属性value,属性的默认值default,数组的属性值,枚举的属性值,注解的属性值

次日:

1.dom4j解析XML文档

(1).Dom4j是一个简单、灵活的开放源代码的库,Dom4j是由早期开发JDOM的人分离出来然后独立开发的,与JDOM不一样的是,dom4j使用接口和抽象基类,虽然Dom4j的api相对要复杂一些,可是他提供了比JDOM更好的灵活性

(2).Dom4j是一个很是优秀的Java XML API,具备性能优异、功能强大和极易使用的特色,如今不少软件采用的Dom4j,例如hibernate,包括sun公司本身的JAXM也使用了Dom4j

(3).使用Dom4j开发,须要下载dom4j相应的jar文件

  1. XML语法

(1).编写XML文档时,须要先使用文档声明,声明XML文档的类型,使用IE校验XML文档的正确性.

(2).XML文档中的"中国"保存为本地的码表的"中国"

(3).在XML文档中,空格和换行都做为原始内容被处理

(4).XML区分大小写,一个标签能够有多个属性,每一个属性都有它本身的名称和取值,在XML技术中,

标签属性所表明的信息也能够被表示子标签表示

(5).XML文件中的注释采用:"

在Eclipse中jsp是用UTF-8编码存放的,当读取jsp的内容时,是用本地字符编码的,可能出现乱码,因此要设置读取文件的编码集.

(6).浏览器重定向的JSP不能放在web-inf目录中,而请求转发的JSP能够放在web-inf目录中

(7).struts2中的全局视图:

<resultname="index.jsp">

和struts1中的全局视图是很类似的

咱们能够定义一个包,而后将全局视图的配置放到这个包中

5.struts2经常使用标签

(1).property标签用于输出指定值:

<s:setname="name" value="kk"/>

<s:propertyvalue ="#name"/>

default:可选属性,若是须要输出的属性值为null,则显示该属性指定的值

escape:可选属性,指定是否格式化HTML代码

value:可选属性,指定须要输出的属性值,若是没有指定该属性,则默认输出ValueStack栈顶的值

id:可选属性,指定该元素的标识

(2).iterator标签用于对集合进行迭代,这里的集合包含List、Set和数组

red blue >


value:可选属性,指定被迭代的集合,若是没有设置该属性,则使用ValueStack栈顶的集合,

id:可选属性,指定集合里元素的id(已被标注为过期)

status:可选属性,该属性指定迭代时的iteratorStatus实例,该实例包含以下几个方法:

int getCount(),返回当前迭代了几个元素

int getIndex(),返回当前迭代元素的索引

boolean isEven(),返回当前被迭代元素的索引是否为偶数

boolean isOdd(),返回当前被迭代元素的索引是不是奇数

boolean isFirst(),返回当前被迭代元素是不是第一个元素

boolean isLast(),返回当前被迭代元素是不是最后一个元素

(3).<s:setname="age" value="21" scope="request"/>

<s:iftest="#request.age==23">

23

21

都不等

(4).url标签:

生成相似以下路径:

/struts/test/helloworld_add.action?persionid=3

当标签的属性值做为字符串类型处理时,"%"符号的用途是计算OGNL表达式的值

输出结果:

myurl

http://www.foshanshop.net

(5).表单标签checkboxlist复选框

若是集合为list

若是集合为MAP

生成以下html代码:

固然集合里面存放的也能够是对象类型

(6).单选框

6.struts2的处理流程与Action的管理方式

(1).用户请求->(查看web.xml文 件)StrutsPrepareAndExecuteFilter->Interceptor(struts2内置的一些拦截器或用户自定义拦截 器)->Action(用户编写的Action类,相似Struts1中的action,针对每一次请求,都建立一个 Action)->Result(相似struts1中的forward)->Jsp/html(响应)

(2).StrutsPrepareAndExecuteFilter是struts2框架的核心控制器,它负责拦截由 /"指定的全部用户请求,当用户请求到达时,该fileter会过滤用户的请求,默认情 况下,若是用户请求的路径不带后缀或者后缀以.action结尾,这时请求将被转入到struts2框架处理,不然struts2框架将略过该请求的处 理,当请求转入struts2框架处理时会先通过一系列的拦截器,而后再到Action,与struts1不一样,struts2对用户的每一次请求都会创 建一个Action,因此struts2中的action是线程安全的.

7.XML配置方式实现对action的全部方法进行校验

(1).基于XML配置方式实现对action的全部方法进行输入校验:

使用基于XML配置方式实现输入校验时,Action也须要继承ActionSupport,而且提供校验文件,校验文件和action类放在同一 个包下,文件的取名格式为:ActionClassName-validation.xml,其中,ActionClassName为action的简单 类名,-validation为固定写法,若是Action类为cn.itcast.UserAction,那么该文件的取名应 为:UserAction-validation.xml,下面是校验文件的模板:

<field-validatortype="requiredstring">

<paramname="trim">true

用户名不能为空

指定action中要校验的属性, 指定校验器,上面指定的校验器 requirestring是由系统提供的,系统提供了能知足大部分验证需求的校验器,这些校验器的定义能够在xwork-2.x.jar中的 com.opensymphony.xwork2.validator.validators下的default.xml中找 到, 为校验失败后的提示信息,若是须要国际化,能够为message指定key属性,key的值为资源文件中的key,在 这个校验文件中,对action中字符串类型的username属性进行验证,首先要求调用trim()方法去掉空格,而后判断用户名是否为空.

(2).struts2提供的校验器列表:

required(必填校验器,要求field的值不能为Null)

requiredstring(必填字符串校验器,要求field的值不能为null,而且长度大于0,默认状况下会对字符串取钱后空格)

stringlength(字符串长度校验器,要求field的值必须在指定的范围内,不然校验失败,minLength参数指定最小长度,maxLength参数指定最大长度,trim参数指定校验field以前是否去除字符串先后的空格)

regex(正则表达式校验器,检查被校验的field是否匹配一个正则表达式,expression参数指定正则表达式,caseSensitive参数指定进行正则表达式匹配时,是否区分大小写,默认值为true)

int(整数校验器,要求field的整数值必须在指定范围内,min指定最小值,max指定最大值)

double(双精度浮点数校验器,要求field的双精度浮点数必须在指定范围内,min指定最小值,max指定最大值)

fieldexpression(字段OGNL表达式校验器,要求field知足一个OGNL表达式,expression参数指定OGNL表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验经过,不然不经过)

email(邮件地址校验器,要求若是field的值非空,则必须是合法的邮件地址)

URL(网址校验器,要求若是field的值非空,则必须是合法的URL地址)

date(日期校验器,要求field的日期值必须在指定范围内,min指定最小值,max指定最大值)

conversion(转换校验器,指定在类型转换失败时,提示的错误信息)

visitor(用于校验action中的符合属性,它指定一个校验文件用于校验符合属性中的属性)

expression(OGNL表达式校验器,expression参数指定ognl表达式,该逻辑表达式基于ValueStack进行求值,返回true时校验经过,不然不经过,该校验器不可用在字段校验器风格的配置中)

(3).[CDATA[文本内容]]:文本内容不会被解析,只会原封不动的当作文本处理

(4).编写校验文件时,不能出现帮助信息:

在编写ActionClassName-validation.xml校验文件时,若是出现不了帮助信息,能够按照下面方式解决:

windows->preferences->myeclipse->filesand editors->xml->xmlcatalog:点击add,在出现的窗口中的location中选"file system"而后再xwork-2.1.2戒烟目录的src\java目录中选择xwork-validator-1.0.3.dtd,回到设置窗口的 时候,不要急着关闭窗口,应把窗口中的Key Type改成URI,Key改成http://www.opensymphoney.com/xwork/xwork-validaor-1.0.3.dtd

8.XML配置方式实现对action的指定方法校验

(1).基于XML配置方式对指定action方法实现输入校验:

当校验文件的取名为ActionClassName-validation.xml时,会对action中的全部处理方法实施输入校验,若是你只需 要对action中的某个action方法实施校验,那么校验文件的取名应为:ActionClassName-ActionName- validation.xml,其中ActionName为struts.xml中的action的名称,例如:在实际应用中,常有如下配置:

<resultname="success">/WEB-INF/page/message.jsp

<resultname="input">/WEB-INF/page/addUser.jsp

UserAction中有如下两个处理方法:

public String add() throws Exception{

}

public String update() throws Exception{

}

要对add()方法实施验证,校验文件的取名为:UserAction-user_add-validation.xml

要对update()方法实施验证,校验文件的取名为:UserAction-user_update-validation.xml

(2).基于XML校验的一些特色:

当为某个action提供了ActionClassName-validation.xml和ActionClassName-ActionName-validation.xml两种规则的校验文件时,系统按下面顺序寻找校验文件:

ActionClassName-validation.xml

ActionClassName-ActionName-validation.xml

系统寻找到第一个校验文件时还会继续搜索后面的校验文件,当搜索到全部校验文件时,会把校验文件里的全部校验规则汇总,而后所有应用于action方法的校验,若是两个校验文件中指定的校验规则冲突,则只使用后面文件中的规则。

当action继承了另外一个action,父类action的校验文件会先被搜索到

假设UserAction继承BaseAction

<actionname="user" class="cn.itcast.action.UserAction"method="{1}">

访问上面的action,系统先搜索父类的校验文件:BaseAction-validation.xml,BaseAction-user- validation.xml,接着搜索子类的校验文件:UserAction-validation.xml,UserAction-user- validation.xml,应用于上面action的校验规则为这四个文件的总和

9.动态方法调用和使用通配符定义action

(1).在struts1中实现方法的动态调用:

<actionpath="/control/employee/manage" type="....DispatchAction"parameter="method"/>

/control/employee/manage?method=addUI

可是Action必须继承DispatchAction

(2).在struts2中有两种方式:

第一种:(struts2.1版本后就不建议使用了)是动态方法调用:若是Action中存在多个方法时,咱们能够使用!+方法名调用指定方法,以下:

public class HelloWorldAction{

private String message;

....

public String execute()throws Exception{

this.message="个人第一个struts2应用";

}

public String other() throws Exception{

this.message="第二个方法";

return "success";

}

}

假设访问上面的action的URL路径为:/struts/test/helloworld.action,要访问action的other方 法,咱们就能够这样调用:/struts/test/helloworld!other.action,若是不想使用动态方法调用,咱们能够经过常量 struts.enable.DynamicMethodInvocation关闭动态方法调用:

<constantname="struts.enable.DynamicMethodInvocation"value="false"/>

第二种:使用通配符定义action(推荐使用的)

<action name="helloworld_*"class="cn.itcast.action.HelloWorldAction" method="{1}>

<resultname="success">/WEB-INF/page/hello.jsp

public class HelloWorldAction{

private String message;

....

public String execute()throws Exception{

this.message="个人第一个struts2应用";

}

public String other() throws Exception{

this.message="第二个方法";

return "success";

}

}

要访问other()方法,能够经过这样的URL访问:/test/helloworld_other.action

name="helloworld_"后可根据多个,method={1},'1'表示匹配*的位置

name="helloworld_*_*",method={2}:要访问other()方法,能够经过这样的URL访问:/test/helloworld_xxx_other.action

10.对action指定的方法进行校验

手工编写代码实现对action指定方法输入校验:

经过validateXxx()方法实现,validateXxx()只会校验action中方法名为Xxx的方法,其中Xxx的第一个字母要大 写,当某个数据校验失败时,咱们应该调用addFieldError()方法往系统的fieldErrors添加校验失败信息,(为了使用 addFieldError()方法,action能够继承ActionSupport(),若是系统的fieldErrors包含失败信 息,struts2会将请求转发到名为input的result,在input视图中能够经过 显示失败信息.

validateXxx()方法使用例子:

public Stringadd() throws Exception{return "success";}

public voidvalidateAdd() {

if(username==null&&"".equals(username.trim()))this.addFieldError("username","用户名不能为空");

}

验证失败后,请求转发至input视图:<resultname="input">/WEB-INF/page/addUser.jsp

在addUser.jsp页面中使用 显示失败信息

11.对Action中全部方法进行输入校验

(1).在struts2中,咱们能够实现对action的全部方法进行校验或者对action的指定方法进行校验

(2).对于输入校验struts2提供了两种实现方法:一种是采用手工编写代码实现,另外一种是基于XML配置方式实现

(3).手工编写代码实现对action中全部方法输入校验:经过重写validate()方法实现,validate()方法会校验action 中全部与execute方法签名相同的方法,当某个数据校验失败时,咱们应该调用addFieldError()方法往系统的fieldErrors添加 校验失败信息(为了使用addFieldError()方法,action能够继承ActionSupport(),若是系统的fieldErrors包 含失败信息,struts2会将请求转发到名为input的result,在input视图中能够经过 显示失 败信息.

validate()使用例子:

public voidvalidate(){

if(this.mobile==null||"".equals(this.mobile.trim())) {this.addFieldError("username","手机号不能为 空")}else{if(Pattern.compile("^1[358]\d{9}").matcher(this.mobile.trim()).matchers()) {this.addFieldError("mobile","手机号的格式不正确");}}

}

验证失败后,请求转发至input视图:

<resultname="input">/WEB-INF/page/addUser.jsp

在addUser.jsp页面中使用 显示失败信息

12.多文件上传

(1).多文件上传,就是在一个文件上传的基础上,将属性File变成数组类型File[]类型便可,同时该字段的名称必需要和上传页面中属性name的名称同样.

而后进行一次迭代,就能够获得全部的文件

13.防止表单重复提交

标签防止表单重复提交

第一步:在表单中加入

<s:textfieldname="person.name"/>

第二步:

<interceptor-refname="defaultStack"/>

<interceptor-refname="token"/>

<resultname="invalid.token">/WEB-INF/page/message.jsp

/WEB-INF/page/result.jsp

以上配置加入了"token"拦截器和"invalid.token"结果,由于"token"拦截器在会话的tlent与请求的token不一致时,将会直接返回"invalid.token"结果

在debug状态,控制台出现下面信息,是由于Action中并无struts.token和struts.token.name属性,咱们不用关心这个错误

使用了 标签能够不指定action的上下文标签路径,能够经过命名空间实现.和前面的原理是同样的,在路径后面添加上sessionid号,只是这步操做不须要咱们本身获得sessionid号,struts帮咱们操做.

在值栈中的对象,访问无需添加'#'

14.访问或添加几个属性

(1).访问或添加request/session/application属性,在struts2中的Action中的execute方法中没有Servlet api(没有响应的参数);

public String scope() throws Exception{

ActionContextctx=ActionContext.getContext();

ctx.getApplication().put("app","应用范围");

ctx.getSession().put("ses","session范围");

ctx.put("request","request范围");

return "scope";

}

${applicationScope.app} ${sessionScope.ses} ${requestScope.req}

(2).获取HttpServletRequest/HttpSession/ServletContext/HttpServletResponse对象:

方法一:经过ServletActionContext类直接获取

public String rsa() throws Exception{

HttpServletRequest request =ServletActionContext.getRequest();

ServletContextservletContext=ServletContext.getServletContext();

request.getSession();

HttpServletResponseresponse=ServletActionContext.getResponse();

return "scope";

}

方法二:实现指定接口,由struts框架运行时注入:

public class HelloWorldAction implementsServletRequestAware,ServletResponseAware,ServletContextAware{

private HttpServletRequest request;

private ServletContext servletContext;

private HttpServletResponse response;

public voidsetServletRequest(HttpServletRequest req){

this.request.req;

}

public voidsetServletResponse(HttpServletResponse res){

this.response=res;

}

public voidsetServletContext(ServletContext ser){

this.servletContext=ser;

}

注意1和2的不一样,一个不须要获得对象,一个须要获得对象,因此要区分两个的应用场景

}

15.解决struts配置文件无提示问题

找到struts2.0.dtd文件便可,windows->preferences->MyEclipse->XML->XMLCatalog,点击添加strut2.dtd

16.介绍struts2及struts2开发环境的搭建

(1).struts2是在webwork2基础发展而来的,和struts同样,struts2也属于MVC框架,不过有一点你们须要注意的是: 尽管struts2和struts1在名字上的差异不是很大,可是struts2和struts1在代码编写分割上几乎是不同的,那么既然有了 struts1,为什么还要推出struts2,主要是由于有一下有点:

第一:在软件设计上struts2没有像struts1那样跟Servlet api和struts api有着紧密的耦合,struts2的应用能够不依赖于servlet api 和struts api,struts2的这种设计属于无侵入式的设计,而struts1却属于侵入式设计,由于其的

execute()方法中的参数为ActionMapping,ActionForm,HttpServletRequest,HttpServletResponse

第二:struts2提供了拦截器,利用拦截器能够进行AOP编程,实现如权限拦截等功能

第三:struts2提供了类型转换器,咱们能够把特殊的请求参数转换成须要的类型,在struts1中,若是咱们要实现一样的功能,就必须向struts1的底层实现BeanUtils注册类型转换器才行

第四:struts2提供支持多种表现层技术,如:JSP,freeMarker,Velocity等

第五:struts2的输入校验能够对指定方法进行校验,解决了struts1长久之痛,struts1中的validate方法对全部的方法进行校验

第六:提供了全局范围、包范围、和Action范围的国际化资源文件管理实现.

(2).搭建struts2的环境和struts1是相同的,第一步导入相关包,第二步创建struts2的配置文件,第三步在web.xml中注册struts2框架的配置

(3).所需的包:struts2-core-2.x.x.jar,xwork-2.x.x.jar(webwork的核心架包),ognl-2.6.x.jar

(4).struts2默认的配置文件为struts.xml,该文件须要放在/web-inf/classes目录下

(5).在struts1中,struts框架是经过servlet启动的,在struts2中,struts框架是经过Filter启动的,它在 web.xml中的配置以下所示:能够参照struts文件夹下的例子中拷贝,在strutsperpareExecuteFilter的init()方 法中将会读取类路径下默认的配置文件struts.xml完成初始化操做,注意:struts2读取到struts.xml的内容后,以javabean 形式存放在内存中,之后struts2对用户的每次请求处理将使用内存中的数据,而不是每次都读取struts.xml文件

(6).自从struts2.1.3之后,下面的FilterDispatcher已经标注为过期了,struts2.1.3后期版本为StrutsPrepareAndExecuteFilter类

17.开发第一个应用

(1).在struts.xml中的配置:

<actionname="helloworld" class="cn.itcast.action.HelloWorldAction"method="execute">

<resultname="success">/WEB-INF/page/hello.jsp

在struts2框架中使用包来管理Action,包的做用和Java中的类包是很是相似的,它主要用于管理一组业务功能相关的action,在实际应用中,咱们应该吧一组业务功能相关的Action放在同一个包下

配置包时必须指定name属性,该name属性能够任意取名,但必须惟一,它不对应java的类包,若是其余包要继承该包,必须经过该属性进行引 用,包的namespace属性用于定义该报的命名空间,命名空间做为访问该包下Action的路径的一部分,如访问上面例子的Action,访问路径 为:/test/helloworld.action,namespace属性能够不配置,对本例而言,若是不指定该属性,默认的命名空间为" "(空字符串).固然配置能够减小重复的代码,struts1中的重复代码就能够使用命名空间来解决

一般每一个包都应该继承struts-default包,由于struts2不少核心的功能都是拦截器来实现的,如:从请求中把请求参数封装转到 action、文件上传和数据验证等都是经过拦截器实现的,struts-defaul定义了这些拦截器和Result类型,能够这么说:当包继承了 struts-default才能使用struts2提供的核心功能,struts-default包是在struts2-core-2.x.x.jar 文件中的struts-default.xml中定义,struts-default.xml也是struts2默认配置文件,struts2每次都会自 动加载struts-default.xml文件,包还能够经过abstract="true"定义为抽象包,抽象包中不能包含action,能够查看 struts-default.xml文件中,就能够看到定义了不少拦截器

和struts1中的forward很类似,定义视图

(2).public Stringexecute(){return 视图的名称;}注意到这个方法和struts1不一样,没有参数,返回类型也不一样,这就下降了耦合性,非侵入式的编程了.

(3).在jsp中使用el表达式便可${message},message是Action中的一个方法getMessage()方法,而不是根据Action中的成员变量message

18.配置Action范围国际化资源文件

(1).咱们也能够为某个action单独制定资源文件,方法以下:在Action类所在的路径,放置 ActionClassName_language_country.properties资源文件,ActionClassName为Action类的 简单名称当查找指定key的消息时,系统会先从ActionClassName_language_country.properties资源文件查找, 若是没有找到对应的key,而后沿着当前包往上查找基本名为package的资源文件,一直找到最顶层包,乳沟尚未找到对应的key,最后会从常量 struts.custom.i18n.resources指定的资源文件中查找

(2).JSP中直接访问某个资源文件

struts2为咱们提供了 标签,使用 标签咱们能够在类路径下直接从某个资源文件中获取国际化数据,而无需任何配置:

itcast为类路径下资源文件的基本名

若是要访问的资源文件在类路径的某个包下,能够这样访问:

<s:i18nname="cn/itcast/action/package">

小张

上面访问cn.itcast.action包下基本名为package的资源文件

19.配置包范围的国际化资源文件

(1).在一个大型应用中,整个应用有大量的内容须要实现国际化,若是咱们把国际化的内容都放置在全局资源属性文件中,显然会致使资源文件变得过于庞大、臃肿,不便于维护,这个时候咱们能够针对不一样模块,使用包范围来组织国际化文件

方法以下:在java的包下放置package_language_country.properties资源文件,package为固定写法,处 于该包及子包下的action均可以访问该资源,当查找指定key的消息时,系统会先从package资源文件中查找,当找不到对应的key时,才会从常 量struts.custom.i18n.resources指定的资源文件中寻找.

20.配置国际化全局资源文件、输出国际化信息

(1).准备资源文件,资源文件的命名格式以下:

baseName_language_country.properties

baseName_language.properties

baseName.properties

其中baseName是资源文件的基本名,咱们能够自定义,可是language和country必须是java支持的语言和国家。如:

中国大陆:baseName_zh_CN.properties

美国:baseName_en_US.properties

(2).如今为应用添加两个资源文件:

第一个存放中文:itcast_zh_CN.properties

内容为:welcom=欢迎来到传智播客

第二个存放英语(美国):itcast_en_US.properties

内容为:welcome=welcom to itcast

(3).对于中文的属性文件,咱们编写好后,应该使用JDK提供的native2ascii命令把文件转换为unicode编码的文件,命令的使用方式以下:

native2ascii 源文件.properties 目标文件.properties,在MyEclipse6.6版本以及后面的版本会自动转换.

(4).struts2有:全局范围,包范围,action范围的资源文件

(5).配置全局资源与输出国际化信息:

当准备号资源文件以后,咱们能够在struts.xml中经过:struts.custom.i18n.resources常量把资源文件定义为全局资源文件,以下:

<constantname="struts.custom.i18n.resources" vlaue="itcast"/>

itcast为资源文件的基本名

后面咱们就能够在页面或在action中访问国际化信息:

在JSP页面中使用 标签输出国际化信息:

<s:textname="user"/>,name为资源文件中的key

在Action类中,能够继承ActionSupport,使用getText()方法获得国际化信息,该该方法的第一个参数用于指定资源文件中的key,

在表单标签中,经过key属性指定资源文件中的key,如:

<s:textfieldname="realname" key="user"/>

21.请求参数接受

(1).struts1中是使用ActionForm接受用户的请求参数

(2).采用基本类型接受请求参数(get/post):

在Action类中定义与请求参数同名的属性,struts2便能自动接受请求参数并赋予给同名属性:请求路径:http://localhost:8080/test/view.action?id=78

public classProductAction{

private Integerid;

public voidsetId(Integer id){//struts2经过反射技术调用与请求参数同名的属性的setter方法获取请求参数值

this.id=id;

}

public IntegergetId(){return id;}

}

(3).采用复合类型接受请求参数

请求路径:http://localhost:8080/test/view.action?product_id=78

public class ProductAction{

private Product product;

public void setProduct(Product product){htis.product=product;}

public Product getProduct(){returnproduct;}

}

struts2首先经过反射技术调用Product的默认构造器建立product对象,而后再经过反射技术调用product中与请求参数同名的属性的setter方法来获取请求参数值

(4).关于struts2.1.6版本中存在一个Bug,及接受到的中文请求参数为乱码(以post方式提交),缘由是struts2.1.6在 获取并使用了请求参数后才调用HttpServletRequest的setCharacterEncoding()方法进行编码设置,致使应用使用的就 是乱码请求参数,这个Bug在struts2.1.8中已经解决,若是你使用的是struts2.1.6,要解决这个问题,你能够这样作:新建一个 Filter,把这个Filter放置在Struts2的Filter以前,而后再doFilter()方法中添加如下代码:

public void doFilter(..){

HttpServletRequest req=(HttpServletRequest)request;

req.setCharacterEncoding("UTF-8");

filterchain.doFilter(request,response);

}

22.全局类型转换器

自定义全局类型转换器:将上面的类型转换器注册为全局类型转换器:在WEB-INF/classes下放置xword-conversion.properties文件,在properties文件中的内容为:待转换的类型=类型转换器的全类名

对于本例而言,xwork-conversion.properties文件中的内容为:

java.util.Date=cn.itcast.conversion.DateConverter

23.输出带有占位符的国际化信息

(1).资源文件中的内容以下:

welcom={0}欢迎来到传智播客{1}

在jsp页面中输出带占位符的国际化信息

<s:propertyvalue="realname"/>

学习

在Action类中获取带占位符的国际化信息,能够使用getText(String key,String[] args)或getText(StringaTextName,List args)方法.

(2).占位符就当是一个变量参数,能够传递给定的参数值.

24.为Action属性注入值

struts2为Action中的属性提供了依赖注入功能,在struts2的配置文件中,咱们能够很方便的为Action中的属性注入值,注意:属性必须提供setter方法,

public class HelloWorldAction{

private String savePah;

public String getSavePath(){

            return savePath;

      }

public void setSavePath(String savePath){

      this.savePath=savePath;

      }

}

<paramname="savePath">/images

<resultname="success">/WEB-INF/page/hello.jsp

上面经过节点为action的savePath属性注入"/images";Action的变量的值,不能写死,常常变换,须要经过配置来设置参数

25.为应用指定多个配置文件

(1).在大部分应用中,随着应用规模的增长,系统中的Action的数量也会大量增长,致使struts.xml配置文件变得很是臃肿,为了不 struts.xml文件过于庞大、臃肿,提升struts.xml文件的可读性,咱们能够讲一个struts.xml配置文件分解成多个配置文件,而后 再struts.xml文件中包含其余配置文件,下面的struts.xml经过 元素指定多个配置文件:

<includefile="struts-user.xml"/>

<includefile="struts-order.xml"/>

经过这种方式,咱们就能够将struts2的Action按模块添加在多个配置文件中

26.文件上传

第一步:在WEB-INF/lib下加入commons-fileupload-1.2.1.jar、commons-io-1.3.2.jar, 这两个文件能够从http://commons.apache.org下载,在struts2.1之前的版本须要添加,之后的版本就不须要添加

第二步:把form表的enctype设置为:"multipart/form.data",以下:

这个属性的name必需要和类中File名称同样

第三步:在Action类中添加如下属性

public class HelloWorldAction{

private File uploadImage;//获得上传文件;

private String uploadImageContentType;//获得文件的类型

private String uploadImageFileName;//获得文件的名称

//这里省略了属性的get/set方法(可是要注意get/set方法是必须的)

public String upload() throws Exception{

String realpath =ServletActionContext.getServletContext().getRealPath("/images");

File file=new File(realpath);

if(file.getParentFile().exists())file.getParentFile().mkdirs();//目录是否存在,不存在就建立

FileUtils.copyFile(uploadImage,newFile(file,uploadImageFileName));

return "success";

}

}

(1).若是文件不保存,struts2会把文件保存到本身的目录中,可是当这个Action执行完后,该文件就会被删除,因此咱们要将上传的文件保存到硬盘上

(2).最好还要判断如下,文件uploadImage是否为空

(3).若是上传大的文件,web都会失败,像一些门户网站上传视频,都是经过一个插件,能够把这个插件当作一个程序,只是这个程序是经过Socket变成的,针对一个端口进行传输数据

27.指定struts2处理的请求后缀

(1).前面咱们都是默认使用.action后缀访问Action,其实默认后缀是能够经过常量"struts.action.extension"进行修改的,例如:咱们能够配置struts2只处理以.do为后缀的请求路径

<constantname="struts.action.extendsion" value="do"/>

若是用户须要制定多个请求后缀,则多个后缀之间以英文逗号","隔开,如:

<constantname="struts.action.extendsion" value="do,go"/>

(2).常量能够在struts.xml或struts.properties中配置,建议在struts.xml中配置,两种配置方式以下:

在struts.xml文件中配置常量:

<constantname="struts.action.extendsion" value="do"/>

在struts.properties中配置常量:

struts.action.extension=do

由于常量能够在下面多个配置文件中进行定义,因此咱们须要了解struts2加载常量的搜索顺序:

struts-default.xml

struts-plugin.xml

struts.xml

struts.properties

web.xml

若是在多个文件中配置了同一个常量,则后一个文件中配置的常量值会覆盖前面文件中配置的常量值

(3).

第一:默认编码集,做用于HttpServletRequest的setCharacterEncoding方法和freemarker、velocity的输出:

<constantname="struts.i18n.encoding" value="UTF-8"/>

第二:该属性指定须要struts2处理的请求后缀,该属性的默认值是action,即全部匹配*.action的请求都由struts2处理,若是用户须要指定多个请求后缀,则多个后缀之间以英文逗号(,)隔开

第三:设置浏览器是否缓存静态内容默认值为true(生产环境下使用)开发阶段最好关闭,否则看不到修改后的数据

<constantname="struts.serve.static.browserCache" value="false"/>

第四:当struts的配置文件修改后系统是否自动从新加载该文件默认值为false(生产环境下使用),开发阶段最好打开

<constantname="struts.configuration.xml.reload" value="true"/>

第五:开发模式下使用,这样能够打印出更详细的错误信息

第六:默认的视图主题

<constantname="struts.ui.theme" value="simple"/>

第七:与spring集成时,指定由spring负责action对象的建立

<constantname="struts.objectFactory" value="spring"/>

第七:该属性设置struts2是否支持动态方法调用,该属性的默认值是true,若是须要关闭动态方法调用,则可设置该属性为false

<constantname="struts.enable.DynamicMethodInvocation"value="false"/>

第八:上传全部文件的总大小限制

constantname="struts.mulitipart.maxSize" value="10701096"/>

28.自定义拦截器

(1).若是用户登陆后能够访问action中的全部方法,若是用户没有登陆不容许访问action中的方法,而且提示"你没有权限执行该操做"

(2).

<interceptorname="permission"class="cn.itcast.interceptor.PermissionInterceptor"/>

<actionname="list_*" class="cn.itcast.action.HelloWorldAction"method="{1}">

<interceptor-refname="permission"/>

若是为某一个Action定义一个拦截器,struts2中对Action的默认的不少拦截器都失去功能,因此要想作到一箭双鵰,须要定义一个拦截器栈:

<interceptorname="permission"class="cn.itcast.interceptor.PermissionInterceptor"/>

<interceptor-stackname="permissionStack">

<interceptor-refname="defaultStack"/>

<interceptor-refname="permission"/>

由于struts2中如文件上传,数据验证,封装请求参数到action等功能都是由系统默认的defaultStack中的拦截器实现的,因此我 们定义的拦截器须要引用系统默认的defaultStack,这样应用才能够使用struts2框架提供的众多功能,若是但愿包下的全部action都使 用自定义的拦截器,能够经过<default-interceptor-refname="permissionStack"/>把拦截器定 义为默认拦截器,注意:每一个包只能指定一个默认拦截器,另外,一旦咱们为该包中的某个action显示指定了某个拦截器,则默认拦截器不会起做用.

(3).系统默认的拦截器能够到struts-default.xml中查看,不少功能.系统拦截器放在最前面,自定义的拦截器放在后面.

29.自定义类型转换器

(1).struts2中提供了两种类型转换器:局部类型转换器(只对某一个action起做用),全局类型转换器(全部的action起做用)

(2).类型转换器必须继承DefaultTypeConverter最好用xwork2.jar中的,重写converValue(Map<String,Object>context,Objectvalue,Class toType){

returnsuper.convertValue(context,value,toType);

}

其中第一个参数和ognl表达式,第二个参数是须要转换类型的内容(是String数组,由于可能有多个值),第三个参数是须要转换成什么类型,要实现双向转换

(3).将上面的类型转换器注册为局部类型转换器:

在Action类所在的包下放置ActionClassName-conversion.properties文 件,ActionClassName是Action的类名,后面的-conversion.properties是固定写法,对于本例而言,文件的名称应 为HelloWorldAction-conversion.properties.在properties文件中的内容为:

须要转换的属性名称=类型转换器的全类名

对于本例而言,HelloWorldAction-conversion.properties文件中的内容为:

createtime=cn.itcast.conversion.DateConverter

4、 Spring

  1. @Autowired注解与自动装配

@Autowired

private PersonDaopersonDao;

拿PersonDao与<bean id=""..../>中的id值进行比较,相同就找到了,即进行类型注解,固然也能够过@Qualifier注解进行名称进行注解.

自动装配:

对于自动装配,你们了解一下就能够了,实在不推荐你们使用,例子:

autowire属性取值以下:

byType:按类型装配,能够根据属性的类型,在容器中寻找根该类型匹配的bean,若是发现多个,那么将会抛出异常,若是没有找到,即属性值为null

byName:按名称装配,能够根据属性的名称,在容器中寻找跟该属性名相同的bean,若是没有找到,即属性值为null

construtor与byType的方式相似,不一样之处在于它应用于构造器参数,若是在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常.

autodetect:经过bean类的自省机制,来决定是使用constructor仍是byType方式进行自动装配,若是发现默认的构造器,那么将使用byType方式.

  1. @Resource注解完成属性装配

(1).前面讲到了使用构造器注入,属性的setter方法注入,这里还能够使用注解的方式对Field进行注入

(2).注入依赖对象能够采用手工装配或自动装配,在实际应用中建议使用手工装配,由于自动转配会产生未知状况,开发人员没法预见最终的装配结果

(3).手工装配依赖对象,在这种方式中又有两种编程方式

方式一:在XML配置文件中,经过在Bean节点下配置,如:

构造器注入

属性的setter方法注入

在XML中注入属性,会给XML文件变得很臃肿.特别是对集合类型进行注入时,变得很臃肿.

方式二:

在java代码中使用@Autowire或@Resoruce注解方式进行装配,但咱们须要在XML配置文件中配置如下信息:

<beansxmlns="http://www.springframe.....

....

....

这些配置项隐式注册了多个对注释进行解析处理的处理 器:AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor,RequireAnnotationBeanPostProcessor, 每一个注解都有一个注解处理器,注解自己不干活,是相对应的注解处理器在干活

注:@Resource注解在spring安装目录的lib\j2ee\common-annotations.jar

(4).在java代码中使用@Autowired或@Resource注解方式进行装配,这两个注解的区别是:

@Autowired默认按类型装配,@Resource默认按名称装配,当找不到与名称匹配的bean才会按类型装配.

@Autowired

private PersonDaopersonDao;//用于字段上

@Autowired

public voidsetOrderDao(OrderDao orderDao){//用于setter方法上

this.orderDao=orerDao;

}

@Autowired注解是按类型装配依赖对象,默认状况下,它要求依赖对象必须存在,若是容许null值,能够设置它required的属性为false,若是咱们想使用按名称装配,能够结合@Qualifier注解一块儿使用,以下:

@Autowired@Qualifier("personDaoBean")

private PersonDaopersonDao;

@Resource注解和@Autowired同样,也能够标注在字段或属性的setter方法上,但他默认按名称装配,名称能够经过 @Resource的name属性执行,若是没有指定name属性,当注解标注在字段上,即默认取字段的名称做为bean名称寻找依赖对象,当注解标注在 属性的setter方法上,即默认取属性名做为bean名称寻找依赖对象

@Resource(name="personDaoBean")

private PersonDaopersonDao;//用于字段上

注意:若是没有指定name属性,而且按照默认的名称仍然找不到依赖对象时,@Resoruce注解会回退到按类型装配,但一旦指定了name属 性,就只能按名称装配了.拿personDao与<bean id=" ".../>中的id是否相同,相同就找到,属性的setter方法也不用写,既方便,又优雅.

同时@Resource是j2ee提供的注解(建议使用),@Autowired是spring框架提供的.

  1. Spring的三种实例化Bean的方式

(1).三种实例化bean的方式:

第一种:<bean id="orderService"class="cn.itcast.OrderServiceBean/>

第二种:使用静态工厂方法实例化:

public class OrderFactory{

public static OrderServiceBeancreateOrder(){

return new OrderServiceBean();

}

}

第三种:使用实例工厂方法实例化:

<beanid="personServiceFactory" class="cn.itcast.service.OrderFactory"/>

public class OrderFactory{

public OrderServiceBean createOrder(){

return new OrderServiceBean();

}

}

  1. Spring管理的Bean的生命周期

(1).Bean实例化是在Spring容器实例化时进行的,可是这是Singleton做用域中,实例化的时机是能够更改的,lazy- init="true"延迟初始化,即更改成调用getBean()方法时进行初始化.同时也能够在配置文件中设置全部的bean延迟初始化,其实这个标 签是不建议使用的.

(2).当把做用域改为Prototype时,Bean实例化是在调用getBean()方法进行的

(3).能够指定一个初始化方法:init-method="";即在bean实例化后执行的初始化方法.如数据库的链接.容器经过反射技术调用的,同时还须要进行资源的释放.destory-method="";即在bean被销毁时执行的方法.

(4).关闭Spring容器:ctx.close()方法,bean此时被销毁了

5.Spring自动扫描和管理bean

经过在classpath自动扫描方式把组件归入spring容器中管理,前面的例子咱们都是使用XML的bean定义来配置组件,在一个稍大的项 目中,一般会有上百个组件,若是这些组件采用xml的bean定义来配置,显然会增长配置文件的体积,查找及维护起来也不太方便,spring2.5为我 们引入了组件自动扫描机制,它能够在类路径底下寻找标注了@Componet、@Service、@Controller、@Reponsitory注解 的类,并把这些类归入进spring容器中管理,它的做用和在XML文件中使用bean节点配置组件是同样的,要使用自动扫描机制,咱们须要打开如下配置 信息:

<beansxmln="http://www.springframework.org/schema/beans"

....

....

其中base-package为须要扫描的包(含子包)

@Service用于标注业务层组件、@Controller用于标注控制层组件(如struts中的action)、@Repository用于 标注数据访问组件,即Dao组件,而@Component泛指组件,当组件很差归类的时候,咱们能够使用这个注解进行标注.同时也能够经过注解 @Scope("prototype")修改bean的做用域.@Service("personService")中的名称必须和bean的名称相同, 只是开头字母变成小写了.固然这是默认设置,能够修改的.

能够使用注解的方式指定初始化方法,在初始化方法init()上添加注解@PostConstruct,一样能够指定销毁方法destroy(),注解为:@PreDestroy

这种扫描方式是很方便的,不少人都采用

即前面所说的功能都使用注解进行操做

6. SSH整合开发

hibernate核心安装包下的:

hibernate3.jar

lib\required*.jar

lib\optional\ehcache-1.2.3.jar

hibernate注解安装包下的

lib\test\slf4j-log4j12.jar

Spring安装包下的

dist\spring.jar

dist\modules\spring-webmvc-struts.jar

lib\jakarta-commons\commons-logging.jar、commons-dbcp.jar、commons-pool.jar

lib\aspectj\aspectjweaver.jar、aspectjrt.jar

lib\cglib\cglib-nodep-2.1.3.jar

lib\j2ee\common-annotations.jar

lib\log4j-1.2.14.jar

Struts:下载struts-1.3.8-lib.zip,须要使用到解压目录下的全部jar,建议把jstl-1.0.2.jar和 standard-1.0.2.jar更换为1.1版本,Spring中已经存在一个antlr-2.7.6.jar,因此把struts中的 antlr-2.7.2.jar删除,避免jar冲突

数据库驱动jar

首先整合struts和hibernate而后再整合spring

7.编码解析@Resource注解的实现原理

(1).新建一个注解

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.Field,ElementType.METHOD})

public @interface ItcastResource{

String name() default "";

}

固然还要编写一个注解处理器.

private voidannotationInject(){

首先循环全部的bean对象,判断其是否使用了注解

若是使用了注解,就进行属性注入.

}

8.编码解析Spring依赖注入的原理

(1).基本类型对象注入:

构造器注入

//属性setter方法注入

(2).注入其余bean:

方式一:

ref是指被注入的对象personDao

方式二:(使用内部bean,但该bean不能被其余bean使用)

<beanclass="cn.itcast.service.OrderDaoBean"/>

(3).依赖注入的内部原理

9.编码解析Spring装配基本属性原理

也能够对基本类型进行注入,类型转换.

10.编码剖析Spring管理bean的原理

使用dom4j读取spring配置文件,使用反射技术便可

11.搭建和配置Spring与JDBC整合的环境

(1).配置数据源:

apache的数据源:BasicDataSource

<propertyname="driverClassName" value="org.gjt.mm.mysql.Driver"/>

链接池启动时的初始值

链接池的最大值

最大空闲值

最小空闲值

配置事务:

采用注解的方式:

<ts:annotation-driventransaction-manager="txManage"/>

12.搭建与测试spring的开发环境

(1).dist\spring.jar;

lib\jakarta-commons\commons-logging.jar

以上这两个包是必须的

lib\aspectj\aspectjweaver.jar和aspectjrt.jar

lib\cglib\cglib-nodep-2.1.3.jar

以上这两个包是用于切面编程(AOP)

lib\j2ee\common-annotations.jar

以上的这个包是JSR-250中的注解

(2).Spring项目既能够在j2se中也能够在j2ee中

(3).spring的配置文件模板能够从spring的参考手册或spring的例子中获得,配置文件的取名能够任意,文件能够存放在任何目录下,但考虑到通用性,通常放在类路径下

(4).实例化spring容器经常使用方式:

第一种方式:在类路径下寻找配置文件来实例化容器

ApplicationContextctx=new ClassPathXmlApplicationContext(new String[]{"beans.xml"});

第二种方式:在文件系统路径下寻找配置文件来实例化容器

ApplicationContextctx=new FileSystemXmlApplicationContext(newString[]{"d:\beans.xml"});//将路径写死了,通用性很差,不建议使用.

spring的配置文件能够指定多个,能够经过String数组传入

(5).IOC:控制反转:

public class PersonServiceBean{

private PersonDao personDao = newPersonDaoBean();

public void save(Person person){

personDao.save(person);

}

}

PersonDaoBean是在应用内部建立及维护的,所谓控制反转就是应用自己不负责依赖对象的建立及维护,依赖对象的建立及维护是由外部容器负责的,这样控制权就由应用转移到外部容器,控制权的转移就是所谓反转.

(6).创建一个业务bean为PersonServiceBean,放在cn.itcast.service.impl包中.面向接口编程,进行 解耦,怎么将业务bean交给spring管理,须要在beans.xml配置文件中: ,其中id,name都是bean的名称,可是id不能使用特殊字符,id自己就属于 XML属性的,如:"/sfs/"就会出错,可是name不会出错,class属性是bean类的路径,须要操纵bean的时候,只需到spring容器 中取出bean进行操做,而不须要实例化一个bean了:

ApplicationContext ctx=newClassPathXmlApplicationContext(new String[]{"beans.xml"});

PersonServiceBeanpersonService=(PersonServiceBean)ctx.getBean("personService");

personService.save();

(7).当在配置文件中没有标签的提示信息,须要手动添加schema文件,方法如 下:windows->preferences->myeclipse->filesand editors->xml->xmlcatalog,点击添加,在出现的窗口中的Key Type中选择URL,在location中选"File System",而后在spring解压目录的dist/resources目录中选择spring-beans-2.5.xsd,回到设置窗口的时候不 要急着关闭窗口,应该把窗口的Key Type改成Schema location,Key改成http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

13.配置Spring管理的bean的做用域

(1).Singleton:默认状况下,bean是单例模式的,在每一个springIoc容器中一个bean定义只有一个对象实例,默认状况下会 在容器启动时初始化bean,可是咱们能够指定Bean节点的lazy-init="true"来延迟初始化bean,这时候,只有第一次获取bean会 才初始化bean,如:

若是想对全部bean都应用延迟初始化,能够在根节点beans设置default-lazy-init="true",以下:

Prototype:每次从容器获取bean都是新的对象

Request:在request的域中

Session:在Session的域中

Global Session:在全局的Session的域中

14.全面阐释Spring及其各项功能

(1).Spring是一个开源的控制反转(Inversion of control,IoC)和面向切面(AOP)的容器框架,它的主要目的是简化企业开发

(2).IOC:控制反转:

public class PersonServiceBean{

private PersonDao personDao = newPersonDaoBean();

public void save(Person person){

personDao.save(person);

}

}

PersonDaoBean是在应用内部建立及维护的,所谓控制反转就是应用自己不负责依赖对象的建立及维护,依赖对象的建立及维护是由外部容器负 责的,这样控制权就由应用转移到外部容器,控制权的转移就是所谓反转.PersonServiceBean是业务逻辑层类,PersonDaoBean是 Dao层类,在业务逻辑层类中控制和建立PersonDaoBean,这就是应用自己建立和维护了,可是有了spring容器 后,PersonDaoBean的建立和维护是在容器中进行的,不须要PersonServiceBean进行管理了,控制权进行的反转

(3).依赖注入:当咱们把依赖对象交给外部容器负责建立,那么PersonServiceBean类能够改为以下:

public class PersonServiceBean{

private PersonDao personDao;

//经过构造器参数,让容器把建立好的依赖对象注入进PersonServiceBean,固然也能够使用setter方法进行注入

public PersonServiceBean(PersonDaopersonDao){

this.personDao=personDao;

}

public void save(Person person){

personDao.save(person);

}

}

所谓依赖注入就是指:在运行期,由外部容器动态的将依赖对象注入到组件中.

(4).为什么要使用spring:

第一:下降组件之间的耦合度,实现软件各层之间的解耦:

Controller->Service->Dao

第二:能够使用容器提供的众多服务,如:事务管理服务,消息服务等,当咱们使用容器管理事务时,开发人员就再也不须要手工控制事务,也不须要处理复杂的事务传播

第三:容器提供单例模式支持,开发人员不在须要本身编写实现代码

第四:容器提供了AOP技术,利用它很容易实现如权限拦截、运行期监控等功能

第五:容器提供的众多辅助类,使用这些类可以加快应用的开发,如:JdbcTemplate,HibernateTemplate

第六:Spring对于主流的应用框架提供了集成技术,如集成Hibernate、JPA、Struts等,这样更便于应用的开发.

(5).轻量级和重量级概念的划分,其实划分一个应用是否属于轻量级仍是重量级,主要看他使用了多少服务,使用的服务越多,容器要为普通的java 对象的工做就越多,必然会影响到应用的发布时间或者是运行性能,对于spring容器,它提供了不少服务,可是这些服务并非默认为应用打开的,应用须要 某种服务,还须要指明使用该服务,若是应用使用的服务不多,如:只是用了spring的核心服务,那么咱们就能够认为此时应用属于轻量级的,若是应用使用 了spring提供的大部分服务,这时应用就属于重量级,目前EJB容器就由于他默认为应用提供了EJB规范中全部的功能,因此他属于重量级

15.使用CGLIB实现AOP功能与AOP概念详解

(1).使用cglib架包,构建代理,不须要被代理的对象须要实现接口

public class CGlibProxyFactory implementsMethodInterceptor{

private Object targetObject;

public Object createProxyIntance(ObjecttargetObject){

this.targetObject=targetObject;

Enhancer enhancer = new Enhancer();

enhancer.setSuperclass(this.targetObject.getClass());

enhancer.setCallback(this);

return enhancer.create();

}

public Object intercep(Object proxy,Method method,Object[]args,MethodProxy methodProxy)throws Throwable{

returnmethodProxy.invoke(this.targetObject,args);

}

}

CGLIB能够生成目标类的子类,并重写父类非final修饰符的方法

16.使用JDK中的Proxy技术实现AOP功能

(1).使用在权限拦截上.

(2).被代理对象必须实现接口

public class JDKProxyFactory implementsInvocationHandler{

private Object targetObject;

public Object createProxyIntance(ObjecttargetObject){

returnProxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),this.targetObject.getClass().getInterfaces(),this);

}

public Object invoke(Object proxy,Methodmethod,Object[]args) throws Throwable{//环绕通知

PersonServiceBean bean=(PersonServiceBean)this.targetObject;

Object result=null;

if(bean.getUser()!=null){

//....advice(),调用方法前处理,叫作前置通知

try{

result=method.invoke(targetObject,args);

//....afteradvice();调用方法后处理,叫作后置通知

}catch(RuntimeException e){

//....exceptionadvice();调用方法出现例外后处理,叫作例外通知

}finally{

//....finallyadvice();调用方法最终都会处理,叫作最终通知

}

}

return result;

}

}

整个invoke方法叫作环绕通知.

(3).AOP中的概念:

Aspect(切面):指横切性关注点的抽象即为切面,它与类类似,只是二者的关注点不同,类是对物体特征的抽象,而切面是横切性关注点的抽象。

JoinPoint(链接点):所谓链接点是指那些被拦截到的点,在spring中,这些点指的是方法,由于spring只支持方法类型的链接点,实际上joinpoint还能够是field或类构造器

Pointcut:切入点:所谓切入点是指咱们要对那些joinpoint进行拦截的定义

Advice(通知):所谓通知是指拦截到joinpoint以后所要作的事情就是通知,通知分为前置通知,后置通知,异常通知,最终通知,环绕通知

Target(目标对象):代理的目标对象

Weave(织入):指将aspect应用到target对象并致使proxy对象建立的过程称之为织入

Introduction(引入):在不修改类代码的前提下,Introduction能够在运行期为类动态的添加一些方法或Field

17.使用Spring的注解方式实现AOP

(1).要进行AOP编程,首先咱们要在Spring的配置文件中引入AOP命名空间:

<beansxmlns="http://www.springframework.org/schema/beans"

Spring提供了两种切面使用方式,实际工做中咱们能够选用其中一种:一种是基于XML配置方式进行AOP开发,另一种是基于注解方式进行AOP开发

(2).基于注解方式声明切面:首先启动对@AspectJ注解的支持:

定义一个切面类(MyInterceptor):

@Aspect

public class MyInterceptor{

@Pointcut("execution(cn.itcast.service...*(..))

//定义切入点,通配符:指的是任何返回类型,..是指子包下也进行拦截,:指的是拦截全部类,*:指定的是全部方法,..是指任意参数

private void anyMethod(){}//声明一个切入点

@Before("anyMethod()")//定义一个前置通知,名称为切入点名称

public void doAccessCheck(){

System.out.println("前置通知");

}

@AfterReturning("anyMethod()")//后置通知

public void doAfterReturning(){

System.out.println("后置通知");

}

@After("anyMethod()")//最终通知

public void doAfter(){

System.out.println("最终通知");

}

@AfterThrowing("anyMethod()");//例外通知

public void doAfterThrowing(){

System.out.println("例外通知");

}

@Around("anyMethod()")//环绕通知

public ObjectdoBasicProfiling(ProceedingJoinPoint pjp)throws Throwable{

System.out.println("进入方法");

if(){//判断用户是否有权限,有权限就执行该方法.

Object result = pjp.proceed();

System.out.println("退出方法");

}else{

}

return result;

}

}

因此当出现例外通知时,后置通知是不执行的,即它们二者确定有一个不执行,一个执行.

须要将切面类交给spring管理:基于XML配置管理或者自动扫描管理

(3).想获得参数:

@Before(anyMethod() &&args(userName)")//添加参数,只会拦截到对应的方法,即一个参数的方法.

public void doAccessCheck(String name){

System.out.println("前置通知");

}

(4).想获得返回结果:

@AfterReturning("anyMethod()",returning="result")//后置通知

public void doAfterReturning(String result){

System.out.println("后置通知");

System.out.println(result);//打印返回结果

}

(5).获得异常(例外):

@AfterThrowing("anyMethod()",throwing="e");//例外通知

public void doAfterThrowing(Exception e){

System.out.println("例外通知");

System.out.println(e);//打印例外

}

18.使用Spring配置文件实现AOP

(1).使用配置文件实现AOP,切面类只是个普通的类,其内部没有任何注解

public class MyInteceptor{

public void doAccessCheck(){

System.out.println("前置通知");

}

public void doAfterReturning(){

System.out.println("后置通知");

}

public void doAfter(){

System.out.println("最终通知");

}

public void doAfterThrowing(){

System.out.println("例外通知");

}

public ObjectdoBasicProfiling(ProceedingJoinPoint pjp)throws Throwable{

System.out.println("进入方法");

Object result=pjp.proceed();

System.out.println("退出方法");

return result;

}

}

(2).基于XML配置方式声明切面

<aop:beforepointcut-ref="mycut" method="doAccessCheck"/>

<aop:after-returningpointcut-ref="mycut" method="doReturnCheck"/>

<aop:after-throwingpointcut-ref="mycut" method="doExceptionAction"/>

<aop:afterpointcut-ref="mycut" method="doReleaseAction"/>

<aop:aroundpointcut-ref="mycut" method="doBasicProfiling"/>

(3).对于表达式expression的细节:

返回值类型为String:execution(java.lang.Stringcn.itcast.service...(..))

第一个参数为String:execution(java.lang.Stringcn.itcast.service...(java.lang.String..))

返回值类型为非void:execution(!void cn.itcast.service...(..))

19.使用Spring配置文件实现事务管理

(1).

<aop:pointcutid="transactionPointcut" expression="execution(cn.itcast.service..*(..))"/>对指定的方法进行拦截

<aop:advisoradvice-ref="txAdvice"pointcut-ref="transactionPointcut"/>

<tx:adviceid="txAdvice" transaction-manager="txManager">

<tx:methodname="*"/>

20.使用Spring注解方式管理事务与传播行为详解

(1).只有当遇到RuntimeException时,事务进行回滚.Spring开启的事务管理,当遇到运行期例外(unchecked),而(checked异常)是不进行事务的回滚的.

(2).固然也可修改这种状况.把unckecked异常改为不会进行回滚了:@Transactional(noRollbackFor=RuntimeException.class),

(3).@Transactional(propagation=Propagation.NOT_SUPPORTED);关闭事务,不开启事务的.spring容器默认是打开事务的.固然还有其余一些值:(事务的传播行为)

REQUIRED:业务方法须要在一个事务中运行,若是方法运行时,已经处在一个事务中,那么加入到该事务,不然为本身建立一个新的事务(容器的默认值)

NOT_SUPPORTED:声明方法不须要事务,若是方法没有关联到一个事务,容器不会为他开启事务,若是方法再一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行

REQUIRESNEW:属性声明无论是否存在事务,业务方法总会为本身发起一个新的事务,若是方法已经运行在一个事务中,则原有事务会被挂起,新的事务会被建立,直到方法执行结束,新事务才算结束,原先的事务才会恢复执行

MANDATORY:该属性指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起本身的事务,若是业务方法再没有事务的环境下调用,容器就会抛出异常

SUPPORTS:这一事务属性声明,若是业务方法再某个事务范围内被调用,则方法成为该事务的一部分,若是业务方法再事务范围外被调用,则方法再没有事务的环境下执行

Never:指定业务方法绝对不能在事务范围内执行,若是业务方法再某个事务中执行,容器会抛出异常,只有业务方法没有关联到任何事务,才能正常执行.

NESTED:若是一个活动的事务存在,则运行在一个嵌套的事务中,若是没有活动事务,则按REQUIRED属性执行,它使用了一个单独的事务,这 个事务拥有多个能够回滚的保存点,内部事务的回滚不会对外部事务形成影响,它只对DataSourceTransactionManager事务管理器起 效.Savepoint savepoint=conn.setSavepoint();conn.rollback(savepoint);

(4).readOnly值为事务不能修改了.timeout是事务的超时时间,isolation数据库中的隔离级别.

(5).数据库系统提供了四种事务隔离级别供用户选择,不一样的隔离级别采用不一样的锁类型来实现,在四种隔离级别中,Serializable的隔离 级别最高,Read Uncommited的隔离级别最低,大多数据库默认的隔离级别为Read Commited,如SQLServer,固然也有少部分数据库默认的隔离级别为Repeatable Read,如MySql

Read Uncommited:读未提交数据(会出现脏读,不可重复读和幻读)

Read Commited:读已提交数据(会出现不可重复读和幻读)

Repeatable Read:可重复读(会出现幻读)

Serializable:串行化

脏读:一个事务读取到另外一个事务未提交的更新数据

不可重复读:在同一事务中,屡次读取同一数据返回的结果有所不一样,换句话说就是,后续读取能够读到另外一事务已经提交的更新数据,相反,“可重复读”在同一事务中屡次读取数据时,可以保证所读数据同样,也就是,后续读取不能读到另外一事务已提交的更新数据.

幻读:一个事务读取到另外一个事务已提交的insert数据.

5、 Hibernate

1. Criteria查询方式

(1).Criteria查询方式(条件查询):

Criteriac=s.createCriteria(User.class);

c.add(Restrictions.eq("name",name));//添加查询条件,User中的name属性的值是否等于"name"

List list=c.list();

Useru=(User)c.uniqueResult();

2. hibernate的二级缓存配置与分析

(1).二级缓存:SessionFactory级共享:

实现为可插拔,经过修改cache.provider_class参数来改变;hibernate内置了对 EhCache.OSCache,TreeCaceh,SwarmCache的支持,能够经过实现CacheProvider和Cache接口来加入 Hibernate不支持的缓存实现

在hibernate.cfg.xml中加入:

<class-cacheclass="className" usage="read-only"/>或在映射文件的class元素加入子元素:

其中usage:read-only,read-write,nonstrict-read-write,transactional

Session的save(这个方法不适合native生成方式的主 键),update.saveOrUpdate,list,iterator,get,load,以及Query,Criteria都会填充二级缓存,但 只有(没有打开查询缓存时)Session的iterator,get,load会从二级缓存中取数据(iterator可能存在N+1次查询)

Query,Criteria(查询缓存)因为命中率较低,因此hibernate缺省是关闭;修改cache,use_query_cache为 true打开对查询的缓存,而且调用query.setCaheable(true)或criteria.setCacheable(true)

SessionFactory中提供了evictXXX()方法用来清除缓存中的内容

统计消息打开generate_statistics,用sessionFactory.getSatistics()获取统计信息,获取统计信息成本是很高的,消耗资源.对程序的调试是颇有帮助的,能够看到session的初始化时间,打开多少次,关闭多少次等信息.

(2).相对user对象进行缓存:

<class-cacheclass="cn.itcast.hibernate.domain.User"usage="read-only"/>只读方式,效率高,User类不会再改变了.可以保证并发.

(3).先到一级缓存中查找,找不到在到二级缓存中查找

3.Hibernate的拦截器和监听器

(1).拦截器和事件

拦截器与事件都是hibernate的扩展机制,Interceptor接口是老的实现机制,如今改为事件监听机制,他们都是hibernate的回调接口,hibernate在save,delete,update等会回调这些查询

(2).拦截保存的的事件:

实现SaveOrUpdateEventListener接口

public classSaveListener implements SaveOrUpdateEventListener{

public voidonSaveOrUpdate(SaveOrUpdateEvent event){

if(event.getObject()instantce of cn.itcast.hibernate.domain.User){

User user =(User)event.getObject();

System.out.println(user.getName().getFirstName());

}

}

}

配置文件中:

<eventtype="save">

<listenerclass="cn.itcast.hibernate.SaveListener"/>本身定义的监听器,不一样监听器的注册顺序,输出的结果也是不一样的.

<listenerclass="org.hibernate.evetn.def.DefaultSaveOrUpdateEventListenter"/& gt;hibernate缺省的监听器,本身定义的监听器会覆盖缺省的,因此在这里还要把缺省的监听器注册一下.

当保存user时,会监听到.

4.hibernate的内部缓存分析

(1).第一级缓存是在session中,第二缓存是在sessionFactory

(2).Useruser=(User)s.get(userClass,id);

System.out.println(user.getClass());

user=(User)s.get(userClass,id);

只有一条select语句

(3).当session关闭时,缓存也就没有数据了.

(4).缓存的做用主要用来提升性能,能够简单的理解成一个Map,使用缓存涉及到三个操做:把数据放入缓存、从缓存中获取数据、删除缓存中的无效数据

(5).一级缓存,Session级共 享,save,update,saveOrUpdate,load,get,list,iterate,lock这些方法都会将对象放在一级缓存中,一级 缓存不能控制缓存的数量,因此要注意大批量操做数据时可能形成内存溢出,能够用evict,clear方法清除缓存的内容.

(6).只要有sql语句,就不会去缓存中拿数据,直接到数据库中拿数据

(7).手工的对缓存中的数据进行清除.清除一条记录:s.evict(user);清除全部的记录s.clear();定时的清除能够下降内存溢出的可能性.

(8).session的生命周期很短,只在一个web请求内

5.hibernate介绍与动手入门体验

(1).模型不匹配:Java面向对象语言,对象模型,其主要概念有:继承,关联,多态等,数据库是关系模型,其主要概念有:表,主键,外键等

(2).解决方法:第一种:使用JDBC手工转换,第二种使用ORM框架来解决,主流的ORM框架有Hibernate、TopLink、OJB

(3).下载hibernate,将下载目录/hibernate3.jar和/lib下的hibernate运行时必须的包

(4).配置文件hibernate.cfg.xml和hibernate.properties,XML和properties两种,这两个文件的做用同样,提供一个便可,推荐XML格式,下载目录/etc下是示例配置文件

能够在配置文件制定:

数据库的URL,用户名,密码,JDBC驱动类,方言等,启动时Hibernate会在CLASSPATH里找这个配置文件.映射文件(hbm.xml,对象模型和关系模型的映射),在/eg目录下有完整的hibernate示例

(5).首先创建一个对象:

public class User{

private int id;

private String name;

private Date birthday;

//省略了get/set方法

}

编写映射文件:User.hbm.xml

<hibernate-mappingpackage="cn.itcast.hibernate.domain">

在配置文件中hibernate.cfg.xml:

<propertyname="connection.url">jdbc:mysql://localhost:3306/jdbc

<propertyname="connection.username">root

<propertyname="connection.password">

org.hibernate.dialect.MySQLDialect

<propertyname="hbm2ddl.auto">

<mappingresource="org/hibernate/test/legacy/User.hbm.xml"/>

<class-cacheclass="org.hibernate.test.legacy.Simple" region="Simple"usage="read-write"/>

方言dialect就是哪一种数据库.hibernate本身能够创建表(hbm2ddl.auto)

(6).初始化:

Configuration cfg = new Configuration();

cfg.configure();

SessionFactory sf=cfg.buildSessionFactory();//SessionFactory至关于JDBC中DriverManager

Session s=sf.openSession();//工厂模式,生产connection

Transaction tx=s.beginTransaction();

User user = new User();

user.setBirthday(new Date());

user.setName("name");

s.save(user);

ts.commit();

s.close();

(7).hibernate默认会把事务自动提交功能关闭了,全部本身要手动打开,查看表的结构命令:show create table user,看表的引擎是否支持事务,查看引擎命令:show engines

(8).开发流程:

方式一:由Domain object->mapping->db

方式二:由DB开始,用工具生成mapping和Domain object

方式三:由映射文件

(9).hibernate管理的Domain对象类定义必需要符合JavaBean的定义规则:默认的构造方法(必须的),有无心义的标示符id(主键),非final的,对懒加载有影响

public class User{

private int id;

private String name;

private Date birthDay;

//get/set方法

}

10.编写一个工具类进行初始化Hibernate

public final class HibernateUtil{

private static SessionFactorysessionFactory;

private HibernateUtil(){

}

static{//静态代码块

Configuration cfg = new Configuration();

cfg.configure();//默认的传入是hibernate.cfg.xml文件

sessionFactory = cfg.buildSessionFactory();

}

public static SessionFactorygetSessionFactory(){

return sessionFactory;

}

}

11.static void addUser(User user){//标准规范代码

Session s=null;

Transaction tx=null;

try{

s=HibernateUtil.getSession();

tx.s.beginTransaction();

s.save(user);

tx.commit();

}catch(HibernateException e){

if(tx!=null)

tx.rollback();//不只要回滚,还有抛出异常

throw e;

}finally{

if(s!=null)

s.close();

}

}

6.hibernate配置文件中的配置项

(1).hibernate.cfg.xml和hbm.xml内容解释:

第一:数据类型: type能够是hibernate、java类型或者你本身的类型(须要实现hibernate的一个接口)

第二:基本类型通常不须要在映射文件中说明,只有在一个java类型和多个数据库数据类型相对应时而且你想要的和hiberante缺省映射不一致 时,须要在映射文件中指明类型(如:java.util.Date,数据库 DATE,TIME,DATATIME,TIMESTAMP,hibernate缺省会把java.util.Date映射成DATATIME型),而如 果你想映射成TIME,则你必须在映射文件中指定类型

第三:数据类型的对应关系

(2).Session是非线程安全的,生命周期短,表明一个和数据库的链接,在B/S系统中通常不会超过一个请求;内部维护以及缓存和数据库链接,若是session长时间打开,会长时间占用内存和数据库链接

(3).SessionFactory是线程安全的,一个数据库对应一个SessionFactory,生命周期长,通常在整个系统生命周期内有 效;SessionFactory保存着和数据库链接的相关信息(user,password,url)和映射信息,以及Hibernate运行时要用到 的一些信息.

7. Hibernate映射类型

serializable:序列化到数据库中.

8.Hibernate中使用的集合类型

(1).集合映射(set,list,array,bag,map)

List emps = new ArrayList ();

映射文件中:

<listname="emps">

配置和set标签是相同的,只是区分List,Set的区别

<list-indexcolumn="order_col"/>这一列是给hibernate使用的,须要记录该员工是第几个加进来的,即加入的顺序.

(2).因为懒加载的问题,Hibernate重写了java中的集合类,使其具备懒加载的功能.因此在定义的时候,必需要定义成接口类型即List,Set,Map

9.hql的命名参数与Query接口的分页查询

(1).匿名参数:不使用占位符了

String hql ="from User as user where user.name=:n";

query.setString("n",name);

不会依赖参数的位置

(2).Query接口中的方法

query.setFirstResult(0);

第一条记录从哪开始,参数为开始的位置

query.setMaxResult(10);

实现分页功能

10.Hql与Criteria查询的补充知识

HQL:查询多个对象select art,user from Article art,User user where art.author.id=user.idand art.id=:id这种方式返回的是Object[],Object[0]:article,Object[1]:user;

11.Iterate查询与N+1次查询

(1).假设已经加入到了10个用户

static void iterator(){

Session s=HibernateUtils.getSession();

Query q=s.createQuery("fromUser");

Iterator users =q.iterate();

while(users.hasNext()){

System.out.println(users.next().getName().getFirstName());

}

}

首先把10个用户的id都查询出来,而后按照id去查询详细信息,这是会到一级缓存中查找,找不到在到二级缓存,找不到在到数据库中查找.假设都到 数据库中查询,那么就进行了11次查询,第一次把全部的id都查询,而后再逐一按照id查询进行10次,总共进行了11次,因此在使用时必定要当心,是否 肯定一级缓存和二级缓存中有咱们想要查询的数据,否则的话,性能就降低了

(2).在懒加载的状况下,就会出现N+1次查询,好比一对一:

首先查询IdCard获得id,而后再去访问Person

Session s=HibernateUtil.getSession();

Query q=s.createQuery("fromIdCard");

List ics=q.list();

for(IdCard> ic:ics){

System.out.println(ic.getPerson().getName());

}

由于懒加载,每次访问数据的时候,都进行查询数据库.

12.load方法的懒加载及原理分析

(1).Useruser=(User)s.load(userClass,id);

System.out.println(user.getClass());

就是说s.load(userClass,id)返回的是User的一个代理对象.便是User的子类.在session没有关闭前,去访问数据库 user.getName();可是这种方式很差,最好使用Hibernate.initialize(user);初始化懒加载.

(2).懒加载是将与数据库的交互延迟,提升性能.load()方法,不会到数据库查询,只会返回一个User的一个子类.

(3).asm.jar,cglib.jar这两个包实现懒加载,可以动态的修改内存中的字节码.即动态的生成一个User的子类.

(4).employee.setUser(user);这是就能够使用懒加载,创建employee和user之间个关联,可是不须要去访问数据库的时候

(5).经过asm和cglib两个包实现的,Domain是非final的,session.load懒加载

one-to-one懒加载:必须知足三个条件才能实现懒加载:第一:主表不能有constrained=true,因此主表没有懒加载,第二:lazy!=false,第三:fetch=select;

one-to-many懒加载:第一:lazy!=false,第二:fetch=select

many-to-one:第一:lazy!=false,第二:fetch=select

many-to-many:第一:lazy!=false,第二:fetch=select

(6).可以懒加载的对象都是被改写过的代理对象,当相关联的session没有关闭时,访问这些懒加载对象(代理对象)的属性(getId和 getClass除外),hibernate会初始化这些代理,或用Hibernate.initialize(proxy)来初始化代理对象,当相关联 的session关闭后,再访问懒加载的对象将出现异常.

(7).方法getId和getClass不须要访问数据库也是知道的,因此不是出现懒加载的初始化异常.

(8).表中的属性也能够使用懒加载的,只是须要在编译后的内容进行处理,这种用途主要在字段是大文本类型时须要.

13.OpenSessionInView模式的代码分析

(1).ThreadLocal类

private static ThreadLocal session=newThreadLocal();

线程级变量,做用域在一个线程内.

Session s=(Session)session.get();

if(s==null)}

s=getSession();

session.set(s);

}

当有一个web请求来时,服务器建立一个线程进行服务,将建立一个session,因此在这个线程内能够访问到session

(2).sessioncontext和事务边界

用current_session_context_class属性来定义context(用sessionFactory.getCurrentSession()来得到session),其值为:

第一:Thread:ThreadLocal来管理Session实现多个操做共享一个Session,避免反复获取Session,并控制事务边 界,此时session不能调用close,当commit或rollback的时候session会自动关闭 (connection.realease_mode:after_transaction).Opensession in view:在生成(渲染)页面时保持session打开,前面所说的懒加载时,能够保证session没有关闭,能够访问到数据.

第二:由JTA事务管理器来管理事务(connection.release_mode:after_statement)

(3).用户发送请求->web容器->doFilter(过滤器)->OpenSessionView->打开 session,事务->ActionServlet(struts)的service方法->根据配置文件找到 ->Action(execute方法)->业务逻辑层(register方法)->Dao层(addUser方法)->返回, 直到doFilter的commit,提交事务.在这个过程当中session都没有关闭,能够解决事务的边界问题,解决懒加载的问题(即何时使用懒加 载).缺点:延长事务,session的生命周期,session延迟关闭,那么一级缓存不会释放,长时间占用内存.客户端的网速比较慢,致使事务和 session长时间不能关闭.即延迟关闭.会给服务器端形成很大的负载.

14.Session接口及getloadpersist方法

(1).因为Session能够管理多个数据库表对应的多个实体对象,若是要查询id为1的实体对象,Session.get方法须要知道去哪一个数 据库表中查询id为1的记录,因此,除了给get方法传递所要查询的实体对象的id值外,还必须给get方法传递实体对象的类型,get方法才能知道去哪 个数据库表中进行查询

(2).经过类的类型能够去hibernate.cfg.xml文件中查找到对应的表

(3).在配置文件中添加标签<propertyname="show_sql">true//能够打印sql语句

(4).Useruser=(User)s.get(userClass,id);与User user=(User)s.load(userClass,id);的区别,load不会去访问数据库,只有第一次访问时,才会访问数据库.增长一条打印 出user1的类名的代码,就能够看到load方法所返回的User子类的名称了,该语句以下:

System.out.println(user1.getClass().getName());

(5).s.save(user)和s.persist(user);都是存储数据,persist方法没有sql语句,没有开启事务,save会回滚,persist不会回滚

15.Session与SessionFactory的多线程问题

Session内部封装了一个connection对象,尽可能迟的建立链接,尽可能早的释放链接

16.本地sql查询与命名查询

(1).使用Query接口

static list sql(){

Session s=HibernateUtil.getSession();

Query q = s.createSQLQuery("select * fromuser").addEntity(User.class);//查询的结果是User对象

List rs=q.list();

for(User r:rs){

System.out.println(r.getName());

}

}

(2).不一样的数据库,本地的查询语句是不一样的,因此这种本地的查询语句最好不要使用,兼容性和移植性很差.

(3).命名查询:将查询语句放在配置文件中,之后修改查询语句只修改配置文件中的查询语句就能够了.

<queryname="getUserByBirthday">

<[CDATA[from User wherebirthday=:birthday]]>

这个定义能够放到class标签内部,不须要使用全名,只须要getUserByBirthday便可,可是在这个范围内,不能出现重名,若是在外部,那就须要全名了,cn.itcast.hibernate.domain.User.getUserByBirthday

在配置文件中

static List namedQuery(){

Session s=HibernateUtil.getSession();

Queryq=s.getNamedQuery("getUserByBirthday");

q.setDate("birthday",new Date());

return q.list();

}

(4).hibernate能够作到用Map代替Domain对象,存入到数据库,可是这就符合ORM定义了,同时也能够将数据库中的内容转换XML

17.多对多关联关系的查询

使用表之间的关联join,效率低

18.多对多关联关系的映射与原理分析

(1).多对多(teacher-student):在操做和性能方面都不太理想,因此多对多的映射使用较少,实际使用中最好转换成一对多的对象模型;Hibernate会为咱们建立中间关联表,转换成两个一对多.

ER图:teacher:id(PK);student:id(PK);teacher_student:teacher_id(PK,FK1),student_id(PK,FK2)

(2).

public class Teacher{

private int id;

private String name;

private Set students;

//省略get/set方法

}

public class Student{

private int id;

private String name;

private Set teachers;

//省略get/set方法

}

teacher的映射文件:

根据student_id去查询学生的相关信息

同理student的映射文件类似.

(3).测试类:

Set ts=newHashSet ();

Teacher t1=new Teacher();

t1.setName("t1 name");

Teacher t2=new Teacher();

t2.setName("t2 name");

ts.add(t1);

ts.add(t2);

Set ss=newHashSet ();

Student s1=new Student();

s1.setName("s1");

Student s2=new Student();

s2.setName("s2");

t1.setStudents(ss);//创建关联关系

t2.setStudents(ss);

ss.add(s1);

ss.add(s2);

s.save(t1);

s.save(t2);

s.save(s1);

s.save(s2);

在中间表中插入数据

19.多对一的懒加载分析

(1).查询员工的信息时,是否须要部门的信息,默认的状况下是懒加载的方式,怎样判断是否进行了懒加载,能够经过打印出的sql语句中的查询语句便可

(2).当IdCard中的id是主键也是外键,当id有值时,必定有一个person与之对应,因此能够使用懒加载,先生成一个代理对象,当须要 person的信息时,才去查询,反过来,由于person中的id只是个主键,知道person的id,IdCard中不必定有一个值与之对应,因此不 使用懒加载的方式,而是直接去查询数据库,这就是查询主表时不使用懒加载,查询从表时使用懒加载.

(3).可是多对一的部门和员工,直接就是用了代理,depart.getEmps()获取员工时,Hibernate中的集合把集合空对象和空集合是相同的概念.

20.多对一关联关系的检索与原理分析

(1).查询操做(department表的查询和之前同样,只是employee表不同):

static Employee query(int empid){

Employee emp =(Employee)s.get(Employee.class,empid);

System.out.println("departname:"+emp.getDepart().getName());//获得department的名称.

return emp;

}

进行两次查询,首先根据id查询employee表,获得depart_id,在根据depart_id查询department表.

21.多对一关联关系的映射与原理分析

(1).多对一:映射文件:

ER图中定义Employee主键(PK):id和外键(FK):depart_id,Department的主键id;

(2).创建Department类

public class Department{

private int id;

private String name;

//省略get/set方法

}

创建Employee类

public class Employee{

private int id;

private String name;

private Department depart;

//省略get/set方法

}

(3).映射文件:

<hibernate-mappingpackage="cn.itcast.hibernate.domain">

不在使用标签property,是对象类型depart,使用标签

经过反射能够找到depart对应的映射文件,当depart_id与depart映射文件中的id相同时,就查找到了.也能够使用属性not-null="true",设置colum="depart_id"这列不为空

(4).column="depart_id"不设置能够,默认就是column="depart"

(5).staticDepartemnt add(){

//模板代码,省略

Department depart = new Department();

depart.setName("depart name");

Employee emp = new Employee();

emp.setDepart(depart);//直接赋值就能够了,只要在对象创建关系,数据库中的表就创建关系了.

emp.setName("emp name");

s.save(depart);

s.save(emp);

return depart;

}

(6).当s.save(depart);与s.save(emp)两条语句的顺序调换,会多出现一条更新语句,由于首先存储emp,当存储到 depart时,由于employee中定义了department,因此hibernate检测到employee中的depart发生改变了,就进行 了更新操做.此时是持久态

22. 分布式缓存的分析

大型网站有多个服务器,即就有多个cache,每一个服务器对应一个cache,

23.关联关系的级联操做

(1).cascade和inverse:

Casade用来讲明当对主对象进行某种操做时是否对其关联的从对象也作相似的操做,经常使用的cascade:none,all,save- update,delete,lock,refresh,evict,replicate,persist,merge,delete- orphan(one-to-many),通常对many-to-one,many-to-many不设置级联,在one-to-one和one-to- many中设置级联

Inverse表“是否放弃维护关联关系”(在Java里两个对象产生关联时,对数据库表的影响),在one-to-many和many-to- many的集合定义中使用,inverse="true"表示该对象不维护关联关系;该属性的值通常在使用有序集合时设置成false(注意 hibernate的缺省值是false),one-to-many维护关联关系就是更新外键,many-to-many维护关联关系就是在中间表增减记 录

注:配置成one-to-one的对象不维护关联关系.

24.缓存的原理与模拟分析

(1).第一我的读的信息和后一我的读的信息可能相同,那第二我的读信息时可以加快速度了.

(2).第二人读取信息时,就不是到数据库中读取了,能够到缓存中读取数据.

(3).使用缓存cache存取数据.

25.继承_鉴别器与内链接器相结合

(1).子类的特有属性不少,就拿一张表进行对应,特有属性少的就和父类放在同一个表中,

(2).employee:id(PK),name,depart_id,type,skill;sales:employee_id(PK,FK),sales;

Skiller子类和Employee父类放在一块儿,Sale类本身对应一张表.

(3).映射文件中只需按照前两中方式进行改变.

26.继承_每一个具体类映射一张独立表

(1).没有公共的属性,全部的属性都是本身特有的,在插入时候不须要涉及到多个表的关联了,效率高.若是employee不是抽象的,会有employee表

(2).employee:id(PK),name,depart_id;skiller:id(PK),name,skill,depart_id;sales:id(PK),name,sell,depart_id;

(3).映射文件:

(4).在查询的时候,多态查询时,仍是要进行三种表的关联查询,可是插入只在一张表进行.

27.继承关系_每一个类映射到一张表

(1).employee:id(PK),name,depart_id;sales:employee_id(PK,FK),sell;skiller:employee_id(PK,FK),skill;

(2).此时不须要鉴别器了,每一个子类对应一张表

(3).映射文件:

<joined-subclassname="Skiller" table="skiller">

(4).插入子类时,相同的属性插入到employee表中,本身特有的属性插入到本身表中,若是插入一个技术员Skiller(name,skill)时skill插入skiller表中,name插入employee表中,这时就插入了两张表.

(5).当查询本身特有的属性时,会关联两张表,当查找相同的属性时,会关联三张表.因此查询时效率低.不要进行多态查询,最好查询具体的子类:

具体查询:Employee emp = (Employee)s.getId(Skiller.class,id);

多态查询:Employee emp = (Employee)s.getId(Skiller.class,id);

28.继承关系_整个继承树映射到一张表

(1).public classSkiller extends Employee{

private String skill;

//省略get/set方法

}

public class Sales extends Employee{

private int sell;

//省略get/set方法

}

(2).employee表中的字段:id(PK),name,depart_id,type(区分不一样类型的员工,又称鉴别器),skill,sell

(3).这种方式当增长子类时,须要修改employee表结构.

(4).映射文件:

鉴别器,hibernate用来区分不一样的子类.

(5).将一棵继承树映射到一张表中,因此在查询时,只对一张表进行操做,效率高,可是不灵活,当增长子类时,须要更改表结构,同时每一个字段不能设置成非空约束.

29.实体对象的三种状态与saveOrUpdate方法

(1).Session的几个主要方法:

第一:save,persist保存数据,persist在事务外不会产生insert语句

第二:delete:删除对象

第三:update:更新对象,若是数据库中没有记录,会出现异常

第四:get:根据ID查询数据,会马上访问数据库

第五:load:根据ID查询,(返回的是代理,不会当即访问数据库)

第六:saveOrUpdate,merge(根据ID和version的值来肯定是save或update),调用merge你的对象仍是托管的

第七:lock(把对象编程持久对象,但不会同步对象的状态)

(2).瞬时(transient):数据库中没有数据与之对应,超过做用域会被JVM垃圾回收器回收,通常是new出来且与session没有关联的对象

持久(persistent):数据库中有数据与之对应,当前与session有关联,而且相关联的session没有关闭,事务没有提交;持久对象状态发生改变,在事务提交时会影响到数据库(hibernate能检测到)

脱管(detached):数据库中有数据与之对应,但当前没有session与之关联;托管对象状态发生改变,hibernate不能检测到.

(3).当关闭session时,持久态就变成了脱管状态了,区分这三种状态的两个标准:是否与数据库中记录相对应,是否在session中.

(4).当在脱管的状态时,更新的时候须要执行update的更新语句,由于不在session中.

(5).对象new是瞬时的,get(),load(),find(),iterate()等是持久的,瞬时状态执行 save(),saveOrUpdate()时变成持久的,当持久状态执行delete()时变成瞬时的,当脱管状态执行 update(),saveOrUpdate(),lock()时变成持久状态,当持久状态执行evict(),close(),clear()时,持久 状态变成脱管状态.

(6).瞬时对象的id没有值,脱管对象的id是有值的.因此当没有值时执行save()方法,当有值时执行update()方法.

30.实体类或属性名与数据库关键字冲突问题

使用Oracle时,user是个关键字,可能出现问题,将表名添加反引号.

31.使用Hibernate完成CRUD实验的步骤说明

(1).实验步骤:

第一步:设计domain对象User

第二步:设计UserDao接口

第三步:加入hibernate.jar和其依赖的包

第四步:编写User.hbm.xml映射文件,能够基于hibernate/eg目录下的org/hibernate/auction/User.hbm.xml修改

第五步:编写hibernate.cfg.xml配置文件,能够基于hibernate/etc/hibernate.cfg.xml修改;必须提 供的几个参数:connection.driver_class、connection.url、connection.username、 connection.password、dialect、hbm2ddl.auto

第六步:编写HibernateUtils类,主要用来完成hibernate初始化和提供一个得到Session的方法

第七步:实现UserDao接口

32.事务的悲观锁和乐观锁

(1).悲观锁和乐观锁

悲观锁由数据库来实现;乐观锁hibernate用version和timestamp来实现,悲观锁就至关于写锁,当本身在操做时,别人不能进行任何操做,

(2).可能多我的来读取同一个数据源,可能后一我的修改后的结果覆盖前一我的修改的结果,存在并发问题

(3).悲观锁是不可取的,咱们给每条记录添加一个版本号,当同时操做数据源时,判断版本号,若是版本号不符合,就不进行更新.假设刚开始版本号为 0,同时来两我的进行操做,判断版本号是否为0,若是为0,就进行操做,操做完后版本号加一,那第二我的就发现版本号不等于0,就不会进行操做了,也不会 覆盖前一我的进行的操做.

(4).在映射文件中:

<versionname="ver"/>该标签必须在id标签的下面,便是id的子标签.

(5).版本号的类型是整型的,也能够是日期型的

(6).

Session s1=HibernateUtil.getSession();

Transactiontx1=s1.beginTransaction();//第一个线程操做事务

User user1=(User)s1.get(User.class,id);

Session s2 =HibernateUtil.getSession();

Transactiontx2=s2.beginTransaction();//第二个线程操做事务

User user2=(User)s2.get(User.class,id);

user1.getName().setFirstName("firstName1");

user2.getName().setFirstName("firstName2");

tx2.commit();//线程二先提交,成功了

tx1.commit();//线程一提交不成功.由于版本号不同.

33.事务与事务边界的相关知识

(1).一个SessionFactory对应一个数据库,由JDBC实现

(2).事务的控制应该在业务逻辑层实现.可是事务的对象是在DAO层,那么在业务逻辑层中调用事务的对象,就出现了耦合,因此要解决这个耦合,就需借助第三方架包了EJB,Spring

34.完善HibernateUtil类及hql查询入门

(1).HQL:面向对象的查询语言,与SQL不一样,HQL中的对象名是区分大小写的(除了Java类和属性其余部分不区分大小写),HQL中查的 是对象而不是和表,而且支持多态;HQL主要经过Query来操做,Query的建立方式:Query q=session.createQuery(hql);

from Person

from User as userwhere user.name=:name//其中User是类不是表名,user是别名

form User as userwhere user.name=:name and user.birthday<:birthday

(2).Criteria:是一种比HQL更面向对象的查询方式;Criteria的建立方式:Criteria crit=session.createCriteria(DomainClass.class);

简单属性条件如:

criteria.add(Restrictions.eq(propertyName,value)),criteria.add(Restrictions.eqProperty(propertyName,otherPropertyName))

(3).public staticvoid add(Object entity){//可以保存全部对象

Session s=null;

Transactiontx=null;

try{

s=HibernateUtil.getSession();

tx.s.beginTransaction();

s.save(entity);

tx.commit();

}catch(HibernateExceptione){

if(tx!=null)

tx.rollback();//不只要回滚,还有抛出异常

throw e;

}finally{

if(s!=null)

s.close();

}

}

同理更新,删除同时一样的道理

(4).执行HQL语句

Session s=null;

try{

s=HibernateUtil.getSession();

Stringhql="from User as user where user.name=?";

Queryquery=s.createQuery(hql);

query.setString(0,name);//替换占位符

Listlist=query.list();//JDBC中的executQuery()相似

for(Useruser:list{

System.out.println(user.getName());

}

//Object obj=query.uniqueResult();当肯定返回值只有一个的时候,使用这种方法.当查询有多个结果时,会出现异常

}finally{

if(s!=null)

s.close();

}

支持多态,查询的话,子类对应数据库表也被查询,若是from Object的话,会把数据库中的表都查一遍,由于全部的类都是Object的子类.

35.一对多关联关系的映射与原理分析

(1).在Department的角度上是否是一对多了,在Department中定义:

privateSet emps;//一个department中有多个员工

(2).映射文件:

<classname="Department">

<idname="id">

<generatorclass="native"/>

<propertyname="name"/>

<setname="emps">用set属性进行映射

<keycoluem="depart_id"/>设置外键

<one-to-manyclass "Employee"/>

(3).System.out.println("empsize:"+depart.getEmps().size());

打印出department中全部的employee人数.

(4).首先添加employee,在添加到department,即告诉employee属于哪一个department;多两条更新语句.

Set emps = new HashSet ();

emps.add(emp1);

emps.add(emp2);

depart.setEmps(emps);

告诉department有哪些employee

emp1.setDepart(depart);

emp2.setDepart(depart);

(5):ER图:Deparment:id(PK);Employee:id(PK),depart_id(FK1);

36.一对多和多对多的懒加载分析

(1).对于one-to-one懒加载方式体现出的效率不是很明显,查询身份证号时,把person的信息也查询出来,没有查询太多的信息,对效率的影响不是很大

(2).对于one-to-many懒加载方式就体现的很明显的了,当咱们查询部门的详细信息时,可能把该部门的全部员工都查询出来,由于一个部门可能有不少员工,因此这时效率就明显下降了.

(3).缺省的是懒加载,当depart.getEmps()时,才会查询员工的信息,由于java中的set集合没有懒加载的功能,当咱们的代码 只是获取集合代理对象的引用,比没有调用该集合代理对象的方法,因此,hibernate在这里还用不着去查询数据库来填充集合代理,所以不会抛出"代理 未初始化"的异常,若是将代码改成depart.getEmps().size(),就能够看到异常了.

(4).对于many-to-many方式懒加载也很重要,由于涉及到三张表的查询.因此也须要懒加载的功能.

37.一对一的懒加载分析

(1).one-to-one在查询主对象的时候默认状况下不使用懒加载,使用一个关联查询.可是在查询从对象的时候使用了懒加载.

(2).constrain=true是创建外键约束

(3).lazy="proxy",使用懒加载,默认的值也是proxy,还有false,true的取值

(4).fetch="join",使用什么方式去抓取,默认值为select,join是一次查询(表的链接),select是两次查询.当lazy="proxy"时,fetch="join"是无效的,它们俩之间的设置是互斥的.

38.一对一外键关联关系的映射与原理分析

(1).一对一:基于外键的one-to-one,能够描述为多对一,加上unique="true"约束 <many-to-onename="person" column="person_id" unique="true"not-null="true"/>区别于多对一.只需将外键设置为惟一.

(2).对于IdCard的映射文件,其的id不是外部生成的,而是自增加的.

<generatorclass="native"/>对于Person的映射文件:

39.一对一主键关联关系的检索

(1).查询主对象:

Personp=(Person)get(Person.class,id);

System.out.println(p.getIdCard().getId());

理论上是两次查询,可是实际只进行了一次查询,使用了表之间的关联join,效率上比两次查询高

查询从对象:

IdCardidCard=(IdCard)get(IdCard.class,id);

System.out.println(idCard.getPerson().getId());

理论上和实际上都进行了两次查询

40.一对一主键关联关系的映射与原理分析

(1).基于主键的one-to-one(person的映射文件)

<generatorclass="foregin"><paramname="property">idCard

(2).对象模型:

public class Person{

private int id;

private String name;

private IdCard idCard;

//省略get/set方法

}

public class IdCard{

private int id;

private String name;

private Person person;

//省略get/set方法

}

(3).Person的映射文件:

IdCard的映射文件:

主键是由外部获得,不是本身获得的

<paramname="property">personIdCard的id是由person获得的

添加约束,配置外键.

idcard中的主键是person中的外键

(4).测试代码:

IdCard idCard = new IdCard();

Person p=new Person();

p.setName("p1");

p.setIdCard(idCard);

idCard.setPerson(p);

s.save(p);

s.save(idCard);

IdCard中的id是由Person获得的.只有主对象(person)存在,从对象(idcard)存在.

(5).ER图:person:id(PK);IdCard:id(PK,FK)

41.组件关联关系的映射与原理分析

(1).组件映射(User-Name):关联的属性是个复杂类型的持久化类,但不是实体即:数据库中没有表与该属性对应,但该类的属性要之久保存的

当组件的属性不能和表中的字段简单对应的时候能够选择实现:

org.hibernate.usertype.UserType或

org.hibernate.usertype.CompositeUserType

(2).用户名name是个对象类型

public Class Name{

private String firstName;

private String lastName;

//get/set方法省略

}

想过使用一对一.一对多,多对一均可以,一我的只能有一个名字,一我的能够有多个名字.这是数据库中确定有两张表:User和Name,可是如今 Name的内容很小,不想设计成一个实体,不想在数据库中对应一张表,由于它过小了,此时就是用组件相关联,将用户user和名字name设计到同一张表 中

6、 JDBC

1.DAO设计思想与搭建骨架

(1).创建Domain包,在包中创建一个实体对象(bean).

public class User{

private int id;

private String name;

private Date birthday;//java.util.Date

private float money;

//生成对应的get/set方法,省略

}

(2).定义Domain接口:

public interface UserDao{

public void addUser(User user);

public User getUser(int userid);

public void update(User user);

public void delete(User user);

public User findUser(StringloginName,String password);

}

这个接口是给service层使用的.

(3).实现UserDao接口

public class UserDaoImpl implements UserDao{

public void addUser(User user){};

public User getUser(int userid){};

public void update(User user){};

public void delete(User user){};

public User findUser(StringloginName,String password){};

}

(4).在UserDaoImpl中抛出的异常进行包装,定义一个异常类

(5).工厂模式:UserDao userDao = DaoFactory.getInstance().getUserDao();

2.Java的动态代理及使用该技术完善链接代理

(1).前面说到的静态代理模式,有点麻烦,由于须要实现接口Connection的全部方法.

(2).public classMyConnectionHandler implements InvocationHandler{

private Connection realConnection;

MyConnectionHandler(){

}

Connectionbind(Connection realConn){//经过此方法将链接传进来

ConnectionwarpedConnection = (Connection)Proxy.newProxyInstance(this.getClass().getClassLoader(),newClass[] {Connection.class},this);//动态的编写一个类,这个类实现Connection接口,最终会把Connection的全部方 法都交给InvocationHandler处理器处理,在内存中直接产生一个字节码.

returnwarpedConnection;

}

public Objectinvoke(Object proxy,Method method,Object[] args){

if("close".equals(method.getName())){//是close方法

this.dataSource.connectonsPool.addList(this.warpedConnection);

}

returnmethod.invoke(this.realConnection,args);

}

}

这就是动态代理模式,无论是动态的,仍是静态的,最终到底都是关心操做Connection的方法.

3.JdbcTemplate类中的其余各个查询方法

(1).Spring的JdbcTemplate

第一:查询带有参数,和行映射方法:

public ObjectqueryForObject(String sql,Object[]args,RowMapper rowMapper),使用自定义的UserRowMapper完成映射

一个RowMapper的经常使用实现BeanPropertyRowMapper,该实现可将结果集转换成一个Java Bean(字段名与Java Bean属性名不符合规范,可用别名处理)返回一条记录.

第二:public List query(String sql,Object[]args,RowMapperrowMapper)返回多条记录

第三:public int queryForInt(String sql)(如:selectcount(*) from user),其余结果好比String可用queryForObject方法向下转型

public MapqueryForMap(String sql,Object[]args)返回不是对象类型的Map(key:字段名或别名,value:列值);当查询的结果不是一个对象时,就是用一个 Map进行存放结果.查询共多少条记录,最大值,最小值等信息时,当返回的是String类型时,就是用queryForObject(String sql);只是要对返回类型进行转换.

第四:public List queryForList(String sql,Object[]args)返回多个Map

4.JDBC的理论概述

(1).JDBC(Java数据库链接)由一些借口和类构成的api,j2se的一部分,由java.sql,javax.sql包组成

(2).应用程序、JDBC API、数据库驱动及数据库之间的关系:

应用程序-->JDBC-->MySql Driver,Oracle Driver,DB2Driver--->MySql,ORacle,DB2

5.jdbc中数据类型与日期问题

(1).rs.getInt("id"),getString("name"),rs.getDate("birthday"),rs.getFloat("money")),不一样的类型的获取数据.

(2).java.sql.Date是继承java.util.Date,java.util.Date是日期和时间的,而java.sql.Date只有日期,而没有时间.

(3).不能将java.util.Date赋给java.sql.Date,所 以:newjava.sql.Date(birthday.getTime()));这样就能够将java.util.Date转换成 java.sql.Date,java.sql.Date直接赋给java.util.Date能够的.

(6).st.executeUpdate(sql),带参数的方法是Statement的,不带参数的方法是PreperedStatement的

6.JTA分布事务的简要介绍

(1).跨多个数据源的事务,使用JTA容器实现事务,分红两个阶段提交

javax.transaction.UserTransactiontx=(UserTransaction)ctx.lookup("jndiName");

tx.begin();//connection1,connection2(可能来自不一样的数据库)

tx.commit()//tx.rollback();

(2).tomcat不支持这种容器.weblogic能够支持.

(3).第一阶段:向全部的数据库提交事务的请求,当有事务回滚的请求,全部的数据库都回滚,第二阶段:当没有回滚请求,就进行提交事务.

(4).分布式事务处理.

7.Statement的sql注入问题

(1).SQL注入:PreparedStatement和Statement:

在sql中包含特殊字符或SQL的关键字(如:'or 1 or')时,Statement将出现不可预料的结果(出现异常或查询的结果不正确),可用PreparedStatement来解决

PreperedStatement(从Statement扩展而来)相对Statement的优势:

第一:没有SQL注入的问题

第二:Statement会使数据库频繁编译SQL,可能形成数据库缓冲区溢出

第三:数据库和驱动能够对PreperedStatement进行优化(只有在相关联的数据库链接没有关闭的状况下有效)

PreparedStatement是Statement的子接口.

(2).

PreparedStatementps=null;//预处理接口,须要进行预处理,因此在构造的时候就须要SQL语句了,ps=conn.prepareStatement(sql);而Statement是在查询的时候须要SQL语句.

Stringsql="select id,name from user where name=?";?问号是占位符

ps.setString(1,name);将传过来的name参数替换第一个占位符?,在此过程当中,将name进行的处理,将特殊符号去除,当执行查询时,不须要SQL语句了,否则会报错,rs=ps.executeQuery();

(3).创建链接最消耗时间的,当程序执行屡次时,PreperedStatement比Statement除去创建链接的时间,前者效率高.

8.编写一个基本的链接池来实现链接的重复使用

(1).链接池常用到插入和删除,因此使用LinkedList,

public class MyDataSource{

private LinkedList connectionsPool = new LinkedList ();

public MyDataSource(){

for(int i=0;i<10;i++){//开始时建立10个链接

this.connectionsPool.addLast(this.createConnection());

}

}

public Connection createConnection() {//建立链接

returnDriverManager.getConnection(url,user,password);

}

public Connection getConnection(){//获取链接

return this.connectionPool.removeFirst();

}

public void free(Connection conn){//释放链接

this.connectionsPool.addList(conn);

}

}

获得链接并非重复的.想重复的拿取链接,建立的链接数n<用户取链接数m,便可.

(2).

private static int initCount=5;//定义初始化链接数

private static int maxCount=10;//最大链接数

private static int currentCount=0;//当前建立的链接数

(3).为了保证并发操做,须要在获取链接中同步:

synchronized(connectionsPool){

if(this.connctionPool.size()>0)//链接池中还有链接

return this.connectionsPool.removeFirst();

if(this.currentCount<maxCount)//当前链接数没有超过最大链接数,能够接着建立链接,

return this.createConnection();

throw new SQLException("已经没有链接了");//超过了最大链接数,抛出异常.

}

9.编写一个简单的JDBC的例子

(1).链接数据的步骤:

第一步:注册驱动(只作一次)

DriverManager.registerDriver(newcom.mysql.jdbc.Driver());

第二步:创建链接(Connection)

Connectionconn=DriverManager.getConnection("jdbc:mysql://localhost:3305/jdbc","root","");没有密码

第三步:建立执行SQL的语句(Statement)

Statementst=conn.createStatement();

第四步:执行语句

ResultSet rs =st.executeQuery("select * from user");

第六步:处理执行结果(ResultSet)

while(rs.next()){//遍历行

System.out.println(rs.getObject(1)+'\t'+rs.getObject(2));//第一列,第二列

}

第七步:释放资源

rs.close();//关闭资源和打开资源的顺序是相反的

st.close();

conn.close();

10.参数的元数据信息

(1).

Connection conn=JdbcUtils.getConnection();

PreparedStatementps=null;

ResultSet rs=null;

ps.conn.prepareStatement(sql);//sql中可能含有参数(占位符),Object[]params存储参数,能够动态的查看sql中含有哪些参数.

ParameterMetaDatapmd=ps.getParameterMetaData();

intcount=pmd.getParameterCount();//获得参数的个数

for(inti=1;i<count;i++){

System.out.println(pmd.getParameterClassName(i));//获得参数的类名

System.out.println(pmd.getParameterType(i));//获得参数的类型

ps.setObject(i,parames[i-1]);//遍历替换参数

}

String sql ="select * from user where name=? and birthday<? and money>?";

直接返回的类型都是String,VARCHAR

11.分析jdbc程序的编写步骤和原理

(1).链接是经过底层的TCP/IP协议进行的

(2).注册驱动:

方式一:Class.forName("com.mysql.jdbc.Driver");

推荐这种方式,不会对具体的驱动类产生依赖,类加载到内存中,会调用静态代码块:

static{

try{

DriverManager.registerDriver(new Driver());

}catch(SQLException e){

throws RuntimeException();

}

}

方式二:DriverManager.registerDriver(newcom.mysql.jdbc.Driver());

会形成DriverManager中产生两个同样的驱动,并会对具体的驱动类产生依赖,其内部定义了一个Vector列表,将多个驱动存放到Vector中

方式三:System.setProperty("jdbc.drivers","driver1:driver2");

虽然不会对具体的驱动类产生依赖;但注册不太方便,因此不多使用,能够注册多个驱动

(3).方式一接受的是一个字符串,方式二接受的是一个驱动类,因此具备依赖关系

(4).建立链接:

Stringurl="jdbc:mysql://localhost:3394/jdbc";

格式:jdbc:子协议:子名称//主机名:端口/数据库名

String user="root";

String password="";

Connectionconn=DriverManager.getConnection(url,user,password");

(5).释放资源:数据库创建链接的个数也是有限制的,当数据库建立了多个链接,数据库可能运行的很慢,可能致使数据库崩溃,占用系统资源.

12.分析在实际项目中该如何应用JDBC

(1).三层架构:

表示层:基于web的jsp、servlet、struts、webwork、spring web MVC等,基于客户端的swing,swt等

业务逻辑层:Pojo(service,manager),Domain,session EJB、spring

数据访问层:JDBC,IBatis,Hibernate,JDO,Entity Bean

层与层之间用接口隔离

13.规范和封装JDBC程序代码

(1).规范的代码:

Stringurl="jdbc:mysql://localhost:2332/jdbc";

String user="root";

String password="";

Statement st=null;

ResultSet rs=null;

Connecton conn=null;

try{

Class.forName("com.mysql.jdbc.Driver");

conn=DriverManager.getConnection(url,user,password);

st = conn.createStatement();

rs=st.executeQuery("select * fromuser");

}finally{

try{

if(rs!=null)

rs.close();

}finally{if(st!=null)

try{

st.close();

}finally{

if(conn!=null)

conn.close();

}

}

}

}

(2).设计一个工具类:

public final class JdbcUtils{

private static Stringurl="jdbc:mysql://localhost:2332/jdbc";

private static String user="root";

private static String password="";

private JdbcUtils(){//不容许实例化

}

static{//驱动只注册一次

try{

Class.forName("com.mysql.jdbc.Driver");

}catch(ClassNotFoundException e){

throw new ExceptionInitializerError(e);

}

}

public static Connection getConnection(){//建立链接

returnDriverManager.getConnection(url,user,password);

}

public static void free(ResultSetrs,Statement st,Connection conn){//释放资源

try{

if(rs!=null)

rs.close();

}catch(SQLException e){

e.printStackTrace();

}finally{if(st!=null)

try{

st.close();

}catch(SQLException e){

e.printStackTrace();

}finally{

if(conn!=null)

conn.close();

}

}

}

}

}

14.将Dao中的修改方法提取到抽象父类中

(1).对于代码的重构,焦点就是将代码变化的部分和不变的部分分离开来.一个是sql语句不一样,参数不一样,提取一个超类,相同的部分,放到超类中,不一样的部分由子类实现.

publci abstract class AbstractDao{

public void update(String sql,Object[]args){

Connection conn=null;

PreparedStatement ps=null;

ResultSet rs=null;

conn=JdbcUtils.getConnection();

ps=conn.prepareStatement(sql);

for(int i=0;i<args.length;i++){//用args参数列表,更新数据

ps.setObject(i+1,args[i]);

}

}

//args就是参数列表,

}

public class UserDaoImpl extendsAbstractDao{

public void update(User user){

String sql="update user setname=?,birthday=?,money=?,where id=?";

Object[] args=new Object[]{user.getName(),user.getBirthday(),user.getMoney(),user.getId()};

super.update(sql,args);//调用父类的update方法.

}

}

15.可更新和对更新敏感的结果集

(1).

st=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,Result.CONUR_UPDATABLE);

在读取数据时,能够更改数据,可更新结果集.

(2)

while(rs.next()){

rs.getObject("name");

rs.getObject("money");

String name= rs.getString("name");

if("lisi".equals(name)){

rs.updateFloat("money",200f);

rs.updateRow();//更新行

}

}

(3).这种方式不经常使用,在查询时,把数据更改了,给人一种不明确感.

在查询结果集时,更新数据时数据库能不能感知到数据更新了.数据库敏不敏感SENSITIVE

16.利用结果集元素数据将查询结果封装为map

(1).将查询结果放在map中

key:列的名称

value:列的值

(2).

Connection conn=JdbcUtils.getConnection();

ps=conn.prepareStatement(sql);

rs=ps.executeQuery();

ResultSetMetaData rsmd =rs.getMetaData();//获取结果集的元数据

int count= rsmd.getColumnCount();//结果有多少列

for(int i=1;i<=count;i++){//循环遍历列

System.out.println(rsmd.getColumnClassName(i));//每一列的类型名

System.out.println(rsmd.getColumnName(i));//每一列的名称

System.out.println(rsmd.getColumnLabel(i));//每一列的别名

}

这里就能够准确的获得每一列的类型,而不像前面的都是String类型.

String[]colName=new String[count];//存放每一列的名称

Map<String,Object> data=null;

while(rs.next()){//按行循环

data = new HashMap<String,Object>();

for(int i=0;i<colNames.length;i++){//按列循环

data.put(colNames[i],rs.getObject(colNames[i]));//根据类名获得列的值

}

}

灵活性很是高.可以按照各类方式查询

16.如何使用开源项目DBCP

(1).dbcpconfig.properties数据源的配置文件:

链接配置:

driverClassName=com.mysql.jdbc.Driver

url=jdbc:mysql://localhost:3306/test

username=root

password=root

初始化链接:

initialiSize=10

最大链接数量:

maxActive=50

最大空闲链接://不一样的时间段建立的链接不一样,可能出现空闲链接.

maxIdle=20

最小空闲链接:

minIdle=5

超过等待时间以毫秒为单位 6000毫秒/1000等于60秒

maxWait=60000

//当没有链接可取的时候,让当前线程等待一段时间,在去拿链接

JDBC驱动创建链接时附带的链接属性,属性的格式必须为这样:[属性名=property;],注意:"user" 与"password"两个属性会被明确地传递,所以这里不须要包含它们(url后面携带的值)

connectionProperties=userUnicode=true;characterEncoding=gbk

指定由链接池所建立的链接的自动提交状态

defaultAutoCommit=true

driver default指定由链接池所建立的链接的只读(read-only)状态,若是没有设置该值,则"setReadOnly"方法将不被调用,(某些驱动并不支持只读模式,如:Informix)

defaultReadOnly=

driver default指定 由链接池所建立的链接事务级别(TransactionIsoation),可用值为下列之一(详情可见javadoc)NONE,READ,UNCOMMITTED,READ_COMMITTE

defaultTransactionIsolation=READ_UNCOMMITTED

(2).DBCP是apache的开源项目.实现了DataSource接口.Data Base Connection Pool,修改代码须要重新编译,打包,修改配置文件只须要重启便可.

(3).

Properties prop = new Properties();

InputStream is =JdbcUtils.class.getClassLoader().getResource.AsStream("dbcp.property");//读取property文件

prop.load(is);

private static DataSoruce myDataSource=null;

myDataSource=BasicDataSourceFactory.createDataSource(prop);

(4).DataSource用来取代DriverManager来获取Connection;经过DataSource得到Connection 速度快;经过DataSource得到Connection都是已经被包裹过的(不是驱动原来的链接),它的close方法已经被修改了;通常 DataSource内部会用一个链接池来缓存Connection,这样能够大幅度提升数据库的访问速度;链接池能够理解成一个可以存放 Connection的Collection;咱们的程序只和DataSource打交道,不会直接访问链接池.

(5).使用dbcp须要的三个包:common-dbcp.jar,common-collections.jar,common-pool.jar

17.使用JDBCTemplate工具类简化对象查询

(1).Spring框架中提供了一个JdbcTemplate工具类,JdbcTemplate类对JDBC API进行了很好的封装,这个类就像咱们本身对JDBC进行封装同样,只是代码更健壮和功能更强大而已,咱们之后在实际项目中能够使用 JdbcTemplate类来彻底替换直接使用JDBC API,这与直接使用JDBC API没有太大的性能区别,使用JdbcTemplate类须要额外从spring开发包中导入spring.jar和commons- logging.jar包

(2).JdbcTemplate的设计思想和前面的MyDaoTemplate类是相同的

static User findUser(String name){

JdbcTemplate jdbc = newJdbcTemplate(JdbcUtils.getDataSource());//参数是拿到一个数据源

String sql = "select id,name from userwhere name=?";

Object[]args=new Object[]{name};

jdbc.queryForObject(sql,args,newRowMapper(){//行映射器

public Object mapRow(ResultSet rs,introwNum){

User user = new User();

user.setId(rs.getInt("id"));

return user;

}

});

return null;

}

//这里能够不须要实现行映射器,能够用类代替:

new BeanPropertyRowMapper(User.class);便可

18.使用JdbcTemplate实现Dao和用工厂模式灵活切换实现

(1).

public class UserDaoSpringImpl implementsUserDao{

private SimpleJdbcTemplatesimpleJdbcTemplate

= new SimpleJdbcTemplate(JdbcUtils.getDataSource());

//增长用户

public void addUser(User user){

String sql = "insert into user(name,money,birthday)values(:name,:money,:birthday);

KeyHolder keyHolder = newGeneratedKeyHolder();

SqlParameterSource param = new BeanPropertySqlParameterSource(user);

this.simpleJdbcTemplate.getNamedParameterJdbcOperations().update(sql,param,keyHoler);

user.setId(keyHolder.getKey().intValue());

}

}

//增长user的代码减小了太多了.

delete,update等方法都是大同小异

//删除用户

public void delete(User user){

String sql = "delete from user whereid=?";

this.simpleJdbcTemplate.update(sql,user.getId());

//使用了可变参数的特性,不须要复杂的Map存储参数

}

//查询用户

public User findUser(StringloginName,String password){

//如何简化返回查找集

String sql = "selectid,name,money,birthday from user where name=?";

returnthis.simpleJdbcTemplate.queryForObject(sql,ParameterizedBeanProertyRowMapper.newInstance(User.class),userId);

}

//更新用户

public void update(User user){//如何替换占位符?

String sql ="update user setname=?,birthday=?,money=? where id=?";

this.simpleJdbcTemplate.update(sql,user.getName(),user.getBirthday(),user.getMoney(),user.getId());

//这里也能够使用bean属性的参数源;

}

代码量大大减小了.

19.使用JDBC的批处理功能

(1).和数据库打交道的成本是很高的,当须要发送多条sql语句时,成本更高了,这时就须要使用批处理技术,将多条查询语句打成一个包.

(2).

for(int i=0;i<10000;i++){

ps.setString();

ps.setName();

ps.addBatch();//把一条更新语句增长到包中

}

int[] a = ps.executeBatch();//执行批处理,不是ps.executeUpdate();

(3).首先将语句打包时,并非包越大越好,若是包过大的话,可能形成内存溢出,因此可能将一个打包在分红几个小包进行发送,不一样的数据库,包的最适合大小是不一样的.

(4).并非全部的批处理都能提升性能,这和不一样的数据库以及数据库驱动决定的.

(5).Hibernate就是用了批处理技术,可是它进行了一些优化技术.

20.使用JDBC调用的存储过程

(1).存储过程常常用在之前的两层结构中,如今的三层结构已经就用不到了

(2).CallableStatement(从PreparedStatement继承来的)

java代码:

CallableStatement cs=null;

String sql="{calladdUser(?,?,?,?)}";

cs=conn.prepareCall(sql);

//替换参数

cs.registerOutParameter(4,Types.INTEGER);

cs.setString(1,"ps name");

cs.setDate(2.new java.sql.Date(System.currentTimeMills()));

cs.setFloat(3,100f);

cs.executeUpdate();

int id = cs.getInt(4);

System.out.println(id);

存储过程:

create procedure 'jdbc'.'addUser' (in pnamevarchar(45),in birthday date,in money float,out pid int)

//in:输入参数,out:输出参数

begin

insert intouser(name,birthday,money)values(pname,birthday,money);

select last_insert_id() into pid;

//last_insert_id()是一个函数,最后一次插入的id号

end $$

21.使用SimplejdbcTemplate和泛型技术简化代码

(1).

public class SimpleJdbcTemplateTest{

static SimpleJdbcTemplate simple = newSimpleJdbcTemplate(JdbcUtils.getDataSource());

static T find(String nameClass clazz){

String sql = "selectid,name,money,birthday from user where name=? and money=?";

User user =

simple.queryForObject(sql,ParameterizedBeanPropertyRowMapper.newInstance(User.class),name,100f);

}//使用了可变参数功能,没有使用了参数数组,参数Map;使用泛型,将查询的类型也当作是参数传递过来.

}

(2).它的内部也是包装了NamedParameterJdbcOperations类,当将对象可变参数变成数组后,剩下的工做都交给NamedParameterJdbcOperations类作.

simple.getNamedParameterJdbcOperations()获取NamedParameterJdbcOperations对象

simple.getJdbcOperations()获取JdbcTemplate对象

22.使用策略模式对模板方法设计模式进行改进

(1).对于不一样的查询语句,返回的结果集可能不一样,只要一个name,可是把全部的信息都查询出来了,这就要求不一样的映射结果.

public StringfindUserName(int id){

Stringsql="select name from user where id=?";

Object[]args=newObject[]{id};

}

protected ObjectrowMapper(ResultSet rs){//重新覆盖rowMapper方法

returnrs.getString("name");

}

这种方式可能致使有多少条不一样的查询语句,就须要覆盖多少次rowMapper方法.

(2).java中是不容许传递方法的,可是能够传递一个类,接口

根据不一样的sql中的内容,查询的列不一样,如:

select name fromuser:能够获得name一列

select id,namefrom user:能够获得id,name这两列

selectid,name,money from user:能够获得id,name,money这三列.

public classMyDaoTemplate{

public Objectfind(String sql,Object[]args,RowMapper rowMapper){

obj =rowMapper.mapRow(rs);//映射的过程由一个接口去作

}

}

public interfaceRowMapper{//定义一个行映射器接口

public ObjectmapRow(ResultSet rs);

}

public classUserDaoImpl2{

MyDaoTemplate template= new MyDaoTemplate();

public UserfindUser(String loginName,String password){

Stringsql="select id,name,money,birthday from user where name=?";

Object[] args =new Object[]{loginName};

Object user =this.template.find(sql,args,new UserRowMapper());

retrun (User)user;

}

}

classUserRowMapper implements RowMapper{

public ObjectmapRow(ResultSet rs){//行映射器

User user = newUser();

user.setId(rs.getInt("id"));

user.setName(rs.getString("name"));

user.setMoney(rs.getFloat("money"));

return user;

}

}

//当须要不一样的查询结果集,只需实现RowMapper接口就好了(能够使用匿名内部方式实现)

(3).这是一种策略模式,根据不一样的功能,调用不一样的方法(策略),实现类组合的方式(在UserDaoImpl2类中定义一个MyDaoTemplate类)实现的,模板模式是根据继承的方式实现的.

23.使用模板方法设计模式处理DAO中的查询方法

publc abstractclass AbstractDao{

public Object find(String sql,Object[]args){//相同的部分在父类中实现

Connectionconn=null;

PreparedStatementps=null;

ResultSet rs=null;

conn=JdbcUtils.getConnection();

ps=conn.prepareStatement(sql);

for(inti=0;i<args.length;i++){

ps.setObject(i+1,args[i]);

}

rs=ps.executQuery();

Object obj-null;

while(rs.next()){

obj=rowMapper(rs);

}

return obj;

}

abstract protectedObject rowMapper(ResultSet rs);//父类中不知道具体的查询结果集.放到子类实现该方法.

}

public classUserDaoImpl extends AbstractDao{

public UserfindUser(String loginName,String password){//不变的部分放到子类实现.

Stringsql="select id,name,money,birthday from user where name=?";

Object[] args =new Object[]{loginName};

Object user = super.find(sql,args);

return (User)user;

}

@Override

protected ObjectrowMapper(ResultSet rs){//在子类中知道查询结果有几列

User user=newUser();

user.setId(rs.getInt("id"));

user.setName(rs.getString("name"));

user.setMoney(rs.getFloat("money"));

user.setBirthday(rs.getDate("birthday"));

return user;

}

}

假设如今有一个帐户AccountDao

public classAccountDaoImpl extends AbstractDao{

public UserfindAccount(int id){//不变的部分放到子类实现.

Stringsql="select id,name,money from account where id=?";

Object[] args =new Object[]{id};

Object user =super.find(sql,args);

return(Account)account;

}

@Override

protected ObjectrowMapper(ResultSet rs){//在子类中知道查询结果有几列

Accountaccount=new Account();

account.setId(rs.getInt("id"));

account.setName(rs.getString("name"));

account.setMoney(rs.getFloat("money"));

return account;

}

}

public classAccount{

private int id;

private Stringname;

private floatmoney;

//get/set方法省略

}

模板模式,相同的步骤放到父类中,不一样的步骤放到子类中设计,service方法,doGet(),doPost()方法,首先调用service方法.service会根据method参数的值来调用doGet(),doPost()方法.

24.使用支持命名参数的JdbcTemplate

(1).Spring的NamedParameterJdbcTemplate

第一:NamedParameterJdbcTemplate内部包含了一个JdbcTemplate,因此JdbcTemplate能作的事情 NamedParameterJdbcTemplate都能干,NamedParameterJdbcTemplate相对于JdbcTemplate主 要增长了参数能够命名的功能

第二:public Object queryForObject(String sql,MapparamMap,RowMapper rowMapper)

第三:public Object queryForObject(Stringsql,SqlParameterSoruce paramSource,RowMapper rowMapper)

SqlParameterSource的两个主要实现MapSqlParameterSource和BeanPropertySqlParameterSource

第四:public int update(String sql,SqlParameterSourceparamSource,KeyHolder generatedKeyHolder)保存数据得到主键

(2).在传递参数时,须要将参数Object[]args与?占位符的位置对应好,若是对应错了,就会出现问题,这时,咱们就能够给占位符起个别名

staticNamedParameterJdbcTemplate named = new NamedParameterJdbcTemplate();

Stringsql="select id from user where name=:n and money>:m andid<:id";

Map params=newHashMap();//使用Map存放参数,而不是数组了

params.put("n",user.getName());

params.put("m",user.getMoney());

params.put("id",user.getId());

/Object[]args=new Object[]{user.getName(),user.getMoney(),user.getId()};/

Object u =named.queryForObject(sql,params,new BeanPropertyRowMapper(),User.class));

//注意sql的书写,将占位符?替换了,注意替换的规则.NamedParameterJdbcTemplate只干了一件事,就是将占位符?替换成变量名,将参数命名话后,以后的操做都会交给JdbcTemplate处理.

(3).为何要使用命名参数:

SqlParameterSourceps = new BeanPropertySqlParameterSource(user);//关于user的bean参数源

Stringsql="select id from user where name=:name and money>:money andid<:id";

Object u =named.queryForObject(sql,ps,new BeanPropertyRowMapper(),User.class));

这时参数就存放在user参数源中,参数名必须和user的属性名同样,将参数封装成一个类(参数源),符合面向对象设计思想

(4).保存数据,拿到记录的主键.当主键是符合类型(就是多列组成),也多是String类型的.

static voidaddUser(User user){

String sql ="insert into user(name,birthday,money) value(:name,:birthday,:money);

SqlParameterSourceps = new BeanPropertySqlParameterSource(user);//关于user的bean参数源

KeyHolderkeyHolder = new GeneratedKeyHolder();

named.update(sql,ps,keyHolder);

//插入的记录的主键放到keyHoler中

int id =keyHolder.getKey().inValue();

user.setId(id);

Map map =keyHolder.getKeys();//主键由多列组成的时候

}//重点

25.事务的保存点处理

(1).当事务进行回滚时,不是所有进行回滚,有时只想回滚一部分的操做,

(2).Savepoint sp=null;

sp=conn.setSavepoint();//设置保存点

if(conn!=null&&sp!=null){

conn.rollback(sp);//将保存点当作参数,只回滚到保存点

}

26.事务的概念与JDBC事务处理

(1).事务的特性:(ACID)

原子性(atomicity):组成事务处理的语句造成了一个逻辑单元,不能只执行其中的一部分

一致性(consistency):在事务处理执行先后,数据库是一致的(数据库数据完整性约束)

隔离性(isolcation):一个事务处理对另外一个事务处理的影响持久性(durability):事务处理的效果可以被永久保存下来

(2).connection.setAutoCommit(false)//打开事务

connection.commit();//提交事务

connection.rollback();//回滚事务

(3).查看数据库表的引擎是否支持事务的操做

27.事务的隔离级别

(1).当两个事务同时去操做同一个数据源,这就是隔离性

(2).设置隔离级别:connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

隔离级别:

读未提交:可能出现脏读,不可重复度,幻度

读已提交:不可能脏读,可能出现不可重复读,幻读

可重复读:不可能脏读,不可重复读,可能出现幻读

可串行化:不可能脏读,不可重复读,幻读

级别逐渐提升.当级别越高的时候,须要的资源越多,对并发的操做有影响.

脏读:别人的数据没有提交,我就读到了.

不可重复读:第一次读和第二次读的结果不一样

幻读:当咱们正在查询id>10的记录,这时有另一个事务增长一条正好id>10的记录.

(3).隔离级别的缺省值和数据库相关.不一样数据库拥有的隔离级别的个数也是不一样的.

28.数据库的元数据信息

(1).能够获得数据库的相关信息,是否支持事务,数据库的名称,版本号,隔离级别等信息.

Connectionconn=JdbcUtils.getConnection();

DatabaseMetaDatadbmd =conn.getMetaData()//返回数据的元信息

dbmd.getDatabaseProductName();//获得数据库的名称

(2).hibernate支持各类数据库,因此它确定须要知道全部数据库的相关信息.

29.经过代理模式来保持用户关闭链接的习惯

(1).用户可能不使用JdbcUtils.free()方法释放链接,而是按照conn.close()方法释放链接,这时咱们建立的链接池就没有用了,链接数也就减小了,因此咱们但愿用户始终使用咱们本身编写的方法进行释放链接

(2).经过close()方法,仍是可以将链接方法链接池中,因此咱们要拦截close()方法,组合优先继承

(3).public classMyConnection implements Connection{

private ConnectionrealConnection;//使用组合方式

privateMyDataSource dataSource;

MyConnection(ConnectionrealConnection,MyDataSource dataSource){

this.realConnection=connection;

this.dataSource=dataSource;

}

//实现Connection的全部方法

public voidclose(){//这里就能够实现Connection的close()方法了

this.dataSource.connectionPool.addLast(this);//把本身从新放到池中.

}

(4).此时代码中的Connection处都使用MyConnection,这就是面向接口编程的好处.同时类MyConnection的访问权限是包访问权限,不许用户访问的,可是容许在dataSource中访问.

(5).DataSource类和MyConnection类之间相互调用.

(6).MyConnection是个代理,是Connection的代理模式,实现Connection的close()方法.这就是静态代理设计模式,在MyConnection类中定义一个Connection,这是组合方式,也能够使用集成方式实现代理.

30.完成数据库的CRUD操做

(1).书写SQL语句时应该注意的问题:select * from user,就是不该该写星号,最好书写列名,获得数据,能够根据列的索引号,也能够根据列名,建议使用根据列名取数据.

31.用jdbc访问大段文本数据

(1).数据库中的varchar最大是255个字节,因此就须要使用大文本类型TEXT.只有纯文本格式才能放进去.

(2).

Stringsql="insert into clob_test(big_text) value(?)";

ps=conn.prepareState(sql);

File file=newFile("src/cn/itcast/jdbc/JdbcUtils.java");

Reader reader =new BufferedReader(new FileReader(file));//可能含有IO的异常

ps.setAsciiStream(1,reader,(int)file.length());//须要一个Reader,字符流的长度Length,这个方法只能用于文本只含有Ascii码的

inti=ps.executeUpdate(sql);

reader.close();

rs=st.executeQuery("selectbig_text from clob_test");//读取文本类型数据

while(rs.net()){

Clob clob =rs.getClob(1);

Reader reader =clob.getCharacterStream();

File file=newFile("JdbUtils_bak.java");

Writer writer=newBufferedWriter(new FileWriter(file));

char[]buff=newchar[1024];

for(inti=0;(i=reader.read(buff))>0;){

writer.write(buff,0,i);

}

}

writer.close();

reader.close();

7、 iBaits

优势:

  1. ibatis把sql语句从Java源程序中独立出来,放在单独的XML文件中编写,给程序的维护带来了很大便利。

  2. ibatis封装了底层JDBC API的调用细节,并能自动将结果集转换成Java Bean对象,大大简化了Java数据库编程的重复工做。

  3. 简单易于学习,易于使用,很是实用。

  4. 由于Ibatis须要程序员本身去编写sql语句,程序员能够结合数据库自身的特色灵活控制sql语句,所以可以实现比hibernate等全自动orm框架更高的查询效率,可以完成复杂查询。

  5. 阿里巴巴、慧点科技等多家知名软件公司都使用Ibatis。

缺点:

1.CRUD的操做只能带一个参数

2.和Hibernate相比,须要编写Sql语句,可是Hibernate不须要编写Sql语句

相关文章
相关标签/搜索