Servlet:
1)servlet : servlet是一个特殊的java程序,需要在web服务器上运行,并接收和响应客户端的请求,遵循http协议.
2)Servlet;作用: 主要用于控制层.
3)Servlet;实现方式:
3.1实现接口servlet,复写所有的方法;
3.2在web.xml文件中配置servlet内容.
>>在web.xml文件中的配置,目的是通知Web服务器,Servlet的存在和访问路径
>>程序员写的每一个Servlet程序(普通类实现了Servlet接口)被Web服务器调用,叫反调,即main方法在Web服务中
实现servlet的三种方式:
1)类implementServlet接口,复写其中的方法
2)类extendsGenericServlet类
public class GenericServlet extends HttpServlet {
public void doGet(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
//获取字符输出流对象
PrintWriter pw = response.getWriter();
//将数据输出到浏览器
pw.write("<h1>servlet first methodgenericServlet</h1>");
}
public void doPost(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
}
}
3)类extendsHttpServlet类
HttpServlet指能够处理HTTP请求的servlet,它在原有Servlet接口上添加了一些与HTTP协议处理方法,它比Servlet接口的功能更为强大。因此开发人员在编写Servlet时,通常应继承这个类,而避免直接去实现Servlet接口。
HttpServlet在实现Servlet接口时,覆写了service方法,该方法体内的代码会自动判断用户的请求方式,如为GET请求,则调用HttpServlet的doGet方法,如为Post请求,则调用doPost方法。因此,开发人员在编写Servlet时,通常只需要覆写doGet或doPost方法,而不要去覆写service方法。
由于客户端是通过URL地址访问web服务器中的资源,所以Servlet程序若想被外界访问,必须把servlet程序映射到一个URL地址上,这个工作在web.xml文件中使用<servlet>元素和<servlet-mapping>元素完成。
<servlet>元素用于注册Servlet,它包含有两个主要的子元素:<servlet-name>和<servlet-class>,分别用于设置Servlet的注册名称和Servlet的完整类名。
一个<servlet-mapping>元素用于映射一个已注册的Servlet的一个对外访问路径,它包含有两个子元素:<servlet-name>和<url-pattern>,分别用于指定Servlet的注册名称和Servlet的对外访问路径。
例: <servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>cn.web.servlet.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/MyServlet</url-pattern>
</servlet-mapping>
*3 Servlet生命周期和工作原理
1)工作原理
默认情况下,在用户第一次访问时
空参构造方法():1次
init()方法():1次
doGet()/doPost()每次请求都执行一次:n次
当Web应用重新部署/加载到Web服务器时,Web服务器移除原Web应用,此时调用destory()方法:1次.每次请求,都由同一个Servlet来服务,该Servlet是一个单例模式
2)工作原理
*1)应用能过URL访问某个Servlet,Web服务端根据URL中在web.xml文件中查询是有对应的<url-pattern/>存在,如果没有,Web服务器返回404页面;如果有,则进入2)。
*2)Web服务器在Servlet实例缓存池中根据<url-pattern/>这个键,查询对应的Servlet实例,如果找到了,则进入3)。如要没找到,则进入4)。
*3)Web服务端,调用该Servlet中的doGet()或doPost()或service(),创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。这个实现对象与具体Web服务器有关,只需要基于接口编程
*4)Web服务器解析web.xml文件,通过反射方式创建该Servlet实例,初始化Servlet实例,然后将该Servlet实例,放入Servlet实例缓存池中,由Web服务器维护,以备下次重用,(这也是为什么Servlet是单例的原因)
5)执行doGet()或doPost()或service()方法中的业务操作,执行完后,返回Web服务器
6)当Web服务器收到返回请求后,将返回数据取出,形成html网页,发送到浏览器
7)当Web服务器决定销毁该Servlet时(重新部署Web应用或停止Web服务器或者设置servlt的存活时间),会在真正销毁前,调用该Servlet实例的destory()方法
*4 Servlet细节
1)Servlet的访问路径,只是一个以/杠开头的字符串而以,不能以扩展判段是一个什么资源,当静态资源和动态资源访问URL一至的情况下,动态优先
2)一个Servlet可以映射到多个URL路径上,至少一个.
访问URL的路径只有二种:
*.扩展名
/*,*表示通配符
3)多个Servlet访问,按照最像原则,有扩展名的最后考虑
4)程序员写的Servlet,是被Web服务器调用, 主要愿因是:Servlet是一个供其他Java程序(Servlet引擎)调用的Java类,它不能独立运行,它的运行完全由Servlet引擎来控制和调度
5)<load-on-startup/>可以让Servlet在第一次访问之间创建并初始化
但数字最小为0,如果写成负数,相当于不用<load-on-starup/>标签,在用户首次访问该Servlet时创建并初始
举例:
<servlet>
<servlet-name>invoker</servlet-name>
<servlet-class>
org.apache.catalina.servlets.InvokerServlet
</servlet-class>
<load-on-startup>2</load-on-startup>数值小的先加载/正整数
</servlet>
用途:为web应用写一个InitServlet,这个servlet配置为启动时装载,为整个web应用创建必要的数据库表和数据。
6)当用户的URL访问路径在web.xml的所有servlet中不存在时,由这个url-pattern为/的缺省servlet来处理
其实Web服务器已有一个url-pattern为/的缺省Servlet,它叫DefaultServlet,像404页面/a.jpg/post.html,等都是由这个缺省的Servlet来处理的
7)Servlet中线程安全问题:
当多个客户端并发访问同一个Servlet时,web服务器会为每一个客户端的访问请求创建一个线程,并在这个线程上调用Servlet的service方法,因此service方法内如果访问了同一个资源的话,就有可能引发线程安全问题。
8)构成线程安全的三要素
>>单例
>>实例变量
>>对实例变量更新(关键)
5 ServletConfig对象
1)用Servlet读取在web.xml文件中,为该Servlet配置的参数,注意,针对的是该Servlet
2)Web服务器会创建实现ServletConfig接口的实现类传入,该接口主要用于读取配置文件中的数据.
在Servlet的配置文件中,可以使用一个或多个<init-param>标签为servlet配置一些初始化参数。
当servlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,将ServletConfig对象传递给servlet。进而,程序员通过ServletConfig对象就可以得到当前servlet的初始化参数信息。
3)常用的API:
getInitParameter(String参数):String返回值
getInitParameterNames():Enumeration枚举
<!--作用域整个web.xml -->
<context-param>
<param-name>filePath</param-name>
<param-value>/file.properties</param-value>
</context-param>
<servlet>
<servlet-name>Demo5</servlet-name>
<servlet-class>cn.itcast.web.servlet.Demo5</servlet-class>
<!-- 以下初始化参数只能由Demo5访问 -->
<init-param>
<param-name>email</param-name>
<param-value>jack2012@163.com</param-value>
</init-param>
<init-param>
<param-name>tel</param-name>
<param-value>13512344321</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
public classDemo5 extends HttpServlet {
//读取web.xml文件中的内容
public void init(ServletConfigservletConfig) throws ServletException {
//取得初始化参数
Enumeration<String> enums =servletConfig.getInitParameterNames();
//迭代枚举
while(enums.hasMoreElements()){
//取得键
String key =enums.nextElement();
//取得值
String value =servletConfig.getInitParameter(key);
//显示
System.out.println(key +"<-->" + value);
}
System.out.println("Servlet实例名为:" + servletConfig.getServletName());
String email =servletConfig.getInitParameter("email");
String tel =servletConfig.getInitParameter("tel");
//判段
if(email!=null &&tel!=null){
System.out.println("邮箱:" + email);
System.out.println("电话:" + tel);
}else{
System.out.println("查无此信息");
}
}
}
*1 ServletContext对象
1)当某个Web应用部署到Web服务器中,Web服务器为该Web应用创建一唯一的ServletContext对象
2)Web应用和ServletContext对象一一对应
3)Web应用中的所有Servlet都有访问或共享该ServletContext对象中的信息
4)如果要在多个Servlet之间共享数据,此时可以使用ServletContext
5)常用的API
setAttribute(String,Object):void
getAttribute(String):Object
//获取文件的路径名
String path = this.getServletContext().getRealPath("/1231321.jpg");
//D:\apache-tomcat-6.0.33\webapps\WebTest\1231321.jpg
6)浏览器以GET方式发送请求,在服务端为转发的情况下,此时服务端无需通知浏览器,是服务端内部行为,所以依然是GET方式的请求,此时的request和response与先前的一样
7)可以通过ServletContext读取位于web应用中的所有资源文件
8)可以通过ServletContext读取位于web.xml文件中的初始化参数,该参数针对整个web应用,而不是针对某个servlet。
例: 通过ServletContex读取配置文件中的所有信息
public class MyContextServlet extends HttpServlet {
public void doGet(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
//创建ServletContext对象
ServletContext context = this.getServletContext();
//获取web.xml文件中的参数filePath 对应的路径
String filePath = context.getInitParameter("filePath");
//获取输入流对象,该方法适用于整个web.xml文件中
InputStream inStream = context.getResourceAsStream(filePath);
//创建Properties对象
Properties p = new Properties();
//让Properties对象与文件关联
p.load(inStream);
//读取该文件中的键.并通过键查找对应的值.
String name = p.getProperty("name");
String password = p.getProperty("password");
String role = p.getProperty("role");
//设置属性,并存储到域对象中
context.setAttribute("name", name);
context.setAttribute("password",password);
context.setAttribute("role", role);
//获取RequestDispatcher(传发)对象,此处"/"表示当前的目录day06
RequestDispatcher disp = context.getRequestDispatcher("/MyContext");
//转发至MyContext(302转向)
disp.forward(request, response);
}
}
public class MyContext extends HttpServlet {
public void doGet(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
//创建ServletContext对象
ServletContext context = this.getServletContext();
//获取指定的属相键对应的value值
String name = (String)context.getAttribute("name");
String password =(String)context.getAttribute("password");
String role = (String) context.getAttribute("role");
response.getWriter().write(name+": "+password+" ,"+role);
}
}
*2 缓存应用
1)在默认情况下,浏览器访问某个静态资源时,如果浏览器的缓存有该资源,则找缓存;如果无该资源,则首次找服务端,之后将其缓存,以提高效率。
1 设置缓存的两种场景:
场景一:对于不经常变化的数据,在servlet中可以为其设置合理的缓存时间值,以避免浏览器频繁向服务器发送请求,提升服务器的性能。
场景二:如果要实现一种高级功能,即客户端请求动态web资源时,动态web资源发现发给客户端的数据更新了,就给客户端发送最新的数据,如果发现数据没有更新,则动态web资源就要客户端就去访问它自己缓存的数据。此种情况可以通过覆写动态web资源(即servlet)的getLastModify方法予以实现。
2)服务端和浏览器能过If-Modified-Since和Last-Modified二个头相互通信,来判段请求资源的新旧,这就是底层原理。
13 getLastModified方法由service方法调用,默认情况下,getLastModified方法返回一个负数,开发人员在编写servlet时,如果不覆盖getLastModified方法,则每次访问servlet时,service方法发现getLastModified方法返回负数,它就会调用doXXX方法向客户端返回最新的数据。此种情况下,服务器在向客户端返回doXXX方法返回的数据时,不会在数据上加Last-Modified头字段。
8 如果编写servlet时,覆盖了getLastModified方法,并返回某一个时间值,则客户端访问Servlet时,service方法首先会检查客户端是否通过If-Modified-Since头字段带一个时间值过来。如果没有的话,则service方法会调用doXXX方法向客户端返回最新的数据。在返回数据时,service方法还会调用getLastModified方法得到一个时间值,并以这个时间值在数据上加上一个Last-Modified头字段。(即通知客户端缓存数据)
13 客户端在访问servlet时,如果通过If-Modified-Since头字段带了一个时间值过来,则service方法在调用doXXX方法之前,它会先调用getLastModified方法,得到一个时间值,并与客户端带过来的时间值进行比较,如果比客户端的时间值要新,则service方法调用doXXX方法向客户端返回最新的数据。如果要旧,则service方法而不会调用doXXX方法向客户端返回数据,而是返回一个304的状态码给客户端,通知客户端在拿它缓存中的数据。
例1:设置资源文件的修改时间
public class LastModified extends HttpServlet {
protected longgetLastModified(HttpServletRequest req) {
ServletContext context = this.getServletContext();
//获取文件路径
String filepath = context.getRealPath("/temp.html");
//设置资源文件最后修改时间
return newFile(filepath).lastModified();
}
public void doGet(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
ServletContext context = this.getServletContext();
//读取temp.html文件中的内容输到浏览器
InputStream in = context.getResourceAsStream("/temp.html");
ServletOutputStream out =response.getOutputStream();
byte[] buf = new byte[1024];
int len = 0;
while((len = in.read(buf))>0)
{
out.write(buf, 0, len);
}
}
}
*3 response的应用
1)用字节方式输出[数字],该数字需要查询ISO8859-1编码表,将查询后的结构取出,存放响应体中
2)用字节方式输出[字符串],则直接将该字符串存放响应体中
3)用字符方式输出[数字],则直接将该字数字存放响应体中,原样输出
4)以采用二种方式设置浏览器解析的编码方式时,后者起决定作用
>>response.setCharacterEncoding("GB2312");
>>response.setContentType("text/html;charset=UTF-8");
>>//重定向302请求的资源被修改了,需要重新访问新的地址
response.sendRedirect("/myday06/Demo1");/斜杠代表当前web工程/myday06
5)response.setContentType("text/html;charset=UTF-8");位置很重要,在默认情况下,response.getWriter()返回的PrintWriter对象,采用ISO8859-1的方式进行编码,即存入到PrintWriter对象中的一切数据都要采用ISO8859-1方式进行编码
6)字节 vs 字符
>>输出字符串(不论中文或英文),此时使用字符(response.getWriter())
>>输出多媒体数据(图片), 此时使用字节(response.getOutputSteram())
4 response的细节
1)转发:/表示day06(虚拟路径)
重定向:/表示webapps
表单提交的action属性:/表示webapps
<a href=""/>,/表示webapps
2)可以采用字符或字节方式输出中文数据
3)response对象只能使用一种方式进行输出,要么字节,要么字符,不能同时使用
4)response对象不关闭流也没关系,由web服务器在适当的时候关闭;如果你想立即关闭,建议手工关闭
5)在重定向或转发的情况下,设置的请求头,后者起决定作用
案例1:图片下载: 中文图片名字的转换: URLEncoder.encode(file.getName(),"UTF-8")
public class Dowload extends HttpServlet {
public void doGet(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
//获取ServletContext对象
ServletContext context = this.getServletContext();
//获取图片路径
String url = context.getRealPath("/123.jpg");
File file = new File(url);
response.setHeader("content-disposition","attachment;filename="+ URLEncoder.encode(file.getName(),"UTF-8"));
InputStream in = new FileInputStream(file);
OutputStream out = response.getOutputStream();
byte[] by = new byte[1024];
int len = 0;
while((len=in.read(by))>0){
out.write(by,0,len);
}
}
}
案例2:生成一个带验证码图片的表单:
public class CheckOutCode extends HttpServlet {
//随机生成校验码
public void doGet(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
//设置禁止浏览器进行页面缓存
response.setHeader("expires","-1");
response.setHeader("cache","no-cache");
response.setHeader("pragram","no-cache");
//在内存中创建一个图片对象,并指定其x,y轴的坐标,并指定图片的类型
BufferedImage img = new BufferedImage(70,30,BufferedImage.TYPE_INT_RGB);
//获取画图的对象
Graphics g = img.getGraphics();
Random r = new Random();
for(int x=0;x<10;x++)
{
//随机生成一些数字
int x1 = r.nextInt(100);
int x2 = r.nextInt(100);
int y1 = r.nextInt(30);
int y2 = r.nextInt(30);
g.setColor(new Color(r.nextInt(256),r.nextInt(256),r.nextInt(256)) );
//在此图片上下文的坐标系中,使用当前颜色在点 (x1, y1)和(x2, y2) 之间画一条线。
g.drawLine(x1, y1, x2,y2);
}
g.setColor(Color.yellow);
//使用此图形上下文的当前字体和颜色绘制由指定 string给定的文本
g.drawString(getRandom(), 20, 20);
//图片输出流,写会浏览器
ImageIO.write(img,"JPG",response.getOutputStream());
}
//随机生成一个0-999的随机数字
public String getRandom()
{
return (int)Math.ceil((Math.random()*10000))+"";
}
}
例3: 设置静态资源的缓存时间:
public class Expires extends HttpServlet {
public void doGet(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
//获取静态资源的uri路径
String uri = request.getRequestURI();
int end = uri.indexOf("/");
String path = uri.substring(end);
//创建ServletContext对象,加载静态资源
ServletContext context = this.getServletContext();
//获取输入流对象
InputStream input=context.getResourceAsStream(path);
BufferedReader bufr = new BufferedReader(new InputStreamReader(input));
String line = bufr.readLine();
if(line!=null && line.contains("html"))
{ //设置临时文件的存储时间
long time = System.currentTimeMillis()*1*24*60*60*1000;
response.setDateHeader("expires",time);
}
}
}
例4:设置请求头referer
public class RefererServlet extends HttpServlet {
public void doGet(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
//获取请求头(地址栏,主要作用是防止用户在通过url访问时间接访问)
String referer =request.getHeader("referer");
if(referer!=null && referer.equals("http://localhost:8080/myday07/page.html"))
{//转发至success.html
this.getServletContext().getRequestDispatcher("/success.html").forward(request, response);
}else{
this.getServletContext().getRequestDispatcher("/failure.html").forward(request, response);
}
}
}
*1 request对象常用API
//创建每个浏览器对应的HttpSession
HttpSessionsession = request.getSession();
获取每个浏览器对应的HttpSession的编号
String id =session.getId();
String ip =request.getRemoteAddr();///获取每个浏览器的IP////127.0.0.1
String method =request.getMethod();//取得客户端提交请求的方式//get,post
取得客户端提交请求的URL和URI //http://localhost:8080/myday07/ResquestMethod
String url =request.getRequestURL().toString();
String uri = request.getRequestURI();//myday07/ResquestMethod
String path =request.getContextPath();//取得客户端访问的虚拟路径,即/day07
String query =request.getQueryString();//取得客户端传入的参数(查询字符串)
String username= request.getParameter("username");//取得客户端传入的值单个值
String [] likes= request.getParameterValues("like");//取得客户端传入的值多个值
//从HttpSession域对象中取得IP/ID/用户名
String id =(String) session.getAttribute("id");
String ip =(String) session.getAttribute("ip");
获得客户机请求头信息:
getHeader(stringname)方法:String
String encoding = request.getHeader("Accept-Encoding");
String agent = request.getHeader("user-agent");
获得客户机请求体信息(客户端提交的数据)
getParameterNames方法+BeanUtils框架
*2 基于MVC的request应用
request对象也能实现请求转发:请求转发指一个web资源收到客户端请求后,通知服务器去调用另外一个web资源进行处理。
请求转发的应用场景:MVC设计模式
request对象也提供了一个getRequestDispatcher方法,该方法返回一个RequestDispatcher对象,调用这个对象的forward方法可以实现请求转发。
request对象同时也是一个域对象,开发人员通过request对象在实现转发时,把数据通过request对象带给其它web资源处理。
setAttribute方法 getAttribute方法
//转发307
ServletContext context = this.getServletContext();
RequestDispatcher rd =context.getRequestDispatcher("/Demo1");
rd.forward(request, response);
1)POST请求
>>1个中文----3个%----按照UTF-8编码----------浏览器
>>1个中文----2个%----按照GB2312或GBK编码----浏览器
2)HTTP协议特点所决定在HTTP协议中只能明文传递英文和数字,其它字符都需要按照一定方式编码
3)request.getParameter("username"),默认情况下,采用ISO的方式取得username的值,必须在该代码[之前]使用request.setCharacterEncoding("UTF-8")方式,设置request采用UTF-8取得表单参数的值
4)编码和解码的方式要一致
5)request.setCharacterEncoding只能针对[请求体post]中的数据有效,对GET无效
6)GET请求
>>一定要对URL的字符进行编码,比如UTF-8
>>在接收方,一定要对URL字符进行手工解码,比如UTF-8
>>编码和解码双方要一致
doget() 请求:
dopost() 请求:
7)请求和转发的区别:
一个web资源(servlet实例对象)收到客户端请求后,通知[服务器]去调用另外一个web资源进行处理,称之为请求转发。
一个web资源(servlet实例对象)收到客户端请求后,通知[浏览器]去访问另外一个web资源进行处理,称之为请求重定向。
*3 基于request的转发
>>request请求对象
MVC是(模型-视图-控制器)的一种简称,主要用于一种架构设计模式,MVC是独立于任何语言
MVC三层结构的内部关系
MVC把应用程序的开发分为3个层面:视图层、控制层和模型层。其中视图层负责从用户获取数据和向用户展示数据,在这层中不负责对业务逻辑的处理和数据流程的控制。
而模型层负责处理业务逻辑和数据库的底层操作,其中视图层和模型层之间没有直接的联系。控制层主要负责处理视图层的交互,控制层从视图层接受请求,然后从模型层取出
对请求额处理结果,并把结果返回给视图层。在控制层中只负责数据的流向,并不涉及具体的业务逻辑处理。
从图中可以看出,Servlet在MVC开发模式中承担着重要的巨额收入。在MVC结构中,控制层就是依靠Servlet实现的,Servlet可以从流量拿起端接受请求,然后从模型层取出处理结果,并且把处理结果返回给浏览器端的用户。在整个结构中,Servlet负责数据流向控制的功能。
虽然现在利用很多开源框架都能很好地实现MVC的开发模式,且这些开源框架对MVC的实现都是非常出色的,但是在这些框架中处理数据控制流向的时候,采用的还是servlet。
request也具有转发功能
request.getRequestDispatcher().forward(request,response);
>>request域对象
因为该request对象有对应的存取方式
setAttribute()和getAttribute()
405错误:某个URL请求是GET/POST,但对应的Servlet没有提交doGet()/doPost()方式,
在转发情况下,request域对象,二个Servlet的数据共享
在重定向情况下,request域对象,二个Servlet的数据不共享,
>>什么时候用ServletContext域对象,什么时候用HttpServletRequest域对象
ServletContext:
(1)当需要保存的内容在重定向的情况下依然存在,比较适合使用ServletContext域对象
(2)当需要保存的内容时间长时,比较适合使用ServletContext域对象
(3)公共聊天室,就应该使用ServletContext进行保存
HttpServletRequest:
(1)当需要保存的内容在转发的情况下依然存在,[比较适合]使用HttpServletRequest域对象,但此时如果使用, ServletContext也是可以的
(2)当需要保存的内容只要在一次请求中可见,[比较适合]使用HttpServletRequest域对象
(3)查询某个新闻信息,就应该使用HttpServletRequest进行保存
#4 转发 vs 重定向
(1)URL地址拦
转发: 地址拦不变
重定向: 地址拦变化
(2)代码
转发:
>>this.getServletContext().getRequestDispatcher(html/jsp/servlet).foward(*,*);
>>request.getRequestDispatcher(html/jsp/servlet).foward(*,*);
重定向:
>>response.sendRedirect("html/jsp/servlet");
(3)同一个Web还是在不同的Web应用中
转发:只能在当前Web应用中导航
重定向:可以在当前Web应用中,或非当前Web应用中导航,也可以导航外网
转发: 能访问WEB-INF/目录的所有资源
重定向:不能访问WEB-INF/目录的所有资源
(4)/的含义
转发:/表示当前web应用名,虚拟路径,即/day07
重定向:/表示webapps
(5)内部或外部行为
转发:服务器内部行为
重定向:服务器与浏览器的外部行为
(6)request的数据共享能力
转发:reques共享数据
重定向:request不共享数据
5 include包含方法:
RequestDispatcher.include方法用于将RequestDispatcher对象封装的资源内容作为当前响应内容的一部分包含进
来,从实现可编程的服务器端包含功能。被包含的Servlet程序不能改变响应消息的状态码和响应头,如果它
里面存在这样的语句 这些语句的执行结果将被忽略。
1)一定在要IncludeServlet中设置response的输出编码方式,不能在只在所包含的Servlet中,因为作用域的问题
2)先输出的,决定输出编码方式,默认为ISO
3)如果在IncludeServlet中设置response的输出编码方式,被包含的Servlet无需设置编码方式
4)最后编码方式由IncludeServlet决定
COOKE入门
什么是cookie: Cookie是客户端技术,服务器把每个用户的数据以cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。这样,web资源处理的就是用户各自的数据了。
1)cookie是存于浏览器缓存中的数据集合
2)能够解决多个web应用之间或同一个web应用多个动态资源之间的数据交互,例如:day08web应用和day09web应用之间交互数据
3)用户打开浏览器,在该浏览器中的一系列超链接操作后,再关闭该浏览器的过程,叫会话
4)浏览器和服务器交互的过程中,是通过响应头set-cookie和请求头cookik来完成整个会话交互数据的
2 cookie常用api
>>new Cookie("名","值")://构造函数
>>cookie.setMaxAge(秒钟);//设置cookie的存活时间
>>response.addCookie(cookie);//将cookie写入到浏览器中
>>request.getCookies()//获取浏览器所有的cookie,返回的是一个数组cookie[]
>>cookie.getName()//获取cookie的名字
>>cookie.getValue()//获取cookie的值
>>cookie.setPath("/day08-2")//设置cookie的路径
*3 cookie的细节
1)cookie的更新就是再向浏览器写入一个与原cookie名字一样的cookie,后者替换前者;如果cookie的名字不一样,则向浏览器添加新的cookie。
2)cookie产生于服务端,存于浏览器,可以人工删除
3)cookie的存与不存,可以由浏览器自已设置,服务端无法管理是否将cookie存于浏览器
4)一个cookie只能存放一种类型的信息,如果要存多个信息,得用多个cookie完成
5)一个浏览器能够存储多个网站的cookie信息,每个cookie的数量和大小受限,浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB
6)如果不设置cookie的有效存活时间,默认cookie只在内存中有效,当程序结束后,内存中的cookie销毁,不会写入浏览器的缓存中,因此,需要为cookie设置一个有效的时间,当程序运行完毕后,会将该cookie写入浏览器的缓存中,即硬盘中保留。如果cookie的存活时间为0,就相当于浏览器将删除cookie删除或无效
7)在默认情况下,day08web应用的cookie只能被day08web应用的动态资源访问,如果要让其它的web资源访问,得设置cookie.setPath("/day08-2"),默认cookie的path为当前web应用。
8)cookie是以文本文件的方式存于浏览器缓存中,而且里面的内容明码.
例: cookie的使用
public class ClientCount extends HttpServlet {
public void doGet(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
// 通知浏览器以UTF-8的方式解码
response.setContentType("text/html;charset=UTF-8");
// 获取浏览器所有的Cookie
Cookie[] cookie = request.getCookies();
// 获取printWrite流对象
PrintWriter pw = response.getWriter();
Cookie newCookie = null;
// 判断是否为空
if (cookie != null && cookie.length > 0) {
for (Cookie c : cookie) {
if (c.getName().contains("counter")) {
newCookie = c;
break;
}
}
if (newCookie != null) {
String value = newCookie.getValue();
int num = Integer.parseInt(value);
// 创建Cookie对象
Cookie cookies = new Cookie("counter", (num++) +"");
// 设置cookies的存活时间
cookies.setMaxAge(1 * 24 * 60 * 60);
// 将cookies写入浏览器中
response.addCookie(cookies);
pw.write("当前在线" + num +"人");
}
} else { // 创建Cookie对象
Cookie cookies = new Cookie("counter","1");
// 设置cookies的存活时间
cookies.setMaxAge(1 * 24 * 60 * 60);
// 将cookies写入浏览器中
response.addCookie(cookies);
pw.write("当前在线1人");
}
}
}
4 httpSession入门
1)session是服务端创建的一个为每个浏览器所[独享]的容器对象,存在服务端。
2)如果项目中需要多个动态资源找到同一个浏览器所需要的数据,例如:购物车
浏览器禁用cookie后的session处理
解决方案:URL重写
String url = response. encodeRedirectURL(java.lang.String url)
response. sendRedirect(url)
用于对sendRedirect方法后的url地址进行重写。
String url = response.encodeURL(url);
pw.write("<a href='"+url+"'>继续购物</a>");
用于对表单action和超链接的url地址进行重写
注:
session的失效(默认30分钟)
web.xml文件配置session失效时间
5 httpSession常用api
>>request.getSession()//获取所有的HttpSesseion对象
>>session.getId()
>>session.setAttribute(String,Object)
>>session.getAttribute(String):Object
*6 httpSession细节(上)
1)IE高版本浏览器,需要打开浏览器-[文件]-[新建会话],这样才能启动一个新会话,否则依然是原会话
2)context和session和request域对象的比较
>>在哪个域对象中放的数据,只能在哪个域对象中取
>>request只能在转发情况下,共享数据;重定向不共享数据
session在转发或重定向情况下,都共享数据;
context在转发或重定向情况下,都共享数据;
>>session创建的条件如下三个,缺一不可
(1)打开浏览器
(2)访问某一个Servlet
(3)request.getSession(),必须要用执行这个API
>>session销毁
>>当重新部署web应用
>>停此服务器
>>多个动态资源需要共享[同一个浏览器]的数据,可以使用HttpSession
一次HTTP请求中需要共享数据,但下一次请求无需共享上一次数据,可以使用HttpServletRequest
一个web应用中的所有动态资源都需要共享[所有浏览器]的数据时,可以使用ServletContext
>>购物车:HttpSession
公共聊天室:ServletContext
显示某条新闻的信息: HttpServletRequest
3)request.getSession()具有二层含义:
>>创建(无-有)
>>获取(有-有),关键是看代码流程
4)session底层是用cookie技术,通过响应头set-cookie和请求头cookie来维护浏览器与服务端的数据交互,以至于不会出错,完全由服务端负责维护,与浏览器无关
5)cookie和session的比较
>>cookie创建于服务端,存于浏览器;session创建于服务端,存于服务端
>>cookie是有文本文件存入相关的数据并且存入的数据如果是中文需要编码(BASE64),但session没有
>>cookie是使用set-cookie响应头和cookie请求头交换数据,session一样
6)session位于服务端,其大小受web服务器的限制,因此session主要存放web应用中核心的数据,而将非核心的数据放在cookie中。
*1 httpSession细节(下)
1)如果通过超链接点击购买图书,会有"referer"这个请求头;反之如果通过在地址栏直接输入URL购买图书,则无"referer"。
2)httpSession在维护浏览器和服务端的会话中,使用了一个特殊的cookie,它的名字叫JSESSIONID,其值就是httpSession.getId()
3)如果我们强行创建一个cookie,名叫JSESSIONID,其它是httpSession.getId(),那么服务端会认为是同一个用户
4)当浏览器禁用cookie时,可以使用URL重写来达到服务端和浏览器维持通信的能力
5)url = response.encodeURL(url);将url重写,即加上jsessionid这个参数,该方法适合于
超链接,或者表表单<form action=""属性/>
6)url = response.encodeRedirectURL(url);
response.sendRedirect(url);
上述API针对只针对重定向
7)不论当浏览器禁用或不禁用cookie,服务端都可以使用URL重写技术来达到浏览器和服务端维护的方式,因为这二个API能够智能判段浏览器是否禁用cookie,从而采用对应的处理方式
*2 销毁httpSession的几种方法
1)重新部署web应用
2)停止web服务器,
3)httpSession一旦创建,就会在web服务器中,默认停留30个分钟,30分钟后,由web服务器销毁,何时销毁,由web服务器决定,类似GC(垃圾回收处理机制)。
*4)通过在web.xml文件中,配置session的有效时间,单位分钟,关闭浏览器,在默认情况下,没有销毁session。
*5)session.invalidate(),立即马上销毁session,将session中绑定的所有信息清空,例如:安全退出
例: <session-config>
<session-timeout>5</session-timeout>
</session-config>
*3 httpSession应用
1)判段是否是新/旧会话,session.isNew():true表示新会话
2)用户登录[v1版]
3)防止表单重复提交
>>表单需要动态产生一个唯一的随机值,该随机值需要在服务端保存一份,客户端隐蔽域的形成提交
>>服务端在比较的过程中,相同则表示提交成功;成功后,删除服务端的随机值
>>项目中多用于注册,登录等场景
4)一次性验证码
public class ServletCheckcode extends HttpServlet {
public void doGet(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
//设置浏览器不缓存
response.setHeader("expires","-1");
response.setHeader("cache-control","no-cache");
response.setHeader("pragma","no-cache");
//获取验证码
String checkcode = this.getRandom();
//绑定到HttpSession中
request.getSession().setAttribute("checkcode",checkcode);
//在内存中创建一个图片
Buffered-image img = new BufferedImage(80, 30, BufferedImage.TYPE_INT_RGB);
//获取画笔
Graphics g = img.getGraphics();
g.drawString(checkcode, 30, 20);
//输出到浏览器
ImageIO.write(img,"JPG",response.getOutputStream());
}
//产生一个1000-9999的随机数
private String getRandom(){
Random r = new Random();
int num = r.nextInt(9000) + 1000;
return num + "";
}
}
什么是JavaBean
JavaBean是一个遵循特定写法的Java类,它通常具有如下特点:
这个Java类必须具有一个无参的构造函数
属性必须私有化。
私有化的属性必须通过public类型的方法暴露给其它程序,并且方法的命名也必须遵守一定的命名规范。
JavaBean的属性
4 JavaBean的属性可以是任意类型,并且一个JavaBean可以有多个属性。每个属性通常都需要具有相应的setter、 getter方法,setter方法称为属性修改器,getter方法称为属性访问器。
5 属性修改器必须以小写的set前缀开始,后跟属性名,且属性名的第一个字母要改为大写,例如,name属性的修改器名称为setName,password属性的修改器名称为setPassword。
6 属性访问器通常以小写的get前缀开始,后跟属性名,且属性名的第一个字母也要改为大写,例如,name属性的访问器名称为getName,password属性的访问器名称为getPassword。
7 一个JavaBean的某个属性也可以只有set方法或get方法,这样的属性通常也称之为只写、只读属性。
在JSP中使用JavaBean
4 JSP技术提供了三个关于JavaBean组件的动作元素,即JSP标签,它们分别为:
5 <jsp:useBean>标签:用于在JSP页面中查找或创建一个JavaBean组件。
6 <jsp:setProperty>标签:用于在JSP页面中设置一个JavaBean组件的属性。
7 <jsp:getProperty>标签:用于在JSP页面中获取一个JavaBean组件的属性。
8 <jsp:useBean>标签用于在指定的域范围内查找指定名称的JavaBean对象:
• 如果存在则直接返回该JavaBean对象的引用。
• 如果不存在则实例化一个新的JavaBean对象并将它以指定的名称存储到指定的域范围中。
常用语法:
<jsp:useBeanid="beanName" class="package.class"
scope="page|request|session|application"/>
id属性用于指定JavaBean实例对象的引用名称和其存储在域范围中的名称。
class属性用于指定JavaBean的完整类名(即必须带有包名)。
scope属性用于指定JavaBean实例对象所存储的域范围,其取值只能是page、request、session和application等四个值中的一个,其默认值是page。
<jsp:setProperty>标签用于设置和访问JavaBean对象的属性。
语法格式:
<jsp:setProperty name="beanName"
{
property="propertyName"value="{string | <%= expression %>}" |
property="propertyName"[ param="parameterName" ] |
property= "*" }/>
name属性用于指定JavaBean对象的名称。
property属性用于指定JavaBean实例对象的属性名。
value属性用于指定JavaBean对象的某个属性的值,value的值可以是字符串,也可以是表达式。为字符串时,该值会自动转化为JavaBean属性相应的类型,如果value的值是一个表达式,那么该表达式的计算结果必须与所要设置的JavaBean属性的类型一致。
param属性用于将JavaBean实例对象的某个属性值设置为一个请求参数值,该属性值同样会自动转换成要设置的JavaBean属性的类型。
<jsp:getProperty>标签用于读取JavaBean对象的属性,也就是调用JavaBean对象的getter方法,然后将读取的属性值转换成字符串后插入进输出的响应正文中。
语法:
<jsp:getPropertyname="beanInstanceName" property="PropertyName" />
name属性用于指定JavaBean实例对象的名称,其值应与<jsp:useBean>标签的id属性值相同。
property属性用于指定JavaBean实例对象的属性名。
JSP开发模式
4 SUN公司推出JSP技术后,同时也推荐了两种web应用程序的开发模式,一种是JSP+JavaBean模式,一种是Servlet+JSP+JavaBean模式。
5 JSP+JavaBean模式适合开发业务逻辑不太复杂的web应用程序,这种模式下,JavaBean用于封装业务数据,JSP即负责处理用户请求,又显示数据。
6 Servlet+JSP+JavaBean(MVC)模式适合开发复杂的web应用,在这种模式下,servlet负责处理用户请求,jsp负责数据显示,javabean负责封装数据。 Servlet+JSP、JavaBean模式程序各个模块之间层次清晰,web开发推荐采用此种模式。
4 什么是JSP
1)为什么说,HTML是一个静态Web开发技术呢?
因为HTML没有代表客户端和服务端的一个对象
2)为什么说,Servlet是一个动态Web开发技术呢?
因为Servlet有代表客户端和服务端的一个对象,分别request和response对象,
3)SUN公司还定义了一种动态开发技术规则,叫JSP(Java Server Pages)
4)JSP主要解决页面需要动态从服务端获取的数据,例如:(从request/session/servletContext)
5)JSP的特点:
>>与HTML具有类似的风格,原HTML怎么写,JSP就可以怎么写
>>为了取得动态的效果,可以在JSP页面中,使用JSP特有的语言规则嵌入Java代码
5 JSP+Servlet最佳配合体验
1)Servlet可以完成页面输出,但效果较差,不便于难护,比较适合做控制器
2)JSP比较适合做页面输出
*6 JSP工作原理
1)Web服务器是如何调用并执行一个jsp页面的?
>>date.jsp文件,会被web服务器翻充成date_jsp.java源代码,本质就是一个servlet,也与servlet一样,有着类似的生命周期方法,即jsp本质上就是一个servlet,servlet能做的事情,jsp也能做到,jsp和servlet决不能替代,只是一个互相补充的关系
2)Jsp页面中的html排版标签是如何被发送到客户端的?
所有静态数据通过write()方法输出
3)Jsp页面中的java代码服务器是如何执行的?
所有动态数据通过print()方法输出
4)Web服务器在调用jsp时,会给jsp提供一些什么java对象?
Web服务器在将date.jsp文件翻译成date_jsp.java源代码时,会自动创建[看得见]的8个内置对象,因此在jsp面页中可以直接使用这8个内置对象
5)工作原理
1)如果访问的是jsp,那么web服务器就会将请求的资源交由jsp引擎来处理
2)如果是第一次访问jsp,那么jsp引擎会将jsp翻译成servlet源码
3)jsp引擎将servlet源码交由servlet引擎来处理,参见<<servlet工作原理>>
4)如果第一次访问jsp,时间会相对较长,因为jsp引擎在做翻译成servlet工作
5)如果再次访问相同的jsp,如果内容没有改变,这时jsp引擎不做翻译,直接交由servlet引擎处理
6)如果再次访问的jsp,但内容更新了,这时jsp引擎又会将新的jsp做翻译
7 JSP语法
1)JSP模版元素
就是在JSP页面中的HTML标签,例如:<body>,<font>,...
2)JSP表达式
语法:<%="需要输出的字符常量或变量"%>,
类似于response.getWriter().write("当前时间");
输出表达式中不能含有分号
3)JSP脚本片断
语法:<% JSP脚本代码,以分号结束,即Java代码 %>
多个脚本片段之间完全可以访问,因为这些片段中的代码,最终都被翻译到_jspService()方法中,成为局部变量
4)JSP声明
语法:<%! Java代码,会被翻译成实例变量%>
5)JSP注释
语法:
XML: <!-- xxxxx -->
Java: //单行
/* */多行
/**
* 文档注释
*
*/
JS: //单行
/* */多行
HTML:<!-- xxxx -->
JSP:<%-- xxxxxx --%>
如果你使用JSP特有的注释,JSP引擎不会将其翻译到Servlet源码中,除此之外,就要翻译到Servlet源码中
注释不允许嵌套
Day10:
1 JSP中指令
1)JSP指令有三类
>>page指令
>>include指令
>>taglib指令
2)JSP指令的作用:针对JSP引擎来设计,通知JSP引擎应该针对该JSP页面如何处理
*3)page指令的属性含义
格式一:<%@ 指令名 属性名1="属性值1" 属性名2="属性值2"%>
格式二:<%@ 指令名 属性名1="属性值1"%>
<%@ 指令名 属性名2="属性值2"%>
[ language="java" ] 指明JSP脚本〈%%>中的语言是java,默认也是java语言
*[ import="{package.class | package.*},..." ] 指明让JSP引擎导入的相关包,有些包不需要导,javax.servlet.*,javax.servlet.http.*
[ session="true | false" ] 在servlet中,产生HttpSession必需要有如下代码:request.getSession(),没有的话,不会产生当我们访问的任何一个jsp页面时,在默认情况下,web服务器[一定]都会帮我们产生HttpSession 指明让JSP引擎产生HttpSession,true表示产生HttpSession,false表示不产生HttpSession,默认true
[ buffer="none | 8kb | sizekb" ] 指明让JSP引擎创建缓存池的大小,默认有,且8kb
[ autoFlush="true | false" ] 指明让JSP引擎自动监视缓存池的数据刷新情况,
>>当缓存池满,自动刷新,将数据输出到浏览器
>>当缓存池未满,但整个jsp执行完毕,将数据输出到浏览器
如果设置为false,则不让JSP引擎自动刷新,只有人工通过API刷新,默认true.
[ isThreadSafe="true | false" ]
jsp本质就是一个servlet,jsp与servlet一样具有安全问题,如果需要让JSP引擎解决安全问题,则选中true;如果选中false,则自已解决安全问题,即加锁,默认true。[ info="text" ] 指明JSP引擎,该JSP页面的作者,版本,时间,等等相关信息
*[errorPage="relative_url" ] 出错后,转发的页面,路径可以是相对或绝对路径,
*[isErrorPage="true | false" ] 出错面页,如果选用true表示该jsp为出错后转发的页面,JSP引擎自动产生exception对象,如果选用false,JSP引擎不会自动产生exception对象可以在web.xml文件中配置全局的错误出错处理页面,也可以在jsp文件中配置局部的错误出错处理页面,当二者冲突时,局部优先当全局异常中,出现代码和类型时,类型优先默认false,即不能使用exception内置对象
*[contentType="text/html ; charset=UTF-8" ] , [pageEncoding="UTF-8" ]
上述二者等价,但如果冲突,contentType优先
*[isELIgnored="true | false" ]
false指明JSP引擎支持EL,true不支持EL,默认是支持的,为false
4)include指令的属性含义
当在一个jsp页面中包含N个子页面时,可以采用include指令,最终的编码方式由总的页面决定
对于子jsp页面,并没有翻译成servlet,都在翻译时,包含在总的jsp所翻译的servlet中,结构不清淅将include指令所包含的jsp称做"静态包含"
*2 JSP中九大内置对象
request/response
session/application
config/out
注意:out对应的类型是JspWriter类型,它是比PrintWriter更高级的对象,相当于将PrintWriter的缓存作用out对象的缓存用
exception:只有当该JSP为错误处理页时,才能使用
3. PageContext对象作用
1)PageContext是一个域对象(setAttribute/getAttribute/removeAttribute),有且只有在当前jsp页面中有效也可以通过PageContext将信息绑定任意域对象中
(page/request/session/application),默认page
2)PageContext可以取得其它8个内置对象
3)且有转发和包含操作
4 JSP中四个域对象汇总
1)生命周期:
从生命周期的小到大:pageContext->request->session->application
page域:是JSP特有的,当前页面中共资源
request域:一次请求中的所有servlet和jsp共享资源
session域:整个会话中的所有servlet和jsp共享资源
application域:整个web应用中的所有servlet和jsp共享资源
2)案例
page域:在当前页面中使用
request域:读取内容显示,错误消息显示
session域:购物车,用户登录,注册
application域:在线访问计数器
5 JSP内置[非JavaBean]标签
1)JSP标签是一种替代JSP脚本的技术
2)JSP标签可以达到美化页面,又且有业务逻辑流程控制的功能
3)<jsp:forward page=”url”>标签
转发:<%pageContext.forward()%>
4)<jsp:include>标签
包含:<%pageContext.include()%>
<%@ include file%>
<jsp:include page flush/>
>>有N个jsp,则翻译成N个servlet源码
>>总的jsp页面结构清晰
>>是一个动态包含的过程,执行时,临时加载,因此叫动态包含
flush表示表示是否先将include页面的信息输出,false表示不先输出,true表示先输出
5)<jsp:param>标签
带参转发或包含?name=jack&pass=123
6)JSP错误有三类
>>JSP表面语言错误
>>翻译成Servlet源码有误
>>JSP运行过程中出错,逻辑错误为主
JSP内置[JavaBean]标签
什么是JavaBean
JavaBean是一个遵循特定写法的Java类,它通常具有如下特点:
这个Java类必须具有一个无参的构造函数
属性必须私有化。
私有化的属性必须通过public类型的方法暴露给其它程序,并且方法的命名也必须遵守一定的命名规范。
1)JavaBean分二类;
>>狭义JavaBean/实体/域对象/值对象(今天特指)
1)私有字段
2)公有的构造器
3)公有的set(存)/get(取)方法
>>广义JavaBean(模型)
普通类,含普通方法
2)JavaBean有作用?
存取程序员的数据,
3)JavaBean的命名规则
private String name;
存:setName()
取:getName();
JavaBean的属性
JavaBean的属性可以是任意类型,并且一个JavaBean可以有多个属性。每个属性通常都需要具有相应的setter、 getter方法,setter方法称为属性修改器,getter方法称为属性访问器。
属性修改器必须以小写的set前缀开始,后跟属性名,且属性名的第一个字母要改为大写,例如,name属性的修改器名称为setName,password属性的修改器名称为setPassword。
属性访问器通常以小写的get前缀开始,后跟属性名,且属性名的第一个字母也要改为大写,例如,name属性的访问器名称为getName,password属性的访问器名称为getPassword。
一个JavaBean的某个属性也可以只有set方法或get方法,这样的属性通常也称之为只写、只读属性
4)在JSP中如何使用JavaBean
1)<jsp:useBean/>
>>首先在指定的域对象中[查询]是否有对应的JavaBean存在,如果有的话,直接取得该JavaBean;
如果没有的话,则创建该JavaBean,然后自动绑定到指定的域对象中,默认page域
>>标签体的内容,只有新会话首次访问时,才会执行,否则不执行
2)<jsp:setProperty/>
>>为JavaBean设置值
>>自动将String类型,转成8种基本类型
>>当参数名和字段名一致的情况下,可以使用下列代码
<jsp:setPropertyname="user" property="*"/>
3)<jsp:getProperty/>
>>从JavaBean中取出值
例: <body>
<!-- 创建javaBean -->
<jsp:useBeanid="User" class="cn.web.servlet.JSP.User"/>
<!-- 设置javaBean中的属性 1-->
<jsp:setPropertyproperty="name"name="User" value="张三"/>
<jsp:setPropertyproperty="id" name="User" value="12334"/>
<jsp:setPropertyproperty="salary"name="User" value="4500"/>
<!-- 设置javaBean中的属性2 -->
<jsp:setPropertyproperty="name"name="User" param="username"/>
<jsp:setPropertyproperty="id" name="User" param="userid"/>
<jsp:setPropertyproperty="salary"name="User" param="usersalary"/>
<!-- 设置javaBean中的属性3 -->
<jsp:setPropertyproperty="*" name="User"/>
<!-- 取出javaBean中的属性 -->
用户名:<jsp:getPropertyproperty="name"name="User"/>
编号:<jsp:getPropertyproperty="id" name="User"/>
薪水:<jsp:getPropertyproperty="salary"name="User"/>
</body>
在JSP中使用JavaBean
JSP技术提供了三个关于JavaBean组件的动作元素,即JSP标签,它们分别为:
<jsp:useBean>标签:用于在JSP页面中查找或创建一个JavaBean组件。
<jsp:setProperty>标签:用于在JSP页面中设置一个JavaBean组件的属性。
<jsp:getProperty>标签:用于在JSP页面中获取一个JavaBean组件的属性。
<jsp:useBean>标签用于在指定的域范围内查找指定名称的JavaBean对象:
如果存在则直接返回该JavaBean对象的引用。
如果不存在则实例化一个新的JavaBean对象并将它以指定的名称存储到指定的域范围中。
常用语法:
<jsp:useBeanid="beanName" class="package.class"scope="page|request|session|application"/>
id属性用于指定JavaBean实例对象的引用名称和其存储在域范围中的名称。
class属性用于指定JavaBean的完整类名(即必须带有包名)。
scope属性用于指定JavaBean实例对象所存储的域范围,其取值只能是page、request、session和application等四个值中的一个,其默认值是page。
<jsp:setProperty>标签用于设置和访问JavaBean对象的属性。
语法格式:
<jsp:setProperty name="beanName"
{
property="propertyName"value="{string | <%= expression %>}" |
property="propertyName"[ param="parameterName" ] |
property= "*" }/>
name属性用于指定JavaBean对象的名称。
property属性用于指定JavaBean实例对象的属性名。
value属性用于指定JavaBean对象的某个属性的值,value的值可以是字符串,也可以是表达式。为字符串时,该值会自动转化为JavaBean属性相应的类型,如果value的值是一个表达式,那么该表达式的计算结果必须与所要设置的JavaBean属性的类型一致。
param属性用于将JavaBean实例对象的某个属性值设置为一个请求参数值,该属性值同样会自动转换成要设置的JavaBean属性的类型。
<jsp:getProperty>标签用于读取JavaBean对象的属性,也就是调用JavaBean对象的getter方法,然后将读取的属性值转换成字符串后插入进输出的响应正文中。
语法:
<jsp:getProperty name="beanInstanceName"property="PropertyName" />
name属性用于指定JavaBean实例对象的名称,其值应与<jsp:useBean>标签的id属性值相同。
property属性用于指定JavaBean实例对象的属性名。
JSP开发模式
Jsp(v)+jsp(c)+javaBean
Jsp(v)+Servlet(c)+javaBean
SUN公司推出JSP技术后,同时也推荐了两种web应用程序的开发模式,一种是JSP+JavaBean模式,一种是Servlet+JSP+JavaBean模式。
JSP+JavaBean模式适合开发业务逻辑不太复杂的web应用程序,这种模式下,JavaBean用于封装业务数据,JSP即负责处理用户请求,又显示数据。
Servlet+JSP+JavaBean(MVC)模式适合开发复杂的web应用,在这种模式下,servlet负责处理用户请求,jsp负责数据显示,javabean负责封装数据。Servlet+JSP、JavaBean模式程序各个模块之间层次清晰,web开发推荐采用此种模式。
* Web开发中的二种模式
1 EL和JSTL简介和快速入门
1)Java Standard Taglib Language
JSTL是一种SUN公司提供的外置标签,目的是替代原JSP中的脚本符号
JSTL和JSP内置标签是一个互补的关系
2)使用JSTL是补充JSP内置标签的功能不足
3)Expression Language表达式语言,主要是配合JSTL的使用
4)开发JSTL应用的步骤:
>>导入jstl.jar和Standard.jar二个jar包,如果工具自带就不用导入
>>在需要使用jstl的jsp面页,通过<%taglib%>指令声明
<%@ tagliburi="http://java.sun.com/jsp/jstl/core" prefix="c" %>
>>使用相关的jstl外置标签
<c:forEach var/items的值一定要配合EL使用>
数据类型:
tinyint/smallint/mediumint/int/bigint-->1B/2B/3B/4B/8B
float/double-->单精度/双精度浮点型
decimal-->不会产生精度丢失的单精度/双精度浮点型
date-->日期类型
time-->时间类型
datetime-->日期时间类型
year-->年类型
char-->定长字符串类型
varchar-->可变长字符串类型(65535字符)
BLOB类别类型
tinyblob/blob/mediumblob/longblob-->255B/64K/16M/4G大小的图片/音乐等二进行数据(JDBC中讲)
text类别类型
tinytext/text/mediumtext/longtext-->255B/64K/16M/4G大小的文本数据(JDBC中讲)
1 什么是数据库
1)SQL=Struture [Query] Language 结构化的查询语言
2)存放有一定规则的数据的容器
3)文件本身并没有一种机制去检测数据是否合法,因此需要一种新的技术去替代文件作数据的容器
4)文件内容如果过大,打开相对较慢,因此需要一种新的技术去替代文件作数据的容器
5)MySQL-软件-
2 各种常用数据库简介
1)Oracle&Unix/MySQL&Linux/SQLServer&Window(个人推荐,不能固定)
3 SQL[通用/专用]与数据库的关系
1)SQL是Java应用程序操作数据库的工具之一
2)如果某个SQL能够操不各种各样的数据库,这样的SQL叫做"通用SQL"(重点)
3)如果某个SQL只能够操一种数据库,这样的SQL叫做"专用SQL"
*4 MySQL数据库的卸载,安装,设置,进入和退出
1)进入MySQL数据库:c:\>mysql -u root -p回车
entry password:****回车
2)退出MySQL数据库:mysql:>exit回车
8 修改、备份、恢复数据库中的数据
alter database 数据名
mysqldump -u root -p 需要备份的数据库名 > *.sql文件的路径(需要在window环境中运行,专用于mysql)
source *.sql文件的路径(需要mysql环境中运行,专用于mysql)
9 MySQL支持的常用数据类型简介
1)日期时间(date/datetime/timestamp),其它date/datetime必须给定值,而timestamp可以不给定值,默认为当前时间
2)文本数据和多媒体数据(varchar/text/blob)
3)数值型(TINYINT/SMALLINT/MEDIUMINT/INT(默认是11个位数/BIGINT)
4)数值型默认有符号,INT型能存多大的值,不是看位数,而是看INT类型的最大有效值
*10 创建/查看/修改/删除表
create table 表名
desc 表名
11 MySQL解决插入中文乱码问题(WindowXP/7平台)
set character_set_client=gbk;(window平台默认字符集)
set character_set_results=gbk;(window平台默认字符集)
*12 表的增删改操作
1)删除数据可以使用以下二种方式:
>>delete from 表名
都能删除表中的所有记录,保留表结构
一行一行的删除
项目中数据量相对小时,推荐使用delete删除所有数据
可以按条件删除
>>truncate table 表名
都能删除表中的所有记录,保留表结构
复制原表结构->删除整个表->自动创建表结构
项目中数据量相当大时,推荐使用trancate删除所有数据
不可以按条件删除
>>drop table 表名
不光表数据删除,表结构也删除,不可以按条件删除
2)insert into 表名(字段) values(对应的字段值)
3)update table 表名 set 字段1=值1,字段2=值2 where 字段 = 条件
1. 创建一个数据库。
2. 显示数据库语句:
3. SHOW DATABASES
4. 显示数据库创建语句:
5. SHOW CREATE DATABASE db_name
6. 使用数据库
7. USE db_name
8. 数据库删除语句:
9. DROP DATABASE [IF EXISTS] db_name
10. 查看指11. 定的库下所有的表
Show tables;
CHARACTER SET:指定数据库采用的字符集
COLLATE:指定数据库字符集的比较方式、规则,比如排序
创建一个名称为mydb1的数据库。
create databaseif not exists mydb1;
如果在创建数据库时,没有指明编码方式,默认和配置数据库时采用的编码方式一致
if not exists 表示先查询在数据库服务器中是否有数据库存在,如果没有,才创建;如果有,则直接使用
创建一个使用utf8字符集的mydb2数据库。
create databasemydb2 character set utf8;
//准意,MySQL编码集中没有UTF-8,只有UTF8
//MySQL的SQL命令,大小写不敏感
创建一个使用utf8字符集,并带校对规则的mydb3数据库。
create databasemydb3 character set utf8 collate utf8_general_ci;
查看当前数据库服务器中的所有数据库
show databases;
查看前面创建的mydb2数据库的定义信息
show createdatabase mydb2;
删除前面创建的mydb1数据库
drop databaseif exists mydb1;
使用mydb2数据库
use mydb2;
查看服务器中的数据库,并把其中mydb3库的字符集修改为gbk。
alter databasemydb3 character set gbk;
备份mydb2库中的数据,并恢复。
use mydb2;
create tableuser(
name varchar(20)
);
insert intouser(name) values('jack');
insert intouser(name) values('marry');
select * fromuser;
备份:c:\>mysqldump -u root -p mydb2 > d:\xx.sql回车
//mysqldump命令需要在window环境中运行,而不是mysql环境中运行
//mysqldump命令是mysql提供的专用sql命令
//mysqldump命令是将数据库中表数据备份到*.sql文件中
恢复:mysql>source d:\xx.sql回车
//source命令需要在mysql环境中运行,而不是window环境中
//source命令是mysql提供的专用sql命令
//source命令是将硬盘中的*.sql文件中的内容恢复到数据库中
创建一个user表,包含id/name/password/birthday
drop table ifexists user;
create tableuser(
id int(5),
name varchar(20),
password varchar(6),
salary float(6,2),//6表示整个数值的位数,2表示小数点后的位数,不包含小数点本身
birthday timestamp
);
insert intouser(id,name,password,salary) values(1,'jack','123456',9999.99);
查询user表的结构desc user
创建employee表
create table ifnot exists employee(
id int,
name varchar(20),
gender varchar(6),
birthday timestamp,
entry_date timestamp,
job varchar(20),
salary float,
resume text
);
在上面员工表的基本上增加一个image列。
alter tableemployee add image blob;
修改job列,使其长度为60。
alter tableemployee modify job varchar(60);
删除gender列。
alter tableemployee drop gender;
表名改为staff。
rename tableemployee to staff;
修改表的字符集为utf8。
alter tablestaff character set utf8;如果在创建表时不指定,默认和数据库的编码方式一致
列名name修改为username。
alter tablestaff change column name usernamevarchar(40);
删除表
drop table ifexists user;
1.增加数据(使用 INSERT 语句向表中插入数据)
插入的数据应与字段的数据类型相同。
数据的大小应在列的规定范围内,例如:不能将一个长度为80的字符串加入到长度为40的列中。
在values中列出的数据位置必须与被加入的列的排列位置相对应。
字符和日期型数据应包含在单引号中。
插入空值,不指定或insert into table value(null)
insert intoemployee values(1,'jack','male','engineer',5500,'16K','2012-7-14 15:15:15');
insert intoemployee(id,name,gender,job,salary,resume) values(2,'tom','male','engineer',5500,'16K');
insert intoemployee(name,id,gender,job,salary,resume) values('marry',3,'female','engineer',5500,'16K');
insert intoemployee(id,name,gender,job,salary,resume) values(4,'sisi','female','engineer',5500,NULL);
insert intoemployee(id,name,gender,job,salary) values(5,'soso','female','engineer',5500);
insert intoemployee(id,name,gender,job,salary) values(6,'xx','xxxxxx','engineer',5500);
insert intoemployee(id,name,gender,job,salary) values(7,'杰克','男','工程师',5500);
2.使用 update语句修改表中数据。
SET子句指示要修改哪些列和要给予哪些值。
WHERE子句指定应更新哪些行。如没有WHERE子句,则更新所有的行。
将所有员工薪水修改为6000元。
update employeeset salary = 6000;
update employeeset salary=6500,resume='16K' where name='xx';
将姓名为’杰克’的员工薪水修改为7000元。
update employeeset salary=7000 where name='杰克';
将’马利’的薪水在原有基础上增加1000元。
update employeeset salary=salary+1000 where name='马利';
使用 delete语句删除表中数据。
如果不使用where子句,将删除表中所有数据。delete语句不能删除某一列的值(可使用update),使用delete语句仅删除记录,不删除表本身。如要删除表,使用drop table语句。同insert和update一样,从一个表中删除记录将引起其它表的参照完整性问题,在修改数据库数据时,头脑中应该始终不要忘记这个潜在的问题。删除表中数据也可使用TRUNCATE TABLE 语句,它和delete有所不同。
TRUNCATE(复制表结构->销毁表->重建表结构)
DELETE(逐行删除记录)
删除表中名称为’xx’的记录。
delete fromemployee where name='xx';
删除表中所有记录。
delete fromemployee;
使用truncate删除表中记录。
truncate tableemployee
查询表中所有学生的信息。
selectid,name,chinese,english,math from student;
selectname,id,chinese,english,math from student;
简化
select * fromstudent;
查询表中所有学生的姓名和对应的英语成绩。
selectname,english from student;
基本select语句
Select 指定查询哪些列的数据。
column指定列名。
*号代表查询所有列。
From指定查询哪张表。
DISTINCT可选,指显示结果时,是否剔除重复数据
过滤表中重复数据。
select distinctchinese from student;
在所有学生分数上加10分特长分。
加分前:select name,chinese,math,english from student;
加分后:select name,chinese+10,math+10,english+10 from student;
统计每个学生的总分。
selectname,chinese+math+english from student;
使用别名表示学生分数。
select name as 姓名,chinese+math+english as 总分 from student;
查询姓名为’张小明’的学生成绩
select * fromstudent where name = '张小明';
查询英语成绩大于90分的同学
selectname,english from student where english > 90;
查询总分大于200分的所有同学
select name as 姓名,chinese+math+english as 总分
from student
wherechinese+math+english > 200;
查询英语分数在 80-90之间的同学。
selectname,english
from student
whereenglish>=80 and english <=90;
或
selectname,english
from student
where englishbetween 80(小) and 90(大);
查询数学分数为89或者90或者91的同学。
selectname,math
from student
where math=89or math=90 or math=91;
或
select name,math
from student
where mathin(89,90,91);
查询所有姓’李’的学生成绩。
select name
from student
where name like'李%';//模糊查询
查询所有名’李’的学生成绩。
select name
from student
where name like'%李';
查询所有姓名中包含’李’的学生成绩。
select name
from student
where name like'%李%';
select name
from student
where name like'%%';
等价于
select name
from student;
查询所有姓’李’的学生成绩,但姓名必须是三个字
select name
from student
where name like'李__';
查询数学分>80且语文分>80的同学。
selectname,math,chinese
from student
wheremath>80 and chinese>80;
使用order by 子句排序查询结果。
对数学成绩排序(降序)后输出。
selectname,math
from student
order by mathdesc;
或
selectname,math
from student
order by mathasc;//默认升序
对总分排序(降序)后输出。
select name as 姓名,chinese+math+english as 总分
from student
order bychinese+math+english desc;
对姓’李’的学生总分排序(降序)输出。
select name as 姓名,chinese+math+english as 总分
from student
where name like'李%'
order bychinese+math+english desc;
统计一个班级共有多少学生?
select count(*)as 学生总人数 from student;
selectcount(math) as 学生总人数 from student;
统计数学成绩大于80的学生有多少个?
select count(*)as 学生总人数
from student
where math >80;
统计总分大于250的人数有多少?
select count(*)as 学生总人数
from student
wherechinese+math+english>250;
统计一个班级数学总成绩。
selectsum(math) as 数学总分 from student;
统计一个班级语文、英语、数学各科的总成绩。
selectsum(math) as 数学总分,sum(english) as 英语总分,sum(chinese) as 语文总分
from student;
统计一个班级语文、英语、数学的成绩总和。
selectsum(math+english+chinese) as 班级总分
from student;
统计一个班级语文成绩平均分。
selectsum(chinese)/count(chinese) as 语文成绩平均分
from student;
求一个班级数学平均分。
selectavg(math) as 数学平均分
from student;
求一个班级总分平均分。
select avg(math+english+chinese)as 班级总平均分
from student;
求班级最高分和最低分数学。
selectmax(math) as 最高数学分,min(math) as 最低数学分
from student;
对订单表中商品归类后,显示每一类商品的总价
select productas 商品,sum(price) as 总价
from orders
group byproduct;
查询购买了几类商品,并且每类总价大于100的商品
select productas 商品,sum(price) as 总价
from orders
group byproduct
having sum(price) > 100;
查询购买了几类商品,并且每类总价大于100的商品,同时按照总价的降序排列
select productas 商品,sum(price) as 总价
from orders
group byproduct
having sum(price) > 100
order bysum(price) desc;
1 表的完整性
(1)实体完整性:每条记录有一个唯一标识符,通常用无任何业务含义的字段表示
(2)参照完整性:一张表的某个字段必须引用另一张表的某个字段值(外健),主要针对多张表
(3)域完整性:域即单元数据,域中的数值必须符合一定的规则
2 键的概念
(1)单一主键:只有唯一字段(推荐)
(2)组合主键:由多个字段组合起来,形成唯一字段
(3)外键:针对多张表之间的关联
3 主键的特点
1)定义主键的语法:id int primary key
2)主键值不能为NULL
3)主键值不能重复
4)删除已存在的主键的语法:alter table person drop primary key
5)当删除已存在的主键后,可插入重复制,但不允许为NULL或者‘NULL’空串
6)主键可以由MySQL数据库系统自动产生一个唯一的值,语法如下:id int primary key auto_increment,MySQL特有
7)这个唯一的值,只在要表结构删除时清0,否则不会清0,继承累加,可能出现主键值不连续的情况,但一定唯一
8)项目中,主键不采用MySQL特有的auto_increment方式,而是采用UUID或自定义算法来产生唯一的主键,这样当多表中
的数据合起来时,主键重复的机会相当小
4 唯一约束的特点
1)定义唯一约束的语法:name varchar(20) unique
2)不能二次插入同一具体值
3)但可以插入NULL值,而不是’NULL‘串
5 非空约束特点
1)定义非空约束的语法:name varchar(20) unique notnull
2)不能插入NULL值,而不是’NULL‘串
3)以上约束的目的是确保数据的安全性,即能够存入到表中的数据,一定在符合安全约束的
6 外健特点
1)外键体现多表关联,
2)外键的值通常来自于另一张表的主键值
3)在对象世界中,有一对一单向和一对一双向
4)在关系世界中,只有一对一单向关联,原因在于删除操作
5)外键的语法:constraint pid_FK(给MySQL系统) foreign key(pid) references person(id)
测试MySQL特有的函数
使用MySQL特有函数举例:
1)到年底还有几少天[datediff]
select datediff('2012-6-2','2012-12-31');
2)截取字符串[substring]
select substring('abcdef',1,5);
3)保留小数点后2位(四舍五入)[format]
select format(2.333,1);
4)向下取整[floor]
select floor(2.5621);
5)取随机值[rand] 0~1
select rand();
6)取1-6之间的随机整数值[floor和rand]
select floor(rand()*6)+1;
7)截取字符串长度和去空格
select trime(" jack ");
8)字符比较
select strcmp('a','w');
JDBC全称为:Java Data Base Connectivity(java数据库连接),它主要由接口组成。
组成JDBC的2个包:java.sql javax.sql
编写一个程序,这个程序从user表中读取数据,并打印在命令行窗口中。
一、搭建实验环境:
1、在mysql中创建一个库,并创建user表和插入表的数据。
2、新建一个Java工程,并导入数据驱动。
二、编写程序,在程序中加载数据库驱动
DriverManager. registerDriver(Driver driver)
三、建立连接(Connection)
Connection conn =DriverManager.getConnection(url,user,pass);
四、创建用于向数据库发送SQL的Statement对象,并发送sql
Statement st = conn.createStatement();
ResultSet rs = st.excuteQuery(sql);
五、从代表结果集的ResultSet中取出数据,打印到命令行窗口
六、断开与数据库的连接,并释放相关资源
4祥解JDBC连接数据库的各个步骤
1)JDBC开发的步骤
>>导入mysql驱动jar包(mysql-connector-java-5.1.7-bin.jar,每种数据库都会有自动的JDBC实现jar包)
>>使用JDBC规则中的接口或类来操作所有关系型数据库
2)祥解
>>DriverManager(类)是管理一组具体数据库的驱动
>>Drvier(接口)需要被DriverManager管理的驱动,必须实现java.sql.Driver接口DriverManager会试图用URL和每个数据库驱动尝试进行连接
底层方法:
boolean acceptsURL(String url):能够连接返回true;不能够连接返回false ,如果返回true的情况下,DriverManager再调用Connectionconnect(String url, Properties info)返回真正的连接对象
>>Connection(接口)表示与特定数据库连接的对象(会话) ,在完成业务操作后,该对象越早关闭越好,一定要关闭,先关闭轻量有资源Statement和Resulset,后关系重量级资源Connection
>>Statement(接口)表示封装SQL命令对象,并执行SQL命令,取得返回值
>>ResultSet(接口)表示根据查询SQL命令得到的结果集,类似于一个List集合
初始情况下,位于第一行记录之前,需要通过next()方法将光标下移来取值
5 JDBC的六个固定步骤(以查询为例)
>>向DriverManager注册数据库驱动
>>取得Connection重量级连接对象
>>创建封装SQL命令的对象Statement
>>执行SQL命令,返回结果集ResultSet
>>处理结果集ResultSet
>>先轻后重,关闭结果集
Statement对象的executeUpdate方法,用于向数据库发送增、删、改的sql语句,executeUpdate执行完后,将会返回一个整数(即增删改语句导致了数据库几行数据发生了变化)。
Statement.executeQuery方法用于向数据库发送查询语句,executeQuery方法返回代表查询结果的ResultSet对象。
数据库分页:
MySQL分页的实现:
select * from table limit M,N
M:记录开始索引位置
N:取多少条记录。
完成WEB页面的分页显示, 先获得需分页显示的记录总数,然后在web页面中显示页码。
根据页码,从数据库中查询相应的记录显示在web页面中。以上两项操作通常使用Page对象进行封装。
使用JDBC处理大数据
在实际开发中,程序需要把大文本或二进制数据保存到数据库。
基本概念:大数据也称之为LOB(Large Objects),LOB又分为:
clob和blob
clob用于存储大文本
blob用于存储二进制数据
对MySQL而言只有blob,而没有clob,mysql存储大文本采用的是text,text和blob分别又分为:
TINYTEXT、TEXT、MEDIUMTEXT和LONGTEXT
TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB
MySQL中的Text类型
PreparedStatement.setCharacterStream(index, reader, length);
//注意length长度须设置,并且设置为int型
reader = resultSet. getCharacterStream(i);
reader = resultSet.getClob(i).getCharacterStream();
string s = resultSet.getString(i);
对于MySQL中的BLOB类型
文件的存储
PreparedStatement. setBinaryStream(i, inputStream, length);
//字节字符流输出文件
InputStream in =resultSet.getBinaryStream(i);
InputStream in =resultSet.getBlob(i).getBinaryStream();
批处理:
1 批处理
1)当需要向表中插入很多SQL命令时,可以使用批处理
2)对于[相同结构]的SQL命令来说,可以使用PreparedStatement对象,
原因:
>>只需一次性对SQL命令进行编译,以后凡是相同的SQL无需编译
>>只后的每次只是参数不同,
以上这种情部况,叫做预编译
3)对于[不相同结构的SQL命令来说,可以使用Statement对象,
原因:
>>每条不同的SQL命令,都需要进行编译,再到数据库执行
4)项目中,相同结构的SQL命令,建议使用PreparedStatement;不相同结构的SQL命令,建议使用Statement;但不表示,不能使用其它的对象。
5)批对象常用的API
>>stmt.addBatch(sqlA);
>>int[] ints = stmt.executeBatch();//如果执行100次,成功的话,数组中包含100个1
>>stmt.clearBatch();
>>pstmt.addBatch();
>>int[] ints = pstmt.executeBatch();
>>pstmt.clearBatch();
6)如果批处理的数据量较大,不要一次性执行多个SQL命令,而应该分次执行SQL命令
7)Statement对象:不具有预编译功能
PreparedStatement对象:具有预编译功能,可以使用占位符,可以防止SQL注入
2 获取主键值
1)需要使用外键关联别一张表的主键时,这时可以使用获取主键值
2)获取主键值专用于针对insert操作,因为insert操作才能产生主键值
3)常用的API:
pstmt =conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
Statement.RETURN_GENERATED_KEYS表示在完成insert操作后,保留主键值,以备程序获取
rs = pstmt.getGeneratedKeys(); 获取插入记录的主键值
3 事务概述
1)事务指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部不成功。类似于&&操作
2)事务是每个数据库都具有的特点,只是强度不同而以。
3)MySQL常用事务控制命令
start transaction;//显示开始事务,其它每条SQL命令都有事务存在,只是以前我们使用隐式开始事务
commit;//提交事务,以前每条SQL命令执行结束后,数据库帮我们自动隐式提交事务,
在start transaction和commit这二个命令之间的SQL命令,都位于一个事务之间
rollback;//回滚事务,回滚到start transaction之前
4)事务可以让某些程序功能更加安全可靠
5)常用的JDBCAPI:
conn.setAutoCommit(false);等价于start transaction;
conn.commit();等价于commit;
conn.rollback();等价于rollback;
注意:回滚后,也要提交事务
*6)要确定事务成功,就必须操作的是同一个connection对象,才能回滚成功,不能操作不同的connection对象,这一点非常重要
*4 事务的特性(ACID vs CURD)
原子性(A)
>>事务中的各个组成部份是一个整体,不可再分,要么成功,要么失败。
一致性(C)
>>以转帐为例,转帐前后,二者的余额总数不变
隔离性(I)
>>一个用户操作表的某条记录时,应该加锁,不影响其它用户同时使用该张表的某条记录,类似于Java中的同步操作
持久性(D)
>>当用户对表中的数据进行更新后,一旦确认后,就不应该非人为更新,应当永久保存
*5 事务的三个缺点
如果没有遵循“隔离性”原则,就会产生如下错误:
1)脏读
一个事务看到了另一个事务未提交的数据,强调[未提交],但数量不变
2)不可重复读
多次查询,结果不相同,强调[已提交],但数量不变
3)幻读/虚读
多次查询,结果不相同,强调[数量变化]
4)由轻到重:脏读->不可重复读->幻读/虚读
*6 事务的隔离级别
查看当前事务隔离级别
select @@tx_isolation;
修改当前事务隔离级别:
set transaction isolation level read uncommitted;
set transaction isolation level read committed;
set transaction isolation level repeatable read;
set transaction isolation level serializable;
启动事务: start transaction;
提交事务: commit;
回滚事务: rollback
1)事务的隔离级别能够解决[脏读/不可重复读/幻读]
2)事务的隔离级别有三类:
>>readcommitted:不能解决任何事物
>>read committed:只能解决脏读
>>repeatable read:只能解决脏读/不可重复读,MySQL默认
>>serializable:能够解决脏读/不可重复读/幻读,但效率超低,因为在查询表时,都会将表锁住,以至于其它用户无法操作
3)在serializable隔离级别,二者都可以查询操作,但对方不能做非查询操作,即[insert/update/delete]
4)项目中,查询无需事务,但非查询操作得需要事务帮助,更安全
5)设置事务隔离级别的API:
conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
*7 转帐
1)一定在确保Service和Dao层,用的都是同一个Connection对象
2)不要将Dao的Api引用到Serivce层,要做到解耦
3)使用ThreadLocale<Connection>将每个线程和Connection对象绑定在一起,类似于一个Connection复本。
连接池
DBCP----DATABASE CONNECTIONPOOL tomcat内置
C3P0---hibernate内置
使用的.jar
commons-dbcp.jar:连接池的实现
commons-pool.jar: 连接池实现的依赖类
commons-collections.jar :连接池实现的集合类
DBCP:
static{
InputStream in =JdbcUtil.class.getClassLoader().
getResourceAsStream("dbcp.properties");
Propertiesprop = new Properties();
prop.load(in);
BasicDataSourceFactory factory = newBasicDataSourceFactory();
dataSource =factory.createDataSource(prop);
}
C3P0 数据源:
元数据- DataBaseMetaData:
Api:
Connection.getMetaData()
DataBaseMetaData对象
getURL():返回一个String类对象,代表数据库的URL。
getUserName():返回连接当前数据库管理系统的用户名。
getDatabaseProductName():返回数据库的产品名称。
getDatabaseProductVersion():返回数据库的版本号。
getDriverName():返回驱动驱动程序的名称。
getDriverVersion():返回驱动程序的版本号。
isReadOnly():返回一个boolean值,指示数据库是否只允许读操作。
元数据- ParameterMetaData:
PreparedStatement.getParameterMetaData() 获得代表PreparedStatement元数据的ParameterMetaData对象。
ParameterMetaData对象 getParameterCount() 获得指定占位符?参数的个数
元数据- ResultSetMetaData
ResultSet.getMetaData() 获得代表ResultSet对象元数据的ResultSetMetaData对象。
ResultSetMetaData对象 getColumnCount() 返回resultset对象的列数
getColumnName(int column) 获得指定列的名称
getColumnTypeName(int column) 获得指定列的类型(Types类)
常用O-R Mapping映射工具
Hibernate(全自动框架)
Ibatis(半自动框架/SQL)
CommonsDbUtils(只是对JDBC简单封装)
API介绍:
org.apache.commons.dbutils.QueryRunner(类)
org.apache.commons.dbutils.ResultSetHandler(接口)
工具类
org.apache.commons.dbutils.DbUtils。
QueryRunner类
需要一个 javax.sql.DataSource 来作参数的构造方法。
public Object query(Connection conn, Stringsql, Object[] params, ResultSetHandler rsh) throws SQLException:执行一个查询操作,在这个查询中,对象数组中的每个元素值被用来作为查询语句的置换参数。该方法会自行处理PreparedStatement 和 ResultSet 的创建和关闭。
public Object query(String sql, Object[]params, ResultSetHandler rsh) throws SQLException: 几乎与第一种方法一样;唯一的不同在于它不将数据库连接提供给方法,并且它是从提供给构造方法的数据源(DataSource) 或使用的setDataSource方法中重新获得 Connection。
public Object query(Connection conn, Stringsql, ResultSetHandler rsh) throws SQLException : 执行一个不需要置换参数的查询操作。
public int update(Connection conn, Stringsql, Object[] params) throws SQLException:用来执行一个更新(插入、更新或删除)操作。
public int update(Connection conn, Stringsql) throws SQLException:用来执行一个不需要置换参数的更新操作。
ResultSetHandler接口
BeanHandler:将结果集中的第一行数据封装到一个对应的JavaBean实例中。
BeanListHandler:将结果集中的每一行数据都封装到一个对应的JavaBean实例中,存放到List里。
ArrayHandler:把结果集中的第一行数据转成对象数组。
ArrayListHandler:把结果集中的每一行数据都转成一个对象数组,再存放到List中
MapHandler:将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
MapListHandler:将结果集中的每一行数据都封装到一个Map里,然后再存放到List。
calarHandler:结果集中只有一行一列数据。
2012-7-22day17:
使用文档 JSPAPI
<%=request.getrRemoteAdd() %>
标签: <simple: ip/> 获取客户端ip地址
自定义标签: 1.实现simpleTag 接口.
2)在WEB-INF/*.tld,目的是通知Web服务器自定义标签对应的处理类
<tlib-version>1.0(标签的版本号)</tlib-version>
<short-name>simple(标签的前缀)</short-name>
<uri>http://java.sun.com/jsp/jstl/simple(标签从自于哪个包,类似于类来源于哪个包)</uri>
<tag>
<name>ip(标签的名字)</name>
<tag-class>cn.itcast.web.jsp.tag.IpTag(标签对应的底层处理类全路径)</tag-class>
<body-content>empty(该标签是一个空标签)</body-content>
<attribute>
<name>count(属性名)</name>
<required>true(属性是否必须,true必须)</required>
<rtexprvalue>true(属性值是否接收动态值,true表示可接收动态值)</rtexprvalue>
</attribute>
</tag>
3)在需要使用该自定义标签的JSP面页中,通过<%@taglib%>导入
<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/simple" prefix="simple"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01Transitional//EN">
<html>
<body>
客户端IP是:<simple:ip/>
</body>
</html>
Jsp - Jstl:
JSTL: javastandar taglib language: java标准标签语言
Out:标签: <c:out> 标签用于输出一段文本到浏览器中
例:
<% request.setAttribute("role","管理员");%>
<c:outvalue="${roles}"default="<ahref='/myday18/index.jsp'><fontcolor='red'>游客</font></a>"escapeXml="false"/>
Value : 表示输出的值.
default : 表示指定如果value属性的值为null/””时所输出的默认值
escapeXml : 表示输出的内容,是否将>、<、&、'、" 等特殊字符进行HTML编码转换后再进行输出。默认值为true
<c:set>标签
<c:set>标签用于把某一个对象存在指定的域范围内,或者设置Web域中的java.util.Map类型的属性对象或JavaBean类型的属性对象的属性。
例:
<jsp:useBeanid="user" class="cn.web.User" scope="page"/>
<c:set target="${user}"property="password" value="123456"/>
<c:set var="username"value="张三"scope="session"/>
用户名:<c:outvalue="${user.username}"/><br/>
Value; 用于指定属性值 var :用于指定要设置的Web域属性的名称
Target : 用于指定要设置属性的对象,这个对象必须是JavaBean对象或java.util.Map对象
property : 用于指定当前要为对象设置的属性名称
<c:remove>标签 :用于删除各种Web域中的属性。
例:<% request.setAttribute("name","哈哈哈");%>
<c:removevar="name"/> var :表示需要删除的属性名
用户名:<c:outvalue="${name}"/> 输出为空白字符串 “”
<c:catch>标签:用于捕获嵌套在标签体中的内容抛出的异常
例:
<%try{
int a = 10/0;
}catch(Exception e)
{
e.printStackTrace();
}%>
<c:catchvar="exce">
<% int a = 10/0;%>
</c:catch>
<c:outvalue="${exce.message}"/>
<c:if test=“”>标签 :可以构造简单的“if-then”结构的条件表达式
Test : 表示决定是否处理标签体中的内容的条件表达式
var :用于指定将test属性的执行结果保存到某个Web域中的某个属性的名称
<c:forEach>标签:用于对一个集合对象中的元素进行循环迭代操作,或者按指定的次数重复迭代执行标签体中的内容。
Var : 指定将当前迭代到的元素保存到page这个Web域中的属性名称
items : 将要迭代的集合对象
VarStatus : 指定将代表当前迭代状态信息的对象保存到page这个Web域中的属性名称
begin : 如果指定items属性,就从集合中的第begin个元素开始进行迭代,begin的索引值从0开始编号;如果没有指定items属性,就从begin指定的值开始迭代,直到end值时结束迭代.
Step: 指定迭代的步长,即迭代因子的迭代增量(默认为1)
<c:param>标签
在JSP页面进行URL的相关操作时,经常要在URL地址后面附加一些参数。<c:param>标签可以嵌套在<c:import>、<c:url>或<c:redirect>标签内,为这些标签所使用的URL地址附加参数。<c:param>标签在为一个URL地址附加参数时,将自动对参数值进行URL编码,例如,如果传递的参数值为“中国”,则将其转换为“%d6%d0%b9%fa”后再附加到URL地址后面,这也就是使用<c:param>标签的最大好处。
http://localhost:808/servlet/MyServlet?name=“中国”
示例:<c:param name=“name” value=“中国" />
<c:url>标签:用于在JSP页面中构造一个URL地址,其主要目的是实现URL重写。URL重写就是将会话标识号以参数形式附加在URL地址后面
例:
<body>
<ahref='/day18/result.jsp?name=<%=URLEncoder.encode("中文","UTF-8")%>'>
传中文(脚本版)
</a>
<hr/>
<c:urlvalue="/result.jsp"var="myURL">
<c:paramname="name" value="中文"/>
</c:url>
<ahref="${myURL}">
传中文(标签版)
</a>
</body>
Value ; 指定要构造的URL,/表示dayXX(web工程名)
Var:指定将构造出的URL结果保存到Web域中的属性名称
Scope: 指定将构造出的URL结果保存到哪个Web域中
<c:redirect>标签:用于将当前的访问请求转发或重定向到其他资源,它可以根据url属性所指定的地址,执行类似<jsp:forward>这个JSP标准标签的功能,将访问请求转发到其他资源;或执行response.sendRedirect()方法的功能,将访问请求重定向到其他资源。
例:<c:redirecturl="/c_forEach.jsp"/>
url : 指定要转发或重定向到的目标资源的URL地址
JSTL自定义标签:
a.配置*.tld文件。
b.定义一个类继承SimpleTagSupport,重写doTag()方法。
c.通过jsp页面输出到浏览器。
d.
EL 全名为ExpressionLanguage
EL表达式主要用于替换JSP页面中的脚本表达式,以从各种类型的web域中检索java对象、获取数据。(某个web域中的对象,访问javabean的属性、访问list集合、访问map集合、访问数组)
获取路径如: myday8(web应用工程名) ${pageContext.request.contextPath}
<ahref="${pageContext.request.contextPath}/jstl/froeach.jsp">
下载
</a>
EL表达式语言中定义了11个隐含对象,
语法:${隐式对象名称} :获得对象的引用
pageContext 对应于JSP页面中的pageContext对象(注意:取的是pageContext对象。)
pageScope 代表page域中用于保存属性的Map对象
requestScope 代表request域中用于保存属性的Map对象
sessionScope 代表session域中用于保存属性的Map对象
applicationScope 代表application域中用于保存属性的Map对象\
param 表示一个保存了所有请求参数的Map对象
paramValues
表示一个保存了所有请求参数的Map对象,它对于某个请求参数,返回的是一个string[],从0开始
${paramValues.like[0]}
header 表示一个保存了所有http请求头字段的Map对象
headerValues
同上,返回string[]数组。注意:如果头里面有“-”,例Accept-Encoding,则要headerValues[“Accept-Encoding”]
cookie 表示一个保存了所有cookie的Map对象
initParam 表示一个保存了所有web应用初始化参数的map对象
测试headerValues时,如果头里面有“-”,例Accept-Encoding,则要headerValues[“Accept-Encoding”]
测试cookie时,例${cookie.key}取的是cookie对象,如访问cookie的名称和值,须${cookie.username.name}或${cookie.username.value}
new Cookie(“username”,”jack”);
EL自定义函数开发:
步骤:
1.编写一个Java类的静态方法。
2.编写标签库描述符(tld)文件,在tld文件中描述自定义函数,在WEB-INF/目录下。
3.在JSP页面中导入和使用自定义函数。
4.编写完标签库描述文件后,需要将它放置到<web应用>\WEB-INF目录中或WEB-INF目录下的除了classes和lib目录之外的任意子目录中。
5.TLD文件中的<uri>元素用指定该TLD文件的URI,在JSP文件中需要通过这个URI来引入该标签库描述文件。
6. < function>元素用于描述一个EL自定义函数,其中:
<name>子元素用于指定EL自定义函数的名称。
<function-class>子元素用于指定完整的Java类名。
<function-signature>子元素用于指定Java类中的静态方法的签名,方法签名必须指明方法的返回值类型及各个参数的完整类型,各个参数之间用逗号分隔。
*.TLD:文件配置
1)写一个普通类,无需扩展类或现实接口
publicstatic String filter(String message) {}
2)在WEB-INF/目录下写一个*.tld文件,目的是通知Web服务器有一个自定义函数,在部署时通知
<tlib-version>1.0</tlib-version>(函数的版本号)
<short-name>el</short-name>(函数的前缀名)
<uri>http://java.sun.com/jsp/jstl/el</uri>(函数来自到哪里)
<function>
<name>filterString</name>(在JSP页面中出现的函数名)
<function-class>cn.itcast.web.jsp.el.FilterFunction</function-class>(函数对应的处理类全路径)
<function-signature>java.lang.Stringfilter(java.lang.String)</function-signature>
(处理类中函数的签名,引用类型需要使用全路径,基本类型不需要全路径名)
</function>
3)在JSP中引用函数
<%@ pagelanguage="java" pageEncoding="UTF-8"%>
<%@ tagliburi="http://java.sun.com/jsp/jstl/el" prefix="el" %>
<!DOCTYPE HTMLPUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<body>
${el:filterString("<a href='#'>点点</a>")}<br/>
</body>
</html>
JSP-国际化
i18n:internationalization
创建资源包和资源文件:
1.中文、英文环境相对应的资源文件名则为:
“myproperites_zh_CN.properties”
“myproperites_en_US.properties”
2.实现方式:
ResourceBundle类提供了一个静态方法getBundle,该方法用于装载资源文件,并创建ResourceBundle实例:
Locale currentLocale = Locale.getDefault();
ResourceBundle myResources =
ResourceBundle.getBundle(basename, currentLocale);
basename为资源包基名(且必须为完整路径)。
如果与该locale对象匹配的资源包子类找不到。一般情况下,则选用默认资源文件予以显示。
加载资源文件后,程序就可以调用ResourceBundle实例对象的 getString方法获取指定的资源信息名称所对应的值。
Stringvalue = myResources.getString(“key");
3.国际化标签:
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<fmt:setLocale value=“${pageContext.request.locale}”/>(页面的Locale)
<fmt:setBundle basename=“cn/itcast/web/jsp/config/hello”/>(资源文件基名)
<fmt:message
key=“itcast.hello”>(资源文件key)
<fmt:param>
value=“itcast.name”/>(资源文件占位符)
设置POST请求消息的字符集编码
<fmt:requestEncodingvalue="UTF-8"/>
动态数据的国际化:
使用的包java.util包和java.text包
Locale 类
Locale 实例对象代表一个特定的地理,政治、文化区域。
DateFormat类可以将一个日期/时间对象格式化为表示某个国家地区的日期/时间字符串,项目中不提倡用Date类型,可以使用Calendar类,它还定义了一些用于描述日期/时间的显示模式的 int型的常量,包括FULL, LONG, MEDIUM, DEFAULT, SHORT,实例化DateFormat对象时,可以使用这些常量,控制日期/时间的显示长度
实例化DateFormat类方式
getDateInstance(intstyle, Locale aLocale):以指定的日期显示模式和本地信息来获得DateFormat实例对象,该实例对象不处理时间值部分。
getTimeInstance(int style, Locale aLocale):以指定的时间显示模式和本地信息来获得DateFormat实例对象,该实例对象不处理日期值部分。
getDateTimeInstance(int dateStyle, int timeStyle, Locale aLocale):以单独指定的日期显示模式、时间显示模式和本地信息来获得DateFormat实例对象。
2.DateFormat对象的方法:
format:将日期/时间对象格式化为符合某个本地环境习惯的字符串。
parse:将符合某个本地环境习惯的日期/时间字符串解析为日期/时间对象
注意:parse和format完全相反,一个是把date时间转化为相应地区和国家的显示样式,一个是把相应地区的时间日期转化成date对象,该方法在使用时,解析的时间或日期要符合指定的国家、地区格式,否则会抛异常
国际化标签:
<jsp:useBean id="time"class="java.util.Date" scope="page"/>
中国:
<fmt:formatDatevalue="${time}" type="both" dateStyle="Full"timeStyle="default" timeZone="GMT+08:00"/>
美国:
<fmt:formatDatevalue="${time}" type="both" dateStyle="Full"timeStyle="default" timeZone="GMT-06:00"/>
NumberFormat类
NumberFormat可以将一个数值格式化为符合某个国家地区习惯的数值字符串,也可以将符合某个国家地区习惯的数值字符串解析为对应的数值
方法:
getIntegerInstance(Localelocale):以参数locale对象所标识的本地信息来获得处理整数的NumberFormat实例对象
getCurrencyInstance(Localelocale):以参数locale对象所标识的本地信息来获得处理货币的NumberFormat实例对象
getPercentInstance(Localelocale):以参数locale对象所标识的本地信息来获得处理百分比数值的NumberFormat实例对象
国际化标签:
<body>
将字符串转成整型: <fmt:formatNumbertype="number" value="3.555" pattern=".0"/>
<hr/>
将字符串转成货币: <fmt:formatNumbertype="currency"value="3.555"/>
<hr/>
将字符串转成百分比:<fmt:formatNumbertype="percent"value="3.555"/>
<hr/>
</body>
文件上传概述:
如何在web页面中添加上传输入项?
<input type=“file”>标签用于在web页面中添加文件上传输入项,设置文件上传输入项时须注意:
1、必须要设置input输入项的name属性,否则浏览器将不会发送上传文件的数据。
2、必须把form的enctype属值设为multipart/form-data.设置该值后,浏览器在上传文件时,将把文件数据附带在http请求消息体中,并使用特定的格式对上传的文件进行描述,以方便接收方对上传数据进行解析和处理。
实现步骤
1、创建DiskFileItemFactory对象,设置缓冲区大小和临时文件目录
2、使用DiskFileItemFactory 对象创建ServletFileUpload对象,并设置上传文件的大小限制。
3、调用ServletFileUpload.parseRequest方法解析request对象,得到一个保存了所有上传内容FileItem的List对象。
4、对list进行迭代,每迭代一个FileItem对象,调用其isFormField方法判断是否是上传文件
为普通表单字段,则调用getFieldName、getString方法得到字段名和字段值
为上传文件,则调用getName、 getInputStream方法得到数据输入流,从而读取上传数据。
需要使用的类和方法:
1.DiskFileItemFactory是创建 FileItem 对象的工厂,这个工厂类常用方法:
public voidsetSizeThreshold(int sizeThreshold) 设置内存缓冲区的大小,默认值为10K。当上传文件大于缓冲区大小时, fileupload组件将使用临时文件缓存上传文件。
public void setRepository(java.io.File repository) 指定临时文件目录,默认值为System.getProperty("java.io.tmpdir").
2.ServletFileUpload 负责处理上传的文件数据,并将表单中每个输入项封装成一个 FileItem 对象中。常用方法有:
booleanisMultipartContent(HttpServletRequest request)判断上传表单是否为multipart/form-data类型
List parseRequest(HttpServletRequest request)解析request对象,并把表单中的每一个输入项包装成一个fileItem 对象,并返回一个保存了所有FileItem的list集合。
setHeaderEncoding(java.lang.String encoding)
设置编码格式,解决上传中文名文件的问题
3.中文文件名乱码问题
文件名中文乱码问题,可调用ServletFileUpload的setHeaderEncoding方法,或者设置request的setCharacterEncoding属性
4.临时文件的删除问题
由于文件大小超出DiskFileItemFactory.setSizeThreshold方法设置的内存缓冲区的大小时,commons-fileupload组件将使用临时文件保存上传数据,因此在程序结束时,务必调用FileItem.delete方法删除临时文件。
delete方法的调用必须位于流关闭之后,否则会出现文件占用,而导致删除失败的情况。
Filter: 过滤器
1)Filter是位于服务器中,用于拦截客户端和Web资源之间的访问
2)Filter可以客户端访问的Web资源作预先处理,如果符合要求,则将请求交给Web资源;如果不符合要求,则不将请求交给Web资源,类似于一个判段角色
3)将Web请求返回时,也会经过Filter,此时可以通过Filter对该请求做后备处理
Filter开发分为二个步骤:
1.编写java类实现Filter接口,并实现其doFilter方法。
2.在 web.xml 文件中使用<filter>和<filter-mapping>元素对编写的filter类进
行注册,并设置它所能拦截的资源。
Filter链:
1.在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链。
2.web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链 FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。
Filter生命周期
1)Filter是一个单例的,每个用户访问的都是同一个Filter过滤器实例
2)防止线程安全问题,线程安全有三要素
>>单例
>>实例变量
>>对实例变量的更新操作
如果以上三个问题[同时]满足,就要用"加锁"来解决
3)部署Web应用时,执行空参构造方法(),再执行初始化方法
第一,二,N次请求,执行doFilter()方法,
当重新部署web应用时,由web服务器先销毁原Filter实例,在销毁[之前]执行destory()方法,然后立即创建新的Filter实例。
4)注意:过滤器,是不能够直接访问的
FilterConfig接口
1.在配置filter时,可以使用<init-param>为filter配置一些初始化参数,当web容器实例化Filter对象,调用其init方法时,会把封装了filter初始化参数的filterConfig对象传递进来。因此开发人员在编写filter时,通过filterConfig对象的方法,就可获得:
String getFilterName():得到filter的名称。
String getInitParameter(String name): 返回在部署描述中指定名称的初始化参数的值。如果不存在返回null.
Enumeration getInitParameterNames():返回过滤器的所有初始化参数的名字的枚举集合。
public ServletContext getServletContext():返回Servlet上下文对象的引用。
Filter常见应用
1. 禁止浏览器缓存所有动态页面的过滤器:
response.setDateHeader("Expires",-1); Expires数据头:值为GMT时间值,为-1指浏览器不要缓存页面
response.setHeader("Cache-Control","no-cache");
response.setHeader("Pragma","no-cache");
Cache-Control响应头有两个常用值:
no-cache指浏览器不要缓存当前页面。
max-age:xxx指浏览器缓存页面xxx秒
Filter的部署—映射Filter
1.<filter-mapping>元素用于设置一个 Filter 所负责拦截的资源。
2.一个Filter拦截的资源可通过两种方式来指定:Servlet名称和资源访问的请求路径
3.<filter-name>子元素用于设置filter的注册名称。该值必须是在<filter>元素中
明过的过滤器的名字
4.<url-pattern>设置 filter 所拦截的请求路径(过滤器关联的URL样式)
5.<servlet-name>指定过滤器所拦截的Servlet名称。
6.<dispatcher>指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默认REQUEST。用户可以设置多个<dispatcher> 子元素用来指定 Filter 对资源的多种调用方式进行拦截
1 映射Filter的细节
1)在默认情况下,Filter只过滤用户直接请求的Web资源,例如在地栏直接访问,重定向,超链接。
除此之外,不会被过滤,例如:转发
Filter映射有四个种方式
>>REQUEST:默认(地栏直接访问,重定向,超链接)
>>FORWARD:专用于Web资源之间的转发
>>INCLUDE:专用于Web资源之间的包含
>>ERROR:专用于Web资源的[出错]情况,通常不单独使有,要配REQUEST/FORWARD/INCLUDE使用
如果没有出错,ERROR不会执行,项目中可以使用单独的ERROR过滤器
注意:ERROR过滤方式,需要适合在web.xml文件中的全局错误声明
3)一旦在<filter-mapping>中显示配置<dispatcher>,那么默认不会存在,类似于空参构造方法
装饰设计模式
1)当一个类的某个或某些方法不能满足应用的需求,此时可以继承该类,重写对应的方法,前提是该类非final类型
2)装饰设计模式主要运用在对某个不满足应用需求的类的方法而产生的
3)开发步骤:
>>普通类继承需要被装饰的类,例如:BufferedReader,哪个类的方法不满足需求,该类就叫做需要被装饰的类
>>用实例变量的方式记住需要被装饰的类
>>通过构造方式为实例变量覆值
>>争对不满足需求的方法重写
>>对于满足需求的方法,直接调用需要被装饰的类的相关方法[可省]
4 总结Servlet和Filter
1)Servlet和Filter是一个互补的技术
2)Servlet比较适合做核心的业务控制
Filter比较适合做预先处理后后备处理
3)Servlet和Filter在细粒度分层结构中,位于控制层,可以调用业务层
Servlet监听器
监听的事件源分别为 ServletContext,HttpSession 和 ServletRequest 这三个域对象
监听器划分为三种类型。
监听三个域对象创建和销毁的事件监听器
监听域对象中属性的增加和删除的事件监听器
监听绑定到HttpSession域中的某个对象的状态的事件监听器。
监听ServletContext域对象创建和销毁
1.ServletContextListener接口用于监听 ServletContext 对象的创建和销毁事件。
2.当 ServletContext 对象被创建时,激发contextInitialized(ServletContextEvent sce)方法
当 ServletContext 对象被销毁时,激发contextDestroyed(ServletContextEvent sce)方法。
3.和编写其它事件监听器一样,编写servlet监听器也需要实现一个特定的接口,并针对相应动作覆盖接口中的相应方法。
4.和其它事件监听器略有不同的是,servlet监听器的注册不是直接注册在事件源上,而是由web容器负责注册,开发人员只需在web.xml文件中使用<listener>标签配置好监听器,web容器就会自动把监听器注册到事件源中(即:通过反射机制)。
5.一个 web.xml 文件中可以配置多个Servlet 事件监听器,web 服务器按照它们在 web.xml 文件中的注册顺序来加载和注册这些 Serlvet 事件监听器。
ServletContext事件源
创建初始化销毁:ServletContextListener
属性变化:ServletContextAttributeListener
何时产生:部署Web应用时
何时销毁:重新部署Web应用时,或Web服务器停止再重新启动
3)ServletRequest事件源
创建初始化销毁:ServletRequestListener
属性变化:ServletRequestAttributeListener
何时产生:同一个客户端第一次请求会产生一个ServletRequest,该客户端随后多次请求,都是同一个ServletRequest对象别一个客户端访问,会产生不同的ServletRequest对象
何时数据销毁:每次请求的数据不能被第二次请求共享,只能在当次请求中取得数据,这也是为什么重定向情况下,request域中的数据不能共享的原因多次请求request域中的数据不共享
4)HttpSession事件源
创建销毁:HttpSessionListener
属性变化:HttpSessionAttributeListener
何时产生:新会话,首次访问jsp;在原会话中,多次请求,不会创建新HttpSession
当关闭浏览器后,不会销毁原HttpSession
何时销毁:
>>在web.xml文件中,配置HttpSession的有效生命时间,当有效生命时间完成后,Web服务器会依次删除位于Web服务器中的HttpSession,但是删除的时间不精确
>>自已定义一个HttpSession扫描器,来较精确的控制HttpSession的销毁时间
>>当session.invalidate()方法执行时,会被执行attributeRemoved()和sessionDestroyed()
>>当session.removeAttribute()方法执行时,会被执行attributeRemoved()
Session 绑定的事件监听器
保存在 Session 域中的对象可以有多种状态:绑定到 Session 中;从 Session 域中解除绑定;随 Session 对象持久化到一个存储设备中[钝化];随 Session 对象从一个存储设备中恢复[激活]
Servlet 规范中定义了两个特殊的监听器接口来帮助 JavaBean 对象了解自己在 Session 域中的这些状态:HttpSessionBindingListener接口和HttpSessionActivationListener接口 ,实现这两个接口的类不需要 web.xml 文件中进行注册,因为事件源和监听器都是JavaBean自身
实现了HttpSessionBindingListener接口的JavaBean 对象可以感知自己被绑定到 Session 中和从 Session 中删除的事件
当对象被绑定到HttpSession 对象中时,web 服务器调用该对象的 void valueBound(HttpSessionBindingEventevent) 方法
当对象从HttpSession 对象中解除绑定时,web 服务器调用该对象的 voidvalueUnbound(HttpSessionBindingEvent event)方法
HttpSessionActivationListener接口
实现了HttpSessionActivationListener接口的JavaBean 对象可以感知自己被激活和钝化的事件
当绑定到HttpSession 对象中的对象将要随 HttpSession 对象被钝化之前,web 服务器调用如下方法sessionWillPassivate(HttpSessionBindingEventevent) 方法
当绑定到HttpSession 对象中的对象将要随 HttpSession 对象被活化之后,web 服务器调用该对象的void sessionDidActive(HttpSessionBindingEvent event)方法
案例1:手动销毁session
public class MyservletHttpSession implements HttpSessionListener, ServletContextListener{
private List<HttpSession> sessionlist = newArrayList<HttpSession>();
private Timer timer= new Timer();
public voidsessionCreated(HttpSessionEvent arg0) {
System.out.println("session的创建");
//获取每个用户的session域对象
HttpSession session = arg0.getSession();
//获取每一个回话的id
String id = session.getId();
//查看session的创建时间
System.out.println(id+","+newDate(session.getCreationTime()));
synchronized (MyservletHttpSession.class) {
//将用户的session加入集合中
sessionlist.add(session);
}
}
public voidsessionDestroyed(HttpSessionEvent arg0) {
System.out.println("session的销毁");
HttpSession session = arg0.getSession();
//获取每一个回话的id
String id = session.getId();
//查看session的销毁时间
System.out.println(id+","+new Date().toLocaleString());
}
public voidcontextInitialized(ServletContextEvent arg0) {
//初始化定时器
timer.schedule(new MyTimer(sessionlist),5000,1000);
}
public voidcontextDestroyed(ServletContextEvent arg0) {
}
}
//定时器类(作用:每个1分钟在集合中查找session一次,并判断session的创建时间与当前时间只差是否大于1分钟,大于1分钟,销毁session)
class MyTimer extends TimerTask
{
private List<HttpSession> sessionlist;
public MyTimer(List<HttpSession> sessionlist){
this.sessionlist =sessionlist;
}
public void run() {
if(sessionlist.size()>0)
{
synchronized (MyservletHttpSession.class) {
//迭代取出集合中的每一个HttpSession
Iterator<HttpSession> it = sessionlist.iterator();
while(it.hasNext())
{
HttpSession session = it.next();
//获取session的最后一次的访问时间
long end = session.getLastAccessedTime();
//获取当前时间
long currnet = System.currentTimeMillis();
//判断session的存储时间,如果大于1分钟,即销毁该session
long result = currnet-end;
if(result>5000)
{
it.remove();
//销毁session
session.invalidate();
}
}
}
}
}
}
案例2: ServletContextListener
//创建表
publicclass SystemDao {
public void createTable() throws SQLException
{
String sql = "create table if not exists systemdao(idint primary key auto_increment,name varchar(20) not null)";
QueryRunner runner = new QueryRunner(C3P0Util.getDataSource());
runner.update(sql);
}
public void updateTable() throws SQLException
{
String sql = "update systemdao set name ='你好' where id=1";
QueryRunner runner = new QueryRunner(C3P0Util.getDataSource());
runner.update(sql);
}
public void dropTable() throws SQLException
{
String sql = "drop table if exists systemdao";
QueryRunner runner = new QueryRunner(C3P0Util.getDataSource());
runner.update(sql);
}
public void insertTable() throws SQLException
{
String sql = "insert into systemdao(name) value('呵呵')";
QueryRunner runner = new QueryRunner(C3P0Util.getDataSource());
runner.update(sql);
}
}
测试类: SystemListener
public class SystemListener implements ServletContextListener {
private Timer timer = new Timer();
public void contextInitialized(ServletContextEventarg0) {
//启动定时器
timer.schedule(new TimerTask(){
public void run() {
DateFormat dataformat = DateFormat.getDateTimeInstance(DateFormat.FULL,DateFormat.DEFAULT,Locale.CHINA);
String time = dataformat.format(new Date());
System.out.println(time);
}
}, 5000, 1000);
//创建表进行数据的初始化
SystemDao dao = new SystemDao();
try {
dao.createTable();
dao.insertTable();
dao.updateTable();
} catch (SQLException e) {
e.printStackTrace();
}
}
public void contextDestroyed(ServletContextEventarg0) {
SystemDao dao = new SystemDao();
try {
//删除表
dao.dropTable();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Base64编码
BASE64Encoderencoder = new BASE64Encoder();
加密:
encoder.encode(username.getBytes());
encoder.encode(password.getBytes());
Base64解码
BASE64Decoderdecoder = new BASE64Decoder();
解密:
new String(decoder.decodeBuffer(username))
new String(decoder.decodeBuffer(password))
案例1: 加密和解密
//BASE64加密和解密过程
public class Demo1 {
public static void main(String[] args) throws IOException {
String username = "bbb";
String password = "123456";
//加密
BASE64Encoder encoder = new BASE64Encoder();
String usrename64 =encoder.encode(username.getBytes());
String password64 =encoder.encode(password.getBytes());
System.out.println(usrename64);
System.out.println(password64);
//解密
BASE64Decoder decoder = new BASE64Decoder();
byte[] name = decoder.decodeBuffer(usrename64);
byte[] pass = decoder.decodeBuffer(password64);
System.out.println(new String(name));
System.out.println(new String(pass));
}
}
案例2:使用cookie存储用户信息
public class CookieBASE64Servletextends HttpServlet {
public void doGet(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
/* Stringusername = "李四";
//加密,cookie中所存储的中文只能是以base64进行加密和解密
BASE64Encoder encoder = newBASE64Encoder();
username =encoder.encode(username.getBytes());
//创建cookie
Cookie cookie = new Cookie("username",username);
//设置cookie的存活时间
cookie.setMaxAge(1*60*60);
//将cookie写入浏览器中
response.addCookie(cookie);
*/
//取出cookie
Cookie[] cookies = request.getCookies();
Cookie newCookie = null;
for(Cookie cookie : cookies)
{
if(cookie.getName().contains("username"))
{
newCookie = cookie;
}
}
if(newCookie!=null)
{ //获取cookie的名字
String name = newCookie.getName();
String value = newCookie.getValue();
//解密
BASE64Decoder decoder = new BASE64Decoder();
byte[] buf = decoder.decodeBuffer(value);
System.out.println(name +":"+new String(buf));
}
}
}
案例3:使用文件存储用户信息
public class Demo2 {
//将用户信息加密后写入文件中
public static void main(String[] args) throws Exception {
File file = new File("e:/base64.text");
//FileOutputStream有自动创建文件的功能
FileOutputStream out= new FileOutputStream(file);
PrintWriter pw = new PrintWriter(out);
pw.write("username=哈哈\r\n");
pw.write("password=123456");
pw.flush();
pw.close();
}
}
JavaMail开发
1.邮件服务器:要在Internet上提供电子邮件功能,必须有专门的电子邮件服务器
2. 电子邮箱: 电子邮箱(E-mail地址)的获得需要在邮件服务器上进行申请 ,确切地说,电子邮箱其实就是用户在邮件服务器上申请的一个帐户。
邮件传输协议和邮件服务器类型
1.SMTP协议(SimpleMail Transfer Protocol): 通常我们也把处理用户smtp请求(邮件发送请求)的邮件服务器称之为SMTP服务器。(25端口)
2.POP3协议POP3(PostOffice Protocol 3): 通常我们也把处理用户pop3请求(邮件接收请求)的邮件服务器称之为POP3服务器。(110端口)
SMTP协议
WindowXP平台下,进入cmd命令状态:
------------------------------------aaa@zhaojun.com向bbb@zhaojun.com手工发送邮件步骤:
相同域:
telnet 127.0.0.1 25 (SMTP服务器固定端口号)<回车>
ehlo 用户名/可任意 <回车\r\n>
auth login <回车>经过base64编码后的用户名和密码
YWFh 经过Base64加密后的用户名aaa<回车>
MTIzNDU2 经过Base64加密后的密码123456<回车>
mail from:<aaa@wang.com> <回车>
rcpt to:<bbb@wang.com> <回车>
data <回车>
from:aaa@wang.com <回车>
to:bbb@wang.com <回车>
subject:test <回车>
Hello! How are you doing! <回车> 发送的内容
. <回车>点号代表邮件内容的结束
quit <回车> 退出
POP3协议
------------------------------------bbb@zhaojun.com手工接收邮件
相同域
telnet 127.0.0.1 110 (POP3服务器固定端口号)<回车>
user bbb <回车>
pass 123456 <回车>
stat <回车> 返回邮箱的统计信息(字节数)
list 2 <回车> 返回某一封邮件的统计信息
retr 1 <回车>
quit <回车>
JavaMail常用API
1)MimeMessagemessage = new MimeMessage(Session.getDefaultInstance(new Properties()));
Session表示发送到邮件服务器的相关属性<后面继续讲>
2)message.setFrom(new InternetAddress("aaa@zhaojun.com"));
3)message.setRecipient(RecipientType.TO,newInternetAddress("bbb@zhaojun.com"));
4)message.setSubject("bbs");
5)message.setContent("邮件内容","text/html;charset=ISO8859-1");
6)message.writeTo(new FileOutputStream("d:/demo1.eml"));
7)DataHandler dh= new DataHandler(newFileDataSource("src/cn/itcast/web/config/photo.jpg"));
读取附件或图片
8)图片:image.setContentID("imageAID");设置编号
9)附件:append.setFileName(dh.getName());设置附件的名字
10)MimeMultipartmm = new MimeMultipart();
11)mm.addBodyPart(text);
12)mm.setSubType("related");只适合于文本+图片,不限文本和图片的数量
mm.setSubType("mixed");只适合到文本+附件,不限文本和附件的数量
13)message.setContent(mm);一定要将关系设置到邮件中,否则邮件就是空邮件
5 创建简单邮件
>>直接在message对象中设置内容,即MimeMessage.setContent()
6 创建复杂邮件
>>一封邮件有多少个部分组成,就有多少个BodyPart
>>不管是文本前,还是图片,最终加入到MimeMultipart关系中的顺序一定是:[文本先,图片后],关系名字叫"related"
>>图片设置编号,附件设置名字
>>不管是文本前,还是附件前,最终加入到MimeMultipart关系中的顺序[任意],关系名字叫"mixed"
案例1:
//JAVAMail创建邮件
public class MessageMail {
public static void main(String[] args) throws Exception{
//创建一个邮件
MimeMessage message = new MimeMessage(Session.getInstance(new Properties()));
//设置mail: from
message.setFrom(new InternetAddress("bbb@wang.com"));
//设置mail: to
message.setRecipient(RecipientType.TO,new InternetAddress("ahha@wang.com"));
//设置发送发送方式cc,bcc
message.setSubject("cc");
//设置发送的内容
message.setContent("<font color='red'>创建一个邮件</font>","text/html;charset=UTF-8");
//将内存中的message写入硬盘中
message.writeTo(new FileOutputStream(new File("e:/mail.eml")));
}
}
案例2.
//创建复杂邮件(文本+图片)
public class Demo2 {
public static void main(String[] args)throws Exception {
MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()));
message.setFrom(new InternetAddress("aaa@zhaojun.com"));
message.setRecipient(RecipientType.TO,new InternetAddress("bbb@zhaojun.com"));
message.setSubject("text+img");
//文本
MimeBodyPart text = new MimeBodyPart();
text.setContent("<imgsrc='cid:imageBID'><br/>this is aphoto<br/><imgsrc='cid:imageAID'><br/>","text/html;charset=ISO8859-1");
//图片A
MimeBodyPart imageA = new MimeBodyPart();
//采用类去加载图片
DataHandler dh = new DataHandler(new FileDataSource("src/cn/itcast/web/config/photo.jpg"));
//设置图片加载器为DataHandler
imageA.setDataHandler(dh);
//设置图片的编码,该编号是整个邮件中的唯一标识符
imageA.setContentID("imageAID");
//图片B
MimeBodyPart imageB = new MimeBodyPart();
//采用类去加载图片
dh = new DataHandler(new FileDataSource("src/cn/itcast/web/config/zgl.jpg"));
//设置图片加载器为DataHandler
imageB.setDataHandler(dh);
//设置图片的编码,该编号是整个邮件中的唯一标识符
imageB.setContentID("imageBID");
//设置文本和图片的关系,MimeMultipart相关于他们的关系,类似于容器
MimeMultipart mm = new MimeMultipart();
//将文本和图片加入到关系中
mm.addBodyPart(text);//文本
mm.addBodyPart(imageA);//图片A
mm.addBodyPart(imageB);//图片B
mm.setSubType("related");
//设置邮件的内容
message.setContent(mm);
//将邮件输出到硬盘
message.writeTo(new FileOutputStream("d:/demo2.eml"));
}
}
RFC822文档规定了如何编写一封简单邮件
邮件头和邮件体,两者使用空行分隔
邮件头
from字段
to字段
subject字段
cc、bcc字段
邮件体
邮件内容
创建邮件—— MIME协议
1.JavaMail常用API
//发送邮件采用的协议(小写smtp)
props.put("mail.transport.protocol","smtp");
//发送到哪台邮件服务器的IP地址
props.put("mail.host","192.168.10.176");
>>Sessionsession = Session.getDefaultInstance(props);
>>Transport transport =session.getTransport();
>>transport.connect("aaa@zhaojun.com","123456");
>>transport.send(messsage);
>>transport.close();
发送邮件步骤:
1,创建propertiesProperties p = new Properties()
2.设置发送邮件的协议:
p.put("mail.transport.protocol", "smtp");
p.put("mail.host", "127.0.0.1");
3.创建session对象: Session session =Session.getInstance(p)
4.建立连接:
Transport transport = session.getTransport();
transport.connect("1057718341@qq.com","123456");
5.发送邮件 : 创建邮件
5.1 :创建邮件 : MimeMessage message = new MimeMessage(session)
5.2:设置邮件的 from , to ,主题,内容(设置编码方式)
message.setFrom(new InternetAddress("1057718341@qq.com"));
message.setRecipient(RecipientType.TO,new InternetAddress(email));
message.setSubject("欢迎光临");
message.setContent("注册成功码:","text/html;charset=UTF-8");
6.发送: transport.send(message);
7.关闭资源: transport.close();
例:
public class MailUtil {
public static void send(String username, String password,String email)throws Exception { //创建properties
Properties p = new Properties();
//设置发送邮件的协议
p.put("mail.transport.protocol","smtp");
p.put("mail.host", "127.0.0.1");
//创建session
Session session = Session.getInstance(p);
//建立连接
Transport transport = session.getTransport();
transport.connect("1057718341@qq.com","123456");
//发送邮件
Message message = cteateMessage(session,username, password,email);
transport.send(message);
transport.close();
}
public static MessagecteateMessage(Session session,String username, String password, String email)throws Exception, MessagingException
{ //创建邮件
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress("1057718341@qq.com"));//158409270@163.com
message.setRecipient(RecipientType.TO,new InternetAddress(email));
message.setSubject("欢迎光临");
message.setContent("注册成功<br/>用户名:"+username+"<br/>密码:"+password+"<br/>邮箱:"+email,"text/html;charset=UTF-8");
return message;
}
}
例: MD5: 加密和解密
public class Md5Util {
private static String[] hex = { "0", "1", "2","3", "4", "5", "6", "7",
"8", "9", "A", "B","C", "D", "E", "F" };
public static String encoder(Stringpassword) throws Exception {
//创建MD5加密
MessageDigest md5 =MessageDigest.getInstance("md5");
// 将原码进行加密
byte[] byteArray =md5.digest(password.getBytes());
// 将字节转换为十六进制数
String passwordMD5 =byteArrayToString(byteArray);
return passwordMD5;
}
// 将byte[]数组转换为Stirng类型
private static String byteArrayToString(byte[] byteArray) {
StringBuffer sb = new StringBuffer();
for (byte b : byteArray) {
sb.append(byteToHexChar(b));
}
return sb.toString();
}
private static Object byteToHexChar(byte b) {
int n = b;
if (n < 0)
n = n + 256;
return hex[n / 16] + hex[n % 16];
}
}
泛型自定义,注解,代理
1. 泛型(Generic):
注意:泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛形的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。
泛形的基本术语,以ArrayList<E>为例:<>念着typeof
ArrayList<E>中的E称为类型参数变量
ArrayList<Integer>中的Integer称为实际类型参数
整个称为ArrayList<E>泛型类型
整个BaseDao<Type>ParameterizedType/Type
例:自定义泛型
//父类(不能含有具体类型)
public class BaseDao<T> {
//即BaseDao<T>类型叫泛型类型,注意是一个整体,
//也可以叫做“参数据类型”,用ParameterizedType接口来表示,它是
//Type接口中子接口
private String tableName;
private Class clazz;
public BaseDao() {
//取得BaseDao类的字节码对象,即Class
Class baseDaoClass = this.getClass();
//取得子类型的参数据类型
Type type =baseDaoClass.getGenericSuperclass();
//将父接口强转成子接口,即只表示"参数据类型(BaseDao<Type>)"
ParameterizedType pt = (ParameterizedType)type;
//取得"参数据类型(BaseDao<Type>)"中的实际类型,即Type类型
Type[] types = pt.getActualTypeArguments();
//取得第一个实际类型,即Type类型
type = types[0];
//class也是一种数据类型
this.clazz = (Class) type;
//取得表名
int index = this.clazz.getName().lastIndexOf(".");
this.tableName =this.clazz.getName().substring(index+1).toLowerCase();
}
//根据编号查询内容
public T findById(int id)throws SQLException{
T t = null;
QueryRunner runner = new QueryRunner(JdbcUtil.getDataSource());
String sql = "select * from " +tableName + " where id=?";
t = (T)runner.query(sql,id,new BeanHandler(clazz));
return t;
}
}
继承类:
public class TopicDao extends BaseDao<Topic> {
}
public class TypeDao extends BaseDao<Type> { //"参数化类型"
}
测试类:
public static void main(String[] args) throws Exception {
//子类
TypeDao typeDao = new TypeDao();
Type type = typeDao.findById(1);
System.out.println(type.getId()+":"+type.getTitle());
System.out.println("-------------------------------");
TopicDao topicDao = new TopicDao();
Topic topic = topicDao.findById(1);
System.out.println(topic.getId()+":"+topic.getTitle());
}
2,JDK5中内置注解:
>>@Override: 限定重写父类方法, 该注解只能用于方法
>>@Deprecated: 用于表示某个程序元素(类, 方法等)已过时
>>@SuppressWarnings: 抑制编译器警告.
元注解:被注解修饰的注解的注解称为原注解
1.元 Annotation指修饰Annotation的Annotation。JDK中定义了如下元Annotation:
@Retention:只能用于修饰一个 Annotation 定义, 用于指定该 Annotation 可以保留的域, @Rentention包含一个 RetentionPolicy 类型的成员变量, 通过这个变量指定域。
RetentionPolicy.CLASS:编译器将把注解记录在 class 文件中. 当运行 Java 程序时, JVM 不会保留注解. 这是默认值
RetentionPolicy.RUNTIME:编译器将把注释记录在 class 文件中. 当运行 Java 程序时, JVM 会保留注解. 程序可以通过反射获取该注释
RetentionPolicy.SOURCE:编译器直接丢弃这种策略的注释
@Target:指定注解用于修饰类的哪个成员. @Target 包含了一个名为 value,类型为ElementType(JDK6)的成员变量。
@Documented:用于指定被该元 Annotation 修饰的 Annotation 类将被 javadoc 工具提取成文档。
@Inherited:被它修饰的 Annotation 将具有继承性.如果某个类使用了被 @Inherited 修饰的Annotation, 则其子类将自动具有该注解。
例:自定义注解
//自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbInfo {
String driver() default "com.mysql.jdbc.Driver";
String url() default "jdbc:mysql://127.0.0.1:3306/bbs";
String username() default "root";
String password() default "root";
}
测试类:
public class Demo3 {
@DbInfo
public Connection getConnection()throws Exception{
//取得Demo3的字节码对象Class
Class clazz = Demo3.class;
//取得getConnection()方法
Method method = clazz.getMethod("getConnection",null);
System.out.println(method.getName());
//取得DbInfo注解,注解也是一个类型,即DbInf.class是可以的
DbInfo dbInfo = method.getAnnotation(DbInfo.class);
//取得注解中的属性
String driver = dbInfo.driver();
String url = dbInfo.url();
String username = dbInfo.username();
String password = dbInfo.password();
//再通过DriverManager取得连接
Class.forName(driver);
Connection conn =DriverManager.getConnection(url,username,password);
return conn;
}
public static void main(String[] args) throws Exception {
Demo3 demo = new Demo3();
Connection conn = demo.getConnection();
if(conn!=null){
System.out.println("取得连接");
conn.close();
}
}
}
3 自定义注解和反射注解
1)注释有三种生命周期
>>SOURCE:有且只有在源码级别可见,(不推荐使用)
>>CLASS: 有且只有在源码和字节码级别可见,(默认),但在运行时,不可见
>>RUNTIME:在整个类的运行期周,都可见。(即源码,字节码,运行时)
2)通过反射注解在运行级别获取该注解中的属性值,以替换配置文件
3)元注解:修饰其它注解的注解,注意:元注解也可以被其它注解修饰
>>@Retention(RetentionPolicy.RUNTIME)
>>@Target(ElementType.METHOD):表示该注解可以运在哪些地方,默认情况下,该注解可以出在在任意地方
代理(proxy):
代理模式的作用
为其他对象提供一种代理以控制对目标对象的访问。某些情况下客户不想或不能直接引用另一个对象,而代理对象可在客户端和目标对象间起到中介作用
1.静态代理:
优点
不需要修改目标对象就实现了功能的增加
缺点
真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。如果事先并不知道真实角色则无法使用
一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀
2.动态代理: java.lang.reflect
InvocationHandler接口
Proxy类
InvocationHandler接口仅定义了一个方法
public objectinvoke(Object obj,Method method, Object[] args)
obj一般是指代理类
method是被代理的方法
args为该方法的参数数组
这个抽象方法在代理类中动态实现
Proxy类即为动态代理类,主要方法包括
Object newProxyInstance(ClassLoader loader, Class[ ] interfaces,InvocationHandler h)
返回代理类的一个实例
loader是类装载器
interfaces是真实类所拥有的全部接口的数组
h - 指派方法调用的调用处理程序
静态代理和动态代理的比较
1,静态代理类确实存在,动态代理类在运行期动态生成
2,一个真实角色必须对应一个静态代理角色,而动态代理大大减少了代理类的数量
3,动态代理类不会作实质性的工作,在生成它的实例时必须提供一个handler,由它接管实际的工作(会自动执行handler的invoke方法)
如何代理
调用任务业务方法,都会被代理类拦截,从而调用invoke()方法
动态代理的步骤:
NO1:写一个普通类
NO2:写一个实例变量,记录代理谁
NO3:使用构造方法为上述实例变量设置值
NO4:写一个普通方法,返回接口,该方法的返回值就是动态代理对象,该动态代理对象也实现了这个接口,该方法内部使用Proxy.newProxyInstance()来动态创建代理对象
例:1
//实现类:过滤器
public class EncodingFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequestreq, ServletResponse res,FilterChain chain) throws IOException, ServletException {
//目标对象
HttpServletRequest request =(HttpServletRequest) req;
HttpServletResponse response =(HttpServletResponse) res;
//创建代理类
MyRequest myRequest = new MyRequest(request);
//动态产生代理对象
HttpServletRequest httpServletRequestProxy =myRequest.getProxy();
//放行请求时,传入代理对象
chain.doFilter(httpServletRequestProxy,response);
}
public void init(FilterConfigfilterConfig) throws ServletException {
}
}
自定义代理类:
class MyRequest{//NO1
private HttpServletRequestrequest; //NO2
public MyRequest(HttpServletRequest request) {//NO3
this.request =request;}
public HttpServletRequest getProxy() {//NO4(核心)
return (HttpServletRequest)Proxy.newProxyInstance(
MyRequest.class.getClassLoader(),
request.getClass().getInterfaces(),
newInvocationHandler(){
public Object invoke(Object proxy, Methodmethod,Object[] args)throws Throwable {
//如果调用是的getParameter方法
if(method.getName().equals("getParameter")){
//取得请求的类型
String methodType = request.getMethod();
//如果是POST
if(methodType.equals("POST")){
//设置request的解码方式为UTF-8
request.setCharacterEncoding("UTF-8");
//取得表单参数
String username = (String) args[0];
//正确编码
String value = request.getParameter(username);
//将值返回
return value;
//如果是GET
}elseif(methodType.equals("GET")){
//取得表单参数
String username = (String) args[0];
//乱码
String value = request.getParameter(username);
//还原
byte[] buf = value.getBytes("ISO8859-1");
//正码
value = new String(buf,"UTF-8");
return value;
}
}else{
return method.invoke(request,args);
}
return null;
}
});
}
}
javaScript增强:
1.window : alert(‘信息’) :消息框 open( ) : 打开一个新窗口
2.Form表单对象:
访问表单的方式:
document.forms[n]
document.表单名字
3.定义函数的三种方式
<script type="text/javascript">
函数定义方式一
function add(num1,num2){
returnnum1 + num2;
}
document.write("100+200的值为:" + add(100,200) + "<br/>");
函数定义方式二
varadd = new Function("num1","num2","returnnum1+num2;");
document.write("300+400的值为:" + add(300,400) + "<br/>");
函数定义方式三
varmul = function(num1,num2){
returnnum1 * num2;
}
window.alert("100*2的值为:" + mul(100,2));
</script>
4.常用API
>>getElementById(),其中id的值,在整个html页面中必须唯一,大小写不敏感,只能在document对象中使用
>>getElmenetsByName(),其中name的属性name的值,返回一个数组
>>getElemnetsByTagName()
>>hasChildNodes();innerHTML取得的是文本值,即是一个字符串,不是元素
>>firstChild取得是是元素,不是字符串
>>nodeName_nodeType_nodeValue:其中nodeValue是可读可写的属性,其它二个属性只可读
>>replaceChild()
>>getAttrbuteNode():返回属性节点,即是一个对象
>>getAttribute():返回属性值,即是一个字符串
>>setAttribute():创建/更新属性的值,更新要确保name一致
>>removeAttribute()
>>innerHTML:不是DOM的标准,即就是可能在某些浏览器上运行不支持,但各主要的浏览器均支持
>>createTextNode():是DOM的标准
>>appendChild()
>>insertBefore()
>>removeChild(),删除子元素时,一定要从直接父元素中删除,不能自删
>>parentNode
>>childNodes:将空白字符也会当作一个子元素,不同的浏览器会有差别
>>showModelessDialog()打开窗口后,[允许]操作该窗口的其它地方,叫非模式
>>showModalDialog()打开窗口后,[不允许]操作该窗口的其它地方,叫模式
6)dom的api同样适用于xml文件,但有些在html文件中的非标准的api在xml文件中不有执行,例如
options和innerHTML。
例2:
<metahttp-equiv="content-type"content="text/html; charset=UTF-8">
</head>
<!-- 需求:在点击生效按钮后,使该按钮变成失效,然后点击恢复按钮时,使其恢复为生效按钮-->
<body>
<inputtype="button" value="生效"style="font-size:111px"/>
<inputtype="button" value="恢复"/>
<scripttype="text/javascript">
//获取标签元素的第一个元素
var inputElement =window.document.getElementsByTagName("input")[0];
//给该元素绑定时间监听
inputElement.onclick = function()
{
//方式一:
//设置属性值
inputElement.disabled= "disabled";
inputElement.value="失效";
//方式二:
inputElement.setAttribute("disabled","disabled");
inputElement.setAttribute("value","失效");
};
//获取标签元素的第-个元素
var input2Element =window.document.getElementsByTagName("input")[1];
input2Element.οnclick= function()
{ inputElement.removeAttribute("disabled");
inputElement.setAttribute("value","生效");
};
</script>
</body>
</html>
例3:设置图片的显示和隐藏
<!DOCTYPEHTML PUBLIC"-//W3C//DTDHTML 4.01 Transitional//EN">
<html>
<head>
<title>form.html</title>
<metahttp-equiv="content-type"content="text/html; charset=UTF-8">
</head>
<bodyonload="init()">
<divstyle="position:absolute;top:60px;left:400px;border-style:outset">
<imgsrc="zgl.jpg"id="imgID"/>
</div>
<divstyle="position:absolute;top:300px;left:450px">
<inputtype="button" value="隐藏"id="hideID"/>
<inputtype="button" value="显示"id="showID"/> CSS + DIV
</div>
<scripttype="text/javascript">
//单击隐藏按钮
document.getElementById("hideID").οnclick=function(){
//将图片隐蔽
imgHide("imgID");
//将隐藏按钮失效
btnAbled("hideID");
//将显示按钮生效
btnEnabled("showID");
};
//单击显示按钮
document.getElementById("showID").οnclick=function(){
//将图片显示
imgShow("imgID");
//将显示按钮失效
btnAbled("showID");
//将隐蔽按钮生效
btnEnabled("hideID");
};
//初始化
function init(){
//将按钮失效
btnAbled("showID");
}
//将按钮生效
function btnEnabled(id){
varidElement = document.getElementById(id);
idElement.disabled=false;
}
//将按钮失效
function btnAbled(id){
varidElement = document.getElementById(id);
idElement.disabled=true;
}
//将图片隐蔽
function imgHide(id){
varidElement = document.getElementById(id);
idElement.style.visibility="hidden";
}
//将图片显示
function imgShow(id){
varidElement = document.getElementById(id);
idElement.style.visibility="visible";
}
</script>
</body>
</html>
AJAX包含的技术:
什么是Ajax: Ajax被认为是(AsynchronousJavaScript And Xml的缩写)。现在,允许浏览器与服务器通信而无须刷新当前页面的技术都被叫做Ajax.
工作原理:
Ajax的核心是JavaScript对象XMLHttpRequest。
该对象在Internet Explorer 5中首次引入,它是一种支持异步请求的技术。简而言之,XMLHttpRequest使您可以使用JavaScript向服务器提出请求并处理响应,而不阻塞用户。
AJAX采用异步交互过程。AJAX在用户与服务器之间引入一个中间媒介,从而消除了网络交互过程中的处理—等待—处理—等待缺点。
用户的浏览器在执行任务时即装载了AJAX引擎。AJAX引擎用JavaScript语言编写,它负责用户界面及与服务器之间的交互。
AJAX引擎允许用户与应用软件之间的交互过程异步进行,独立于用户与网络服务器间的交流。现在,可以用Javascript调用AJAX引擎来代替产生一个HTTP的用户动作,内存中的数据编辑、页面导航、数据校验这些不需要重新载入整个页面的需求可以交给AJAX来执行。
AJAX:(AsynchronousJavaScript and XML)并不是一项新技术,其实是多种技术的综合,包括Javascript、HTML和CSS、DOM、XML和XMLHttpRequest.
服务器端语言:服务器需要具备向浏览器发送特定信息的能力。Ajax与服务器端语言无关。
XML (extensible Markup Language,可扩展标记语言)是一种描述数据的格式。AJAX 程序需要某种格式化的格式来在服务器和客户端之间传递信息,XML是其中的一种选择
HTML(Hypertext Markup Language,使用超文本标记语言)和CSS(Cascading Style Sheet,级联样式单)标准化呈现;
DOM(Document Object Model,文档对象模型)操作页面规则;
使用XMLHttpRequest对象进行异步数据读取;使用JavaScript实现动态显示和交互;
2 AJAX的特点
1)AJAX是一个多种技术的综合体现
2)AJAX和服务端的技术无关
3)AJAX给用户体验是C/S(桌面应用),但[本质]是还是B/S(Web应用)
4)AJAX和服务端技术无关,它位于浏览器端
5)AJAX的不足:
>>早期浏览器兼容不统一
>>它是局部刷新,导致浏览器的前进和后退按钮无效
>>一些手持设备(iPad/PAD)支持不佳
*3 AJAX开发步骤及详解
Ajax创建:
1,创建XMLHttpRequest异步对象(AJAX引擎对象)
早期IE浏览器: var xhr =new ActiveXObject("microsoft.xmlhttp");
晚期主流浏览器 :varxhr = new XMLHttpRequest();
注意:早期不同的浏览器创建方式不一样。晚期主流浏览器都统一,直接new XMLHttpRequest()就可以创建AJAX引擎,不需要引入第三方的jar包。
2,为AJAX引擎设置监听器
1)xhr.onreadystatechange =function(){}
onreadystatechange: 该事件处理函数由服务器触发,而不是用户.
在 Ajax 执行过程中,服务器会通知客户端当前的通信状态。这依靠更新 XMLHttpRequest 对象的 readyState 来实现。改变readyState 属性是服务器客户端连接操作的一种方式。
每次 readyState 属性的改变都会触发 readystatechange事件
2)AJAX状态的几种变化
0:表示未初始化,即刚创建完毕
1:表示准备发送请求,即open()方法已经调用,但未调用send()方法
2:表示正在发送请求中,即send()方法已经调用,请求正发往服务端
3:表示AJAX引擎和服务端交互中,即AJAX引擎正在接收服务端的响应,但未完毕
*4:表示AJAX引擎完全接收到了服务端的响应,注意:4号状态是每种浏览器通用的
3)AJAX在收到URL请求后,会和原URL进行比较,如果不相同,则将该请求发送到服务器,获取新内容,再加以缓存。如果相同的URL时,直接取得缓存中的内容。只有当每次请求的URL不一样时,AJAX引擎就会将请求发给到服务端,以获取最新内容.
4)通知AJAX引擎接收HTML字符串
response.setContentType("text/html;charset=UTF-8");
通知AJAX引擎接收XML文件
response.setContentType("text/xml;charset=UTF-8");
以上代码在服务端写
5)AJAX接收HTML字符串
xhr.responseText
AJAX接收XML文件
xhr.responseXML
以上代码在客户端写
6)AJAX发送GET请求时,对于中文需要程序员手工编码
username = encodeURI(username)
AJAX发送POST请求时,对于中文由AJAX引擎负责编码,但需要通知AJAX引擎,设置以POST方式提交数据,让AJAX引擎自动进行URL编码
xhr.setRequestHeader("content-type","application/x-www-form-urlencoded");
该代码只能出现在xhr.open()之后,xhr.send()之前
4 XMLHttpRequest(AJAX引擎)方法或属性的小结
属性:
xhr.onreadystatechange = function(){无名处理函数}
xhr.readyState==0,1,2,3,4
xhr.status==200或404或500或304
xhr.responseText(有且只能接收服务端响应的字符串)
xhr.responseXML(有且只能接收服务端响应的XML文件)
方法:
xhr.open(method,url) //准备发送请求
xhr.send(如果是GET请求,就写null) //真正发送异步请求(请求体)
xhr.setRequestHeader("content-type","application/x-www-form-urlencoded");
open(method, url, asynch)
XMLHttpRequest 对象的 open 方法允许程序员用一个Ajax调用向服务器发送请求。
method:请求类型,类似 “GET”或”POST”的字符串。若只想从服务器检索一个文件,而不需要发送任何数据,使用GET(可以在GET请求里通过附加在URL上的查询字符串来发送数据,不过数据大小限制为2000个字符)。若需要向服务器发送数据,用POST。
在某些情况下,有些浏览器会把多个XMLHttpRequest请求的结果缓存在同一个URL。如果对每个请求的响应不同,这就会带来不好的结果。把当前时间戳追加到URL的最后,就能确保URL的惟一性,从而避免浏览器缓存结果。
url:路径字符串,指向你所请求的服务器上的那个文件。可以是绝对路径或相对路径。
asynch:表示请求是否要异步传输,默认值为true(异步)。指定true,在读取后面的脚本之前,不需要等待服务器的相应。指定false,当脚本处理过程经过这点时,会停下来,一直等到Ajax请求执行完毕再继续执行。
send(data):
open 方法定义了 Ajax 请求的一些细节。send方法可为已经待命的请求发送指令
data:将要传递给服务器的字符串。
若选用的是 GET 请求,则不会发送任何数据, 给 send 方法传递 null 即可:request.send(null);
当向send()方法提供参数时,要确保open()中指定的方法是POST,如果没有数据作为请求体的一部分发送,则使用null.
HTML 小结
优点:
JavaScript 进行解析。
HTML 的可读性好。
HTML 代码块与 innerHTML 属性搭配,效率高。
缺点:
若需要通过AJAX 更新一篇文档的多个部分,HTML 不合适,innerHTML 并非 DOM 标准
XML小结
优点:
XML 是一种通用的数据格式。
不必把数据强加到已定义好的格式中,而是要为数据自定义合适的标记。利用 DOM 可以完全掌控文档。
缺点:
如果文档来自于服务器,就必须得保证文档含有正确的首部信息。若文档类型不正确,那么 responseXML 的值将是空的。
当浏览器接收到长的 XML文件后, DOM 解析可能会很复杂
AJAX不是完美的技术。也存在缺陷:
1.AJAX大量使用了JavaScript和AJAX引擎,而这个取决于浏览器的支持。IE5.0及以上、Mozilla1.0、NetScape7及以上版本才支持,Mozilla虽然也支持AJAX,但是提供XMLHttpRequest的方式不一样。所以,使用AJAX的程序必须测试针对各个浏览器的兼容性。
2.AJAX更新页面内容的时候并没有刷新整个页面,因此,网页的后退功能是失效的;有的用户还经常搞不清楚现在的数据是旧的还是已经更新过的。这个就需要在明显位置提醒用户“数据已更新”。
3 对流媒体的支持没有FLASH好。
4. 一些手持设备(如手机、PDA等)现在还不能很好的支持Ajax。
JSON对象
JSON(JavaScript Object Notation)一种简单的数据格式,比xml更轻巧。JSON是JavaScript原生格式,这意味着在JavaScript中处理JSON数据不需要任何特殊的API或工具包。
JSON的规则很简单:对象是一个无序的“‘名称:值’对”集合。一个对象以“{”(左括号)开始,“}”(右括号)结束。每个“名称”后跟一个“:”(冒号);“‘名称/值’对”之间使用“,”(逗号)分隔。
规则如下:
1)映射用冒号(“:”)表示。名称:值
2)并列的数据之间用逗号(“,”)分隔。名称1:值1,名称2:值2
3) 映射的集合(对象)用大括号(“{}”)表示。{名称1:值1,名称2:值2}
4) 并列数据的集合(数组)用方括号(“[]”)表示。
[
{名称1:值,名称2:值2},
{名称1:值,名称2:值2}
]
5 元素值可具有的类型:string, number,object, array, true, false, null
*5 JSON的使用
1)JSON是JavaScript的原生态对象,即JavaScript可以不需要任务第三的jar包直接操作JSON对象
2)默认情况下,JS不能直接解析Java的String类型,需要通过eval()函数转换成JS可以直接解析的JSON字符串
3)三种数据载体的对比
数据载体:服务端发送信息到AJAX引擎,数据用什么容器装。这个容器就叫做数据载体
>>HTML:传输数据小,结构不复杂,不重用,不解析
*>>XML:不同系统之间的大量信息传递,结构相对复杂,不能重用,需要解析
*>>JSON:传递数据量小,有一定结构,需要重用,需要解析,注意:JSON只是一个字符串而以,但该语法相关复杂,可以通过第三方工具自动生成。
例:ajax对象 :
<%@ pagelanguage="java" import="java.util.*"pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01Transitional//EN">
<html>
<body>
输入用户名:
<input type="text" value="哈哈"id="usernameID" maxlength="2"/>
<input type="button" value="检查"id="buttonID"/>
单击检查按钮后,立即检查结果
<hr/>
<div id="divID"></div>
<scripttype="text/javascript">
//取得input元素
var inputElement =window.document.getElementById("buttonID");
//绑定事件
inputElement.onclick = function()
{//获取用户名
var username = window.document.getElementById("usernameID").value;
//创建引擎ajax对象
var xhr = createXHR();
//设置监听时间
xhr.onreadystatechange = function()
{
if(xhr.readyState==4)
{ //判断服务端响应是否正确
if(xhr.status==200)
{//ajax取得响应xml文件
var xmlDoc = xhr.responseXML;
var resElementXML =xmlDoc.getElementsByTagName("res")[0];
//取得值
var msg =resElementXML.firstChild.nodeValue;
//取得divID元素
var divIDElement = window.document.getElementById("divID");
//将msg添加div中
divIDElement.innerHTML = msg;
}
}
};
var method = "POST";
var url ="/myday29/CheckoutServlet?time="+new Date().toLocaleString();
xhr.open(method,url);
//设置以POST方式提交数据,让AJAX引擎自动进行URL编码
xhr.setRequestHeader("content-type","application/x-www-form-urlencoded");
//真正发送请 求体的数据
var sendData = "username="+username;
xhr.send(sendData);
};
//创建ajax引擎对象
function createXHR(){
var xhr =null;
try{
xhr = newActiveXObject("microsoft.xmlhttp");
}catch(e){
xhr = newXMLHttpRequest();
}
return xhr;
}
</script>
</body>
</html>
JSON创建对象步骤:
<!-- 创建对象的方法一-->
例2:json2.jsp
<%@ page language="java"import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01Transitional//EN">
<html>
<body>
<!-- 创建对象的方法第一种: -->
<script type="text/javascript">
var p ={
tel:[
{no:13579343798},
{no:13734544655},
{no:15032454645}
]
};
window.document.write("共有"+p.tel.length+"学员"+"<br/><hr/>");
//取出每个号码
for(var x=0;x<p.tel.length;x++){
document.write("号码:"+p.tel[x].no+"<br/>");
}
</script>
<!-- 第二种:-->
<script type="text/javascript">
var p = {
"city":["北京","上海","广州","重庆"]
};
document.write("共有:" +p.city.length + "个城市<br/>");
document.write("最闲的城市是:" + p.city[p.city.length-1]);
</script>
<!--第三种: -->
<script type="text/javascript">
var p = {
china:[
{city:"北京"},
{city:"上海"},
{city:"广州"}
],
usa:[
{city:"旧金山"},
{city:"纽约"},
]
};
document.write("你目前在:"+ p.china[2].city + "<br/>");
document.write("你最想去:"+ p.usa[1].city + "<br/>");
</script>
</body>
</html>
例3:二级联动:xml版本
<html>
<body>
<selectid="provinceID">
<option>选择省份</option>
<option>选择省份</option>
<option>吉林省</option>
<option>辽宁省</option>
<option>山东省</option>
<option>广东省</option>
</select>
<selectid="cityID" style="width:220px">
<option>选择城市</option>
</select>
<scripttype="text/javascript">
//取得select元素
var selectElement =window.document.getElementById("provinceID");
//绑定事件
selectElement.onchange = function()
{ //取得cityid标签元素
var citySelectElement =window.document.getElementById("cityID");
//取得子元素非个数
var size = citySelectElement.options.length;
//清空option元素
for(var x=size-1;x>0;x--)
{
citySelectElement.removeChild(citySelectElement.options[x]);
}
//取得选中的城市
var index = selectElement.selectedIndex;
//取得选中的option选项
var optionElement = this[index];
//取得值
var province = optionElement.innerHTML;
//创建ajax引擎对象
var xhr = createXHR();
//设置监听器
xhr.onreadystatechange = function()
{ //判断服务端响应状态
if(xhr.readyState==4)
{
if(xhr.status==200)
{
//取得响应的文本字符串
var msg = xhr.responseText;
//将java中的String类型字符串转换为json中的能解析的字符串
var jsonString = eval(msg);
//取得城市的个数
var size = jsonString.length;
for(varx=0;x<size;x++)
{//取得每个城市
var city = jsonString[x].name;
//创建<option>节点
var cityIDoptionElement =window.document.createElement("option");
//设置值
cityIDoptionElement.innerHTML= city;
//添加到cityID的<select>标签中
citySelectElement.appendChild(cityIDoptionElement);
}
}
}
};
//设置发送请求方式
var method = "post";
varurl = "/myday29/JsoncityServlet?time="+new Date().toLocaleString();
//准备发送
xhr.open(method,url);
设置以POST方式提交数据,让AJAX引擎自动进行URL编码
xhr.setRequestHeader("content-type","application/x-www-form-urlencoded");
vardatePronvince = "province="+province;
//发送请求
xhr.send(datePronvince);
};
//创建AJAX异步对象
function createXHR(){
varxhr = null;
try{
xhr= new ActiveXObject("microsoft.xmlhttp");
}catch(e){
xhr= new XMLHttpRequest();
}
returnxhr;
}
</script>
</body>
服务端响应 Servlet
public class CityServletJson extends HttpServlet {
public void doPost(HttpServletRequestrequest, HttpServletResponse response)throws ServletException,IOException {
request.setCharacterEncoding("UTF-8");
String province = request.getParameter("province");
List<City> cityList = new ArrayList<City>();
//模拟调用业务层
if("吉林省".equals(province)){
cityList.add(new City(1,"长春[吉林省]"));
cityList.add(new City(2,"吉林市[吉林省]"));
cityList.add(new City(3,"松原[吉林省]"));
cityList.add(new City(4,"通化[吉林省]"));
}else if("辽宁省".equals(province)){
cityList.add(new City(1,"沈阳[辽宁省]"));
cityList.add(new City(2,"大连[辽宁省]"));
cityList.add(new City(3,"鞍山[辽宁省]"));
cityList.add(new City(4,"抚顺[辽宁省]"));
cityList.add(new City(5,"铁岭[辽宁省]"));
}else if("山东省".equals(province)){
cityList.add(new City(1,"济南[山东省]"));
cityList.add(new City(2,"青岛[山东省]"));
cityList.add(new City(3,"威海[山东省]"));
}else if("广东省".equals(province)){
cityList.add(new City(1,"广州[广东省]"));
cityList.add(new City(2,"深圳[广东省]"));
}
//通过第三方工具,将集合转成JSON字符串
JsonConfig jsonConfig = new JsonConfig();
String[] params = {"id"};
jsonConfig.setExcludes(params);
JSONArray jsonArray =JSONArray.fromObject(cityList,jsonConfig);
String jsonString =jsonArray.toString();
System.out.println("jsonString="+jsonString);
//将JSON字符串输出到AJAX引擎
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write(jsonString);
}
}
JQuery: javaScript Query
javaScript库:jQuery[JS标准],MooTools, Prototype, Dojo, YUI, EXT_JS, DWR
jQuery由美国人John Resig创建
1.jQuery是继prototype之后又一个优秀的JavaScript框架。其宗旨是——WRITELESS,DO MORE,写更少的代码,做更多的事情。
2.它是轻量级的js库(压缩后只有20k左右) ,这是其它的js库所不及的,它兼容CSS3,还兼容各种浏览器 (IE 6.0+, FF 1.5+, Safari 2.0+, Opera 9.0+)。
jQuery是一个快速的,简洁的javaScript库,使用户能更方便地处理HTML/CSS DOM、事件、实现动画效果,并且方便地为网站提供AJAX交互。
3.jQuery还有一个比较大的优势是,它的文档说明很全,而且各种应用也说得很详细,同时还有许多成熟的插件可供选择。
4.jQuery能够使用户的html页保持代码和html内容分离,也就是说,不用再在html里面插入一堆js来调用命令了,只需定义id即可。
1 什么是jQuery?什么要使用jQuery?
1)第三方开源组织提供的实用函数库,大多[主流]的浏览器均支持
2)jQuery能够让程序员写少代码,做多事情,它也是JS标准推荐的实用JS函数库
3)jQuery能够操作HTML/CSS/DOM/动画/事件/AJAX操作
4)jQuery文档丰富,祥细
5)[建议]为每个<html标签>定义一个id属性,该id属性在整个html文件中唯一
2 DOM对象和jQuery对象相互转换
1)通过原始domapi得到的对象,叫dom对象,例如:document.getElementById("id");
自定义规则:dom对象--domElement,例如divElement,tableElement
文本值---dom,例如div,input,span
2)通过jqueryapi得到的对象,叫jquery对象,例如:$("#div")
自定义规则:jquery对象--$dom,例如$div,$input,$table
文本值---dom,例如div,input,span
3)jquery对象是对原始dom对象的封装,原始dom对象中有什么方法或属性,jquery对象就有与之对应的方法(一般以方法为主)
4)dom原始对象转成jquery对象
jquery对象 = $(dom对象);
5)jquery对象转成dom原始对象
一切jquery对象都是数组,每个dom对象位于数据中,下标从0开始
方式一:dom对象 = jquery对象[0]
方式二:dom对象 = jquery对象.get(0)
注意:dom对象只能调用dom对象的api,jquery对象也只能调用jquery对象的api,不能混用
3 DOM操作与jQuery操作对错误的处理方式的比较
1)dom在查找不到元素的情况下,无任何提示;所以项目中,最好自行判段元素是否为null
1)jquery在查找不到元素的情况下,有提示;
*4 jQuery九类选择器(参见jQueryAPI.chm手册)
1)基本选择器
$("#id") 根据id匹配一个元素
$("div") 根据标签名匹配一个元素
$(".myClass")根据给定的类匹配元素
$("#id,div,.myClass")将每一个选择器匹配到的元素合并后一起返回
例:
<scripttype="text/javascript">
//1)查找ID为"div1ID"的元素个数
alert($("#div1ID").length);
//2)查找DIV元素的个数
alert($("div").size());
//3)查找所有样式是"myClass"的元素的个数
alert($(".myClass").html());
//4)查找所有DIV,SPAN,P元素的个数
alert($("div,span,p").size());
//5)查找所有ID为div1ID,CLASS为myClass,P元素的个数
alert($("#div1ID,.myClass,p").size());
</script>
2)层次选择器
$("forminput")[祖先和后代]
$("form>input")[父子关系]
$("form+input")[同一级第一个兄弟关系]
$("form~input")[同一级所有兄弟关系]
例1:
<body>
<form>
<inputtype="text" value="a"/>
<table>
<tr>
<td><inputtype="checkbox" value="b"/></td>
</tr>
</table>
</form>
<inputtype="radio" value="c"/>
<inputtype="radio" value="d"/>
<input type="radio"value="e"/>
<scripttype="text/javascript">
//1)找到表单form下所有的input元素的个数[祖先和后代的关系]
//alert($("forminput").size());
//alert($("forminput:first").size());
//2)找到表单form下所有的子级input元素个数[父子关系]
alert($("form >input").size());
alert($("form >input").val());
//3)找到表单form同级第一个input元素的value属性值[兄弟关系]
alert($("form + input").val());
alert($("form + input").size());
//4)找到所有与表单form同级的input元素个数[兄弟关系]
alert($("form ~ input").size());
</script>
</body>
3)增强型基本选择器
$("ulli:first") 第一个元素
$("ulli:last") 最后一个元素
$("input:checked"):表示<input/>选中的元素,即含有"checked"属性值的<input/>标签
$("input:not(:checked)"):表示<input/>未选中的元素,即不含有"checked"属性值的<input/>标签
$("tabletr:even")索引为为奇数,从0开始
$("tabletr:odd")索引为为偶数,从0开始
$("tabletr:eq(1)")第二行,第一行也可以使用:eq(0)等价于:first
$("tabletr:gt(0)") 查找第二第三行,即索引值是1和2,也就是比0大
$("tabletr:lt(2)") 查找第一第二行,即索引值是0和1,也就是比2小
$(":header")
4)内容选择器
$("div:contains('John')") 匹配包含给定文本的元素
$("p:empty") 匹配所有不包含子元素或者文本的空元素
$("div:has('p')") 给所有包含 p 元素的 div元素添加一个 text 类
$("p:parent") 匹配含有子元素或者文本的元素
例1:
<styletype="text/css">
.myClass{font-size:44px;color:blue}
</style>
<div><p>JohnResig</p></div>
<div><p>GeorgeMartin</p></div>
<div>MalcomJohn Sinclair</div>
<div>J.Ohn</div>
<div></div>
<scripttype="text/javascript">
//1)查找所有包含文本"John"的div元素的个数
//alert($("div:contains('John')").size());
//2)查找所有p元素为空的元素个数
//alert($("div ~p:empty").size());
//alert($("body>p:empty").size());
//alert($("bodyp:empty").size());
// alert($("p:empty").size());
//3)给所有包含p元素的div元素添加一个myClass样式
//alert($("div:has(p)").addClass("myClass"));
//4)查找所有含有子元素或者文本的p元素个数,即p为父元素
alert($("p:parent").size());
</script>
5)可见性选择器
$("tabletr:hidden")匹配所有不可见元素,或者type为hidden的元素
$("tabletr:visible") 匹配所有的可见元素
6)属性选择器
$("div[name]") 匹配包含给定name属性的元素。
$("div[name='xx']") 查找所有 name 属性是 xx 的 input 元素
$("div[name!='xx']") 查找所有 name 属性不包含 xx 的div元素
$("div[name^='xx']")以xx开头
$("div[name$='xx']")以xx结束
$("div[id*='xx']") 查找所有name 包含 'xx' 的 input元素
$("input[id][name$='letter']")同时有id和name属性包含letter的 input元素
例1:
<body>
<div><p>Hello!</p></div>
<divid="test2"></div>
<inputtype="checkbox" name="newsletter" value="HotFuzz" />
<inputid="myID" type="checkbox" name="newsletter"value="Cold Fusion" />
<inputtype="checkbox" name="newsaccept" value="EvilPlans" />
<scripttype="text/javascript">
//1)查找所有含有id属性的div元素个数
alert($("div[id]").size());
//2)查找所有name属性是newsletter的input元素,并将其选中
alert($("input[name='newsletter']").size());
//3)查找所有name属性不是newsletter的input元素,并将其选中
//alert($("input[name!='newsletter']").size());
//4)查找所有name以'news'开始的input元素,并将其选中
alert($("input[name^='news']").size());
//5)查找所有name 以'letter'结尾的input元素,并将其选中
alert($("input[name$='letter']").size());
//6)查找所有name包含'news'的input元素,并将其选中
alert($("input[name*=news]").size());
//7)找到所有含有id属性,并且它的name属性是以"letter"结尾的,并将其选中
alert($("input[id][name$=letter]").size());
</script>
</body>
7)子元素选择器
$("ulli:first-child"):所有的ul中li的第一个元素的数组
数组.each();迭代方法,有且只能使用jquery对象调用,不能使用dom对象调用,类似于java中的增强for循环它会自动将数组中的每一个元素覆给each()参数,我们在each中,通过this来获取该参数
$("ul li:last-child")':last'只匹配一个元素,而此选择符将为每个父元素匹配一个子元素
$("ulli:only-child") 在 ul 中查找是唯一子元素的 li
$("ulli:nth-child(1)") 匹配其父元素下的第N个子或奇偶元素
注意: ':eq(index)' 只匹配一个元素,而这个将为每一个父元素匹配子元素。:nth-child从1开始的,而:eq()是从0算起的!可以使用:
nth-child(even)
:nth-child(odd)
:nth-child(3n)
:nth-child(2)
:nth-child(3n+1)
:nth-child(3n+2)
8)表单属性选择器
:input包含input/textarea/button/select
9)表单对象属性
$("input:enabled") 匹配所有可用元素
$("input:disabled")匹配所有不可用元素
$(":checkbox:checked")或$(":radio:checked")
$("selectoption:selected")下拉框选中
$("selectoption:not(:selected)")下拉框未选中
例1:
<body>
<form>
<input name="email" disabled="disabled" />
<input name="password" disabled="disabled" />
<input name="id" />
<input type="checkbox" name="newsletter" checked="checked"value="Daily" />
<input type="checkbox" name="newsletter"value="Weekly" />
<input type="checkbox" name="newsletter"checked="checked" value="Monthly" />
<select>
<option value="1">Flowers</option>
<option value="2" selected="selected">Gardens</option>
<option value="3">Trees</option>
</select>
</form>
<scripttype="text/javascript">
//1)查找所有可用的input元素的个数
//alert($("input:enabled").size());
//2)查找所有不可用的input元素的个数
//alert($("input:disabled").size());
//3)查找所有选中的复选框元素的个数
//alert($("input:checked").size());
//4)查找所有未选中的复选框元素的个数
//alert($("input[checked!='checked']").size());
//alert($("input:not(:checked)").size());
//5)查找所有选中的选项元素的个数
//alert($("selectoption:selected").size());
</script>
</body>
总结案例:
二级连动:jquery版本;
<body>
<selectid="provinceID" name="pname">
<option>选择省份</option>
<option>陕西省</option>
<option>辽宁省</option>
<option>山东省</option>
<option>广东省</option>
</select>
<selectid="cityID" style="width:100px"name="cname">
<option>选择城市</option>
</select>
<selectid="areaID" style="width:100px">
<option>选择地区</option>
</select>
<scripttype="text/javascript">
//取得cityID下拉选项元素
$("#cityID").change(function(){
//取得区域<selsect>不包含第一个选项的元素,并清空其他的选项
$("#areaID option:not(:contains('选择地区'))").remove();
var url ="${pageContext.request.contextPath}/JqueryCityServlet?time="+newDate().toLocaleString();
//var city =$("#cityID option:selected").html();
//varsendData ={city:city};
//收集表单参数
var sendData = $("#cityID").serialize();
$.get(url,sendData,function(backData,textStatus,xhr){
//取得响应的文本内容
var msg = xhr.responseText;
varjsonString = eval(msg);
varsize = jsonString.length;
for( var i = 0; i < size; i++) {
//取得对应的区域
var area = jsonString[i].name;
//创建option标签
var $option =$("<option>"+area+"</option>");
$("#areaID").append($option);
}
});
});
//给<province>绑定事件
$("#provinceID").change(function(){
//取得城市<selsect>不包含第一个选项的元素,并清空其他的选项
$("#cityID option:gt(0)").remove();
$("#areaIDoption:gt(0)").remove();
var url ="${pageContext.request.contextPath}/JqueryCityServlet?time="+newDate().getTime();
var province= $("#provinceID option:selected").html();
alert(province);
var sendData= {province:province};
$.post(url,sendData,function(backData,textStatus,xhr)
{ //取得响应的文本内容
var msg = xhr.responseText;
//将Java中的String类型转成JS中的json类型(JS类型的字符串)
varjsonString = eval(msg);
//取得城市个数
var size = jsonString.length;
//迭代
for ( var i = 0; i < size; i++)
{ //取得每一个城市
var city = jsonString[i].name;
//创建<option>元素
var $option = $("<option>" + city +"</option>");
//添加节点<option>
$("#cityID").append($option);
}
});
});
public class JqueryCityServlet extends HttpServlet {
public void doGet(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
this.findAreaByCity(request, response);
}
public void doPost(HttpServletRequestrequest, HttpServletResponse response)
throws ServletException, IOException {
this.findCityByProvince(request, response);
}
private voidfindAreaByCity(HttpServletRequest request,
HttpServletResponse response) throws IOException {
String city = request.getParameter("cname");
PCAService pcaService = new PCAService();
List<Area> areaList =pcaService.findAreaByCity(city);
//将集合装换为json
JsonConfig jsonConfig = new JsonConfig();
String [] param = {"id"};
jsonConfig.setExcludes(param);
JSONArray jsonArray =JSONArray.fromObject(areaList, jsonConfig);
String jsonString = jsonArray.toString();
System.out.println("地区:"+jsonString);
PrintWriter pw = response.getWriter();
pw.write(jsonString);
}
public voidfindCityByProvince(HttpServletRequest request,
HttpServletResponse response) throws IOException {
//取得参数
String province = request.getParameter("province");
System.out.println(province);
//调用业务层查询城市
PCAService pcaService = new PCAService();
List<City> cityList =pcaService.findCityByProvince(province);
//将集合装换为json
JsonConfig jsonConfig = new JsonConfig();
String [] param = {"id"};
jsonConfig.setExcludes(param);
JSONArray jsonArray =JSONArray.fromObject(cityList, jsonConfig);
String jsonString =jsonArray.toString();
System.out.println("城市:"+jsonString);
PrintWriterpw = response.getWriter();
pw.write(jsonString);
}
}
Jquery基本操作方法:
添加节点:
append(content):父子关系 父.append(子),插入到父元素之后
向每个匹配的元素内部追加内容。这个操作与对指定的元素执行appendChild方法
preappend(content): 父子关系 ,插入到父元素之前
插入节点:
after(content):兄弟关系 ,插入到兄弟元素之后
before(contect): 兄弟关系,插入到兄弟元素之前
查找属性
attr(name):查找元素的属性值
例:取得form下第一个input元素的type属性
$(":text").attr("type")
attr(key,value):设置属性值
例:设置form下最后个input元素的为只读文本框
$(":password").attr("readonly","readonly")
删除属性:
RemoveaAttr(“name”);删除指定的属性
注:删除指定的属性,可以联级删除,
例如:删除table元素的属性为align/border
$table.removeAttr("align").removeAttr("border");
创建元素的节点:
$(“html代码”): jquery自动将html代码转成dom对象,最后以jquery对象的形式返回,html代码中,可以写N个属性值,外面双引,内部单引
例: //创建div元素,添加"哈哈"文本,ID属性,并添加到文档中
Var $div = $(“<div id=’2012’>哈哈</div>”)
$(“body”).append($div);
删除节点元素:
Empty(); 把所有段落的子元素(包括文本节点)删除
例: $("#secondID").empty();删除id为#secondID下的子元素,而#secondID本身不删除
Remove();删除指定的节点(连同自己也删除)
例: $(“#secondID”).remove(); 删除id为secondID的元素
remove([expre]);从DOM中删除所有匹配的元素
例:$(“div”).remove(“#secondID”)删除指定id为#secondID的元素
复制元素:
clone():复制对象,不复制行为
clone(true):复制对象,也复制行为例: 克隆所有b元素(并选中这些克隆的副本),然后将它们前置到所有段落中。
<b>Hello</b><p>, howare you?</p>
例: $("b").clone().prependTo("p");
替换节点:
replacewith(): 将所有匹配的元素替换成指定的HTML或DOM元素
例: <divstyle="width:165px;height:23px">
双击会被替换成文本框 </div>
<scripttype="text/javascript">
//双击<div>中的文本,用文本框替换文本
$("div").dblclick(function(){
//创建文本框
var $text = $("<inputtype='text'/>");
//用文本框替换<div>标签
$(this).replaceWith($text);
//文本框失去焦点
$text.blur(function(){
//取得文本框的value属性
var username = $(this).val();
//用<div>标签替换文本框
$(this).replaceWith("<div>"+username+"</div>");
});
});
</script>
添加样式:
addClass(“样式名或者类名”);为无样式的DIV添加样式
例:$("div:first").addClass("myClass");为无样式的DIV添加样式
removeClass(“样式名或者类名”);移除样式
例:为有样式的DIV删除样式
$(".myClass").removeClass("myClass")
hasClass(“样式名或者类名”):判断元素是否有样式
例:最后一个DIV是否有样式
$(".myClass").hasClass("myClass")
toggleClass(“样式名或者类名”): 给没有样式的元素添加样式,有样式的元素取出样式.
例:有样式的变成无样式,无样式的变成有样式
$("div").toggleClass("myClass");
文档的加载:
$(document).Ready()
查找元素:
children():取得一个包含匹配的元素集合中每一个元素的所有子元素的元素集合
例: $("div span").html() : 取得div元素的直接子元素内容,只包含直接子元素,不包含后代
next(): 查找下一个元素
例:$("div").next().html():取得div元素的下一个同级的兄弟元素内容
prev():查找上一个元素
例:$("div").prev().html() :取得div元素的上一个同级的兄弟元素内容
注: 只有紧邻的同辈元素会被匹配到,而不是前面所有的同辈元素。
siblings(): 取得一个包含匹配的元素集合中每一个元素的所有唯一同辈元素的元素集合
例: 依次取得div元素的上下一个同级的所有兄弟元素的个数
$("div").siblings().size()
注意:是不包含自已的同级兄弟集合
图片显示:
$("p").show(): 显示隐藏的匹配元素
$("img").show(5000,function(){})在动画完成时执行的函数,每个元素执行一次。
三种预定速度之一的字符串("slow","normal", or "fast")或表示动画时长的毫秒数值(如:1000).
hide() : 隐藏匹配元素
例:$("img"). hide(5000,function(){})该方法在调用函数时只执行一次
淡入淡出:
fadeOut() :淡出隐蔽图片(循环显示,隐藏)
例:$("img").fadeIn(5000); 用缓慢的动画将隐藏的图片显示,历时5秒。
fadeIn() :淡入显示图片(循环显示,隐藏)
例: $("img").fadeOut(5000); 用缓慢的动画将隐藏的图片显示,历时5秒。
滑动:
jquery对象.slideUp(“slow”):向上滑动
例: <div>
中国0<br/>
中国1<br/>
中国2<br/>
中国3<br/>
</div>
$(":button").click(function(){
$("div").slideUp("slow");
});
jquery对象.slideDown():向下滑动
例:$(":button").click(function (){
$("div").slideDown("slow");
});
三种预定速度之一的字符串("slow","normal", or "fast")或表示动画时长的毫秒数值(如:1000)
jquery对象.toggle() : 切换元素的可见状态 ,如果元素是可见的,切换为隐藏的;如果元素是隐藏的,切换为可见的.
Strust2开发 :
创建步骤:1.导包
struts2-core-2.1.8.1.jar:Struts 2框架的核心类库
xwork-core-2.1.6.jar :XWork类库,Struts2在其上构建
ognl-2.7.3.jar :对象图导航语言(Object Graph Navigation Language),struts2框架通过其读写对象的属性
freemarker-2.3.15.jar :Struts 2的UI标签的模板使用FreeMarker编写
commons-logging-1.1.x.jar :ASF出品的日志包,Struts2框架使用这个日志包来支持Log4J和JDK 1.4+的日志记录。
commons-fileupload-1.2.1.jar 文件上传组件,2.1.6版本后需要加入此文件
commons-io-1.3.2.jar,上传文件依赖的jar包
1.配置web.xml文件.
即:<!--配置过滤器-->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
过滤器文件路径:
E:\java\开发类库\struts-2.3.1.1\src\apps\blank\src\main\webapp\WEB-INF
2. 配置struts.xml文件放在src目录下:通过struts.xm文件通知struts框架的进行业务处理.
同web.xml文件通知web服务器.
struts.xml文件路径E:\java\开发类库\struts-2.3.1.1\src\core\src\test\resources
即: <!--声明唯一的包名,主要用户装配aciton类-->
<struts>
<packagename="default"/> <!—包名-->
<actionname="HelloWorldAction"class="cn.web.itcast.HelloWorldAction "method="helloMethod"/> <!—加载时所需的类的名称及类所在的路径-->
</struts>
3.部署web服务器,通知浏览器访问.
4.部署web应用时, StrutsPrepareAndExecuteFilter类就已经被实例化,相关的文件也加载形成javaBean对象Struts-default.xml(框架),struts.xml,每次部署web应用时,都会加载最新的xml到内存中的javaBean对象中,javaBean主要封装的是配置strust.xml文件中的属性如(<action>标签中的name,method,class属性).
访问方式: /Package中的namespace属性值/action中name属性
配置名称空间namespace
<constantname="struts.action.extension"value="action,qq,xx,,"/>
例如:http://127.0.0.1:8080/day32/xx/yy/zz/TestAction回车,且namespace="/"(默认)
>>去/xx/yy/zz的name包名下查询是否有HelloWorldAction存在,如果不存在,
>>如果再找不到,返回404面页
例:http://127.0.0.1:8080/day32/xx/yy/zz/HelloWorldAction回车,
且namespace="/xx/yy/zz"(自定义)
那么访问该Action有且只能一种方式
:http://127.0.0.1:8080/day32/xx/yy/zz/HelloWorldAction回车
5,如果项目中,你要使用框架默认的Action,
struts2的默认action是ActionSupport类,如果要用它,必须继承struts2的"struts-default"包
即:需要继承struts-default包.此时调用的是struts-default包中默认的excecut()方法,通过success.jsp页面回显示信息.此时需要配置<result>,并指明属性name=”execute()的返回字符串”,访问路径
需要在struts.xml文件中配置如下信息:
<package name = “default”extends="struts-default">
<action name=”HelloWorldAction(类名)”/>
<resultname="success(方法名)"type="dispatcher(跳转页面方式)">
/WEB-INF/success.jsp(访问路径)
</result>
</package>
此时可以直接访问: http://127.0.0.1:8080/day32/HelloWorldAction
6.转发:在struts.xml文件中配置如下代码:
<struts><!--声明唯一的包名,主要用户装配aciton类 namespace为包名的命名空间-->
<packagename="default"namespace="/"extends="struts-default">
<default-action-refname="HelloWorldAction"/><!—错误utl处理路径-->
<actionname=" HelloWorldAction">
<resultname="success(转发的文件名)"type="dispatcher(跳转页面的方式)">
/WEB-INF/success.jsp(转发的页面)
</result>
</action>
</package>
</struts>
7.配置错误URL路径的处理方式:
<default-action-refname="HelloWorldAction"/>,类似于缺省Servlet。
默认的url路径主要是在当访问的路径不存在是,通过默认的路劲找到默认的类,然后通过<result name=”success”type=”dispater”>标签中指定的路径名和路径访问seccess.jsp显示信息.
1).如果不配置method属性,默认是execute()方法,前提是在程序员自己定义的Action类中有excetue()方法如果没有该方法则会出错(404),是因为在访问时默认的类找不到方法.如果定义的类中没有该方法execute()方法,则在<action>标签中一定要写属性method=”方法名” .
2) <result>标签中type属性可以不写,默认是转发dispater
3) <result>标签中不配置name属性,默认是success
8访问Action可以有扩展名,扩展名默认是.action(小写),也可以使用无扩展名的访问路径.
可以通过自己手动在struts.xml中配置标签的扩展名:如下
<constantname="struts.action.extension" value="action,qq,xx,,"/>
可以在src/目录下,写一个名字struts.properties的必属性文件,去[覆盖]系统default.properties文件中的属性参数, 属性文件配置: struts.action.extension=”action,aa,bb,,”
当strust.properties文件和default.properties文件内容突冲时,strtus.properties起决定作用
9项目中,可以使用strust.properties或struts.xml文件,配置扩展名等相关属性参数,struts.properties文件起决定作用
程序员自定的文件,会覆盖框架的默认配置文件(default.properties)
10. strust2框架中,可以通过总的struts.xml文件,加载各个模块的分xml文件,总的struts.xml文件有且必须放在src目录下,其它xml文件,放置任意类路径下,即src目录下。
例: <includefile="cn/itcast/web/struts2/config/default_struts.xml"/>
<includefile="cn/itcast/web/struts2/config/user_struts.xml"/>
<includefile="cn/itcast/web/struts2/config/both_struts.xml"/>
11.struts2中的action采用非单例模式(原型模式),每次请求,都会产生一个实例。
ActionContext:数据中心
Api:
ServletActionContext.getRequest();取得request域对象:
servletActionContext.getRequest().getMethod();取得提交方式
servletActionContext.getRequest(); 取得HttpServletrequest
servletActionContext.getSession(),取得HttpSession
Map<String, Object> sessionMap =ActionContext.getContext().getSession();
通过session域对象取得map集合
6 struts2数据格式验证----手工验证篇
1)类继承ActionSupport类,重写validate()方法或添加validateLoginMethod()方法
2)在struts.xml文件的某个<action/>标签中,配置input属性值,表示出错后,转在的页面
3)在页面中通过如下二种方法将,错误消息按指定的格式输出
>><s:fielderrors/>
>><s:form/>和<s:textfield/>和<s:password/>(推荐)
Action:特有的标签校验表单提交的数据
<s:formaction="/RegisterAction"method="post">
<s:textfieldlabel="用户名"name="username"/>
<s:passwordlabel="密码"name="password"/>
<s:submitvalue="注册"/>
</s:form>
4)validate()方法,是检证UserAction中所有的业务方法,即与execute()同样签名的方法,例如registerMethod()
5)validateLoginMethod()方法,可以争对UserAction中的loginMethod()业务方法验证,其它方法不验证
6)当validate()和validateLoginMethod()方法同时存在时,先执行validateLoginMethod()方法,再执行validate()方法,如果验证成功,到达值得的LoginMethod()方法的中的return “success” 返回文件名,过滤器根据该文件名查找显示信息的文件路径(如:/WEB-INF/success.jsp),最终以标签<s: property value=”username”>获取显示信息,
7)只有当validateLoginMethod()方法和validate()方法验证都成功了,才能执行UserAction的业务方法loginMethod()到达指定的页面显示结果,否则都会转发到struts.xml中的<result>标签的input属性所指明的页面
例:
<resultname="input" type="dispatcher">
/WEB-INF/success.jsp
</result>
7 struts2常用标签总结
<s:property value="ip"/> 从request域对象中获取校验信息
<s:fielderrors/> 从数据校验中心获取显示错误信息,即ActionContext中
<s:form/> 表单提交标签
<s:textfield/> 该标签提交文本数据
<s:submit/>通过配合使用 ,判断提交的数据是否符合指定的规则,是返回true, 非返回false
例:
<body>
<s:formaction="UploadAction"method="post"enctype="multipart/form-data">
<s:textfieldlabel="上传用户"name="username"/>
<s:filelabel="上传文件1"name="uploadFile"/>
<s:filelabel="上传文件2"name="uploadFile"/>
<s:submitvalue="上传"/>
</s:form>
</body>
<s:text/>国际化
<s:param/>国际化
例:
<s:textname="hello">
<s:param>杰克</s:param> //设置配置文件(*.properties)中的属性值
<s:param>北京</s:param>
</s:text>
<s:file/>选择文件
8 struts.xml文件中的属性总结
<package
name="user(包名,在整个web应用是唯一,必须)"
namespace="/(包所在的访问空间,可选,默认/)"
extends="struts-default(扩展框架内置包,可选)">
<!-- 配置RegisterAction -->
<action
name="RegisterAction(访问的路径,必选)"
class="cn.itcast.web.struts2.action.RegisterAction(处理类的全路径,可选,默认ActionSupport)"
method="registerMethod(类的需要执行的方法,可选,默认execute)">
<result
name="success(Action类中方法返回值,可选,默认success)"
type="dispatcher(跳转的方式,重定向或转发,可选,默认转发">
/WEB-INF/success.jsp(跳转的真实路径)
</result>
</action>
</package>
虚拟目录的配置:
1. 在server.xml中配置,增加一个<host></host>标签,即该server.xml文件中的<host>,然后对其修改属性.
2必须修改该标签中的属性name =”虚拟路径名”appBase=”工程所放置的位置”.
配置<Contextpath=”/工程所映射的URL路径(可选如果不写,默认为映射的URL路径)” docBase=”工程所存放的实际路径” /> 标签,
例如:在浏览器中访问的域名www.bbs.com. , 工程存放在e盘下的bbs文件夹下的day25.
<Host name="www.bbs.com" appBase="E:\bbs"
unpackWARs="true"autoDeploy="true"
xmlValidation="false"xmlNamespaceAware="false">
<Context path=""docBase="E:\bbs\myday25"/>
</Host>
3.在system32--->divers-àextà host文件中配置域名对应的访问主机 例如: www.bbs.com.对应的主机 127.0.0.1
4.在web.xml中配置需要访问的首页例如:
<web-app>
<welcome-file-list>
<welcome-file>welcome.jsp</welcome-file>
</welcome-file-list>
</web-app>
配置完毕,此时可以直接访问www.bbs.com
文件上传
1)上传表单的三要素:
>>enctype="multipart/form-data":表示将上传的文件,随请求体,一起传到服务端
>>method="post"
>>name="uploadFile"为每个表单项,取个名字,便于Action收集
2)框架自身有fileUpload文件上传拦截器,但无文件下载拦截器
框架使用jakarta commons fileupload开源上传类来完成文件上传
框架默认的上传文件[总]大小为2M
3)fileUpload拦截器,自动注入三个参数,
>>uploadFile 临时文件
>>uploadFileFileName真实文件名 定义方式是jsp页面file组件的名称+FileName
>>uploadFileContentType上传文件类型, 定义方式是jsp页面file组件的名称+ContentType,
注意:uploadFile为表单上传项的名字,<s:file name="uploadFile"/>
4)上传文件细节
以下上传文件中参数查找: struts2-core-2.3.1.1.jar-àstruts-default.xml (包含文件的大小,类型,后缀)
总的文件大小及国际化文件的查找:struts2-core-2.3.1.1.jar-àorg.apache.struts2 àstatic àdefault.properties
<constantname="struts.multipart.maxSize"value="15728640"/>
<constantname="struts.custom.i18n.resources"value="cn/web/struts/fileload/file"/>
<packagename="fileload"namespace="/"extends="struts-default">
<actionname="UploadAction"class="cn.web.struts.fileload.UploadAction"method="uploadMethod">
<resultname="SUCCESS"type="dispatcher">
/WEB-INF/success.jsp
</result>
<resultname="input" type="dispatcher"><!—错误时,同过input标签找到显示错误信息的路径->
/WEB-INF/message.jsp
</result><!—临时文件的存储路径-->
<paramname="uploadPath">d:\</param>
<interceptor-refname="fileUpload">
<!-- 上传单个文件的大小 -->
<paramname="maximumSize">307200</param>
<!-- 文件的扩展名 -->
<paramname="allowedExtensions">.jsp</param>
<!-- 文件的类型 -->
<paramname="allowedTypes">image/pjpeg</param>
</interceptor-ref><!—默认的拦截器-->
<interceptor-refname="defaultStack"/>
</action>
</package>
>>上传文件的临时文件存放和删除
在try..catch..finally中通过uploadFile.delete();
>>上传文件总大小限制
<constant name="struts.multipart.maxSize"value="15728640"/> 15M
>>上传文件单大小限制
<!-- 上传单个文件大小不得超过2M -->
<param name="maximumSize">2097152</param>
>>上传文件扩展名限制
<!-- 上传单个文件的扩展名为.jpg或.txt -->
<param name="allowedExtensions">.jpg,.txt</param>
>>上传文件类型限制
<param name="allowedTypes">image/pjpeg</param>
*5)在默认的情况下,每个<action/>标签都有一个默认的defaultStack拦截器栈(18个拦截器),
如果你显示声明了使用某个拦截器,那么拦截器栈将不会被引用,类似于类的空参构造方法
6)在默认的情况下,框架会自动找框架默认的属性资源文件的提示信息,如果你让框架找自定义的属性资源文件信息,要覆盖系统的:
(1)<constant name="struts.custom.i18n.resources"value="cn/itcast/web/struts/upload/xx"/>
(2)在任意目录下,写一个*.properties的资源文件,里面覆盖框架的提示信息,注意,key不能修改
(从struts2-core-2.3.1.1.jar包下的default.properties文件中粘贴)
2栏截器,栏截器栈,类似于Filter
定义自定义拦截器的步骤
1.自定义拦截器类实现Interceptor
2.在 struts.xml 文件中配置自定义的拦截器
例:
<packagename="login" namespace="/" extends="struts-default">
<interceptors > //拦截器的声明
<interceptorname="LoginInterceptor"class="cn.web.struts.login.LoginInterceptor"/>
</interceptors>
<actionname="LoginAction"class="cn.web.struts.login.LoginAction"method="loginMethod">
<resultname="success">
/WEB-INF/success.jsp
</result>
<resultname="input">
/WEB-INF/message.jsp //错误转到的页面
</result>
<interceptor-refname="defaultStack"/> //默认拦截器
<interceptor-refname="LoginInterceptor"></interceptor-ref>//拦截器的引用,必须在action标签中
</action>
</package>
自定义拦截器类:
public class LoginInterceptor implements Interceptor{
public String intercept(ActionInvocationinvocation)throws Exception {
System.out.println(invocation.getAction());
System.out.println(invocation.invoke());
//取得拦截器所拦截的action类
Object obj = invocation.getAction();
if(obj instanceof LoginAction){
//通过ActionContext取得sessionMAp
Map<String, Object> sessionMap = invocation.getInvocationContext().getSession();
String username = (String) sessionMap.get("username");
if(username!=null){
return "success";//相当于执行了一次loginmethod()方法
}else{
return "loginJsp";
}
}
return null;
}
}
1)框架defaultStack中有18个拦截器,选引用的拦截器先被执行,如果第一个拦截器未能过,是否会进行第二个拦截器
2)如果要引用defaultStack外的拦截器,需要在<action/>标签内通过如下代码声明:
<interceptor-refname="拦截器的类名"/>
3)拦截器类似于Filter,可以进行预处理和后处理
4)拦截器在部署web应用是创建并初始化拦截器,
每请求一次LoginAction,都会被拦截拦截
拦截器需要拦截哪个Action,就只需要在该<action/>标签中引用即可(重点)
重新部署时,框架会先销毁原拦截器,再创建新的拦截器
5)拦截器是唯一的一个实例为每个客户端服务,单例模式
6)常用的API
>>Object obj =invocation.getAction();返回拦截的Action类名
>>invocation.getInvocationContext();返回ActionContext对象,从而可以取得HttpSesison,ServletContext等域对象
>>String msg = invocation.invoke();返回拦截Action中业务方法的返回值,例如"success"字符串
ActionInvocation: 代表一个给定动作的执行状态, 拦截器可以从该类的对象里获得与该动作相关联的 Action 对象和 Result 对象. 在完成拦截器自己的任务之后, 拦截器将调用 ActionInvocation 对象的 invoke 方法前进到 Action 处理流程的下一个环节,类似于chain.doFilter()。
注意:拦截器中的"字符串"类似于Filter中的chain.doFiter()
3 国际化: 配置.properties文件,将需要改变的值通过建和值的关系在该文件中写出,中文时需要编码(utf-8),当部署web引用时,该文件会自动加载,在访问的页面中只需要通过键引入即可. 注:在配置属性文件时不能有空格.
例: 键=值
username=\u7528\u6237\u540D
password=\u5BC6\u7801
4 结果类型和异常处理
1)常用的结果类型;
>>dispatcher //转发到指定的页面
>>redirect //重定向到指定的页面
>>redirectAction //重定向到其他的Action
2)当每个<action/>标签中,都有相同时结果类型,可以将其配置成全局结果类型;
3)当局部结果类型和全局结果类型冲突时,局部结果类型优先(在<action/>标签中配置的<result/>叫局部结果类型),在<global-results>标签中配置的<result/>叫,全局结果类型.
4)当异常发生时,捕获的是子异常,如果无子异常,捕获的是父异常.
5)当局部异常类型和全局异常类型冲突时,局部异常类型优先.
6)如果局部异常是父类,全局异常是子类,此时捕获的全局异常
7)在异常体系中,子异常优先。
<packagename="result" namespace="/" extends="struts-default">
<!-- 配置全局结果 -->
<global-results>
<resultname="success"type="dispatcher">
/WEB-INF/result_success.jsp
</result>
<resultname="sonResultInner"type="dispatcher">
/WEB-INF/son.jsp
</result>
<resultname="sonResultOutter"type="dispatcher">
/WEB-INF/father.jsp
</result>
</global-results>
<!-- 配置全局异常 -->
<global-exception-mappings>
<exception-mappingresult="sonResultOutter"exception="java.lang.ArithmeticException"/>
</global-exception-mappings>
<!-- FromAction配置 -->
<actionname="FromAction"class="cn.itcast.web.struts.result.FromAction">
<resultname="goToAction"type="redirectAction">
<!-- 需要重定向到另一个action标签的name属性值-->
<paramname="actionName">ToAction</param>
<paramname="namespace">/</param>
<paramname="method">execute</param>
</result>
</action>
<!-- ToAction配置 -->
<actionname="ToAction"class="cn.itcast.web.struts.result.ToAction">
<!-- 配置局部结果 -->
<resultname="success"type="dispatcher">
/WEB-INF/local_success.jsp
</result>
<!-- 配置局部异常-->
<exception-mappingresult="sonResultInner"exception="java.lang.Exception"/>
</action>
</package>
类型转换器
1)HTTP请求,只有String或String[]类型,无其它类型,例如没有Integer,Address类型
2)在默认情况下,拦截器自动将String或String[]类型,转成8种类型数据类型,例如String转成Float类型
3)在默认情况下,框架支持String类型转成日期类型,但字符串格式必须是yyyy-MM-dd这种形式。框架也能
验证输入日期字符串的合法性
自定义类型转换器
1)能够决定String转成任意JavaBean类型。
自定义转换器步骤:
1.1类 继续 StrutsTypeConverter类 , 复写该类中的方法
public Object convertFromString(Map context,String[] values,Class toClass) {};
values:表示表单提交时传入需要转换的字符串
例如: 在表中提交的地址:
<s:textfieldlabel="地点"name="address"value="广州-越秀-越秀路"/>"广州-天河-越秀路"
toClass: 表示需要转换的类的字节码.例如:"cn.itcast.web.type.Address"
public String convertToString(Map context,Object obj) {}
obj:表示经过转换后的javaBean实例 :例如: "cn.itcast.web.type.Address"
例: 地址的转换器
public class AddressConverter extends StrutsTypeConverter{
public Object convertFromString(Map context,String[] values,Class toClass) {
System.out.println("字符串转换JavaBean");
//System.out.println(context);
//System.out.println(values[0]);"广州-天河-东圃"
//System.out.println(toClass);"cn.itcast.web.type.Address"
if(toClass == Address.class){
String msg = values[0];
//以"-"拆分字符串
String[] all = msg.split("-");
String city = all[0];
String area = all[1];
String brt = all[2];
//封装成JavaBean对象
Address address = new Address(city,area,brt);
return address;
}
return null;
}
public String convertToString(Map context,Object obj) {
System.out.println("JavaBean转换字符串");
//System.out.println(context);
//System.out.println(obj);"cn.itcast.web.type.Address"
if(obj instanceof Address){
//将obj强转成Address类
Address address = (Address) obj;
//将Address转成String
return address.toString();
}
return null;
}
}
1.2.在IDE工具的src目录下新建一个xwork-conversion.properties文件,里面的内容为:
JavaBean的全路径=自定义类型转换器的全路径,例如
cn.itcast.web.struts2.type.Address=cn.itcast.web.struts2.type.AddressConverter
验证的xml文件的规范在xwork-core-2.1.6.jar包下的:xwork-validator-1.0.2.dtd
验证文件的命名
在这个校验文件中,对action中字符串类型的username属性进行验证,首先要求调用trim()方法去掉空格,然后判断用户名是否为空。该文件需要和action类放在同一个包下,文件的取名应遵守ActionClassName-validation.xml规则,其中ActionClassName为action的简单类名,-validation为固定写法。
2 基于XML文件的声明式验证
1)能够解决用声明式方式替换手工validate()和validateLoginMethod()这二个方法
2)validate()等价于UserAction-validation.xml文件,其中User Action是类名
validateLoginMethod()等价到UserAction-Login-validation.xml文件,其中Login为<action/>标签的name属性
3)规则这二个xml文件,必须放置在与UserAction类同级的目录下
4)先执行UserAction-validation.xml文件,再执行UserAction-Login-validation.xml文件,不是后者替换前者,
而是二个验证文件共同作用的结果(交集)。
5)UserAction-validation.xml争对该Action中所有的业务方法验证,因此该xml文件中出现所有业务方法的共同字段
UserAction-Login-validation.xml争对该Action中登录业务方法验证,因此该xml文件中出现登录的特有字段
<validators>:根元素
<field>:指定action中要校验的属性,name属性指定将被验证的表单字段的名字
<field-validator>:指定校验器, type 指定验证规则
上面指定的校验器requiredstring是由系统提供的,系统提供了能满足大部分验证需求的校验器,这些校验器的定义可以在xwork-2.x.jar中的com.opensymphony.xwork2.validator.validators下的default.xml中找到。
<param>:子元素可以向验证程序传递参数
<message>:子元素为校验失败后的提示信息,如果需要国际化,可以为message指定key属性,key的值为属性文件中的key。
例如: UserAction-Register-validation.xml文件中的校验方式:
<!-- 只争对UserAction中注册业务方法进行验证 -->
<validators>
<fieldname="username">
<field-validatortype="regex">
<paramname="trim">true</param>
<paramname="expression">^[\u4E00-\uFA29]+$</param>
<message>[用户名必填是中文]</message>
</field-validator>
</field>
<fieldname="password">
<field-validatortype="regex">
<paramname="trim">true</param>
<paramname="expression">^[0-9]{8}$</param>
<message>[密码必须是8位数字]</message>
</field-validator>
</field>
<fieldname="salary">
<field-validatortype="required">
<paramname="trim">true</param>
<message>[薪水必填]</message>
</field-validator>
<field-validatortype="int">
<paramname="trim">true</param>
<paramname="min">4000</param>
<paramname="max">6000</param>
<message>[薪水必须介于${min}到${max}之间]</message>
</field-validator>
</field>
</validators>
验证规则:
2 required: 确保某给定字段的值不是空值 null
3 requiredstring: 确保某给定字段的值既不是空值 null, 也不是空白.
• trim 参数. 默认为 true, 表示 struts 在验证该字段值之前先剔除前后空格.
4 stringlength: 验证一个非空的字段值是不是有足够的长度.
• minLength: 相关字段的最小长度. 若没有给出这个参数, 该字段将没有最小长度限制
• maxLength:相关字段的最大长度. 若没有给出这个参数, 该字段将没有最大长度限制
• trim: 在验证之前是否去除前后空格
5 int: 检查给定字段的值是否可以被转换为一个整数
• min: 相关字段的最小值. 若没给出这个参数, 该字段将没有最小值限制
• max: 相关字段的最大值. 若没给出这个参数, 该字段将没有最大值限制
6 date: 确保某给定日期字段的值落在一个给定的范围内
• max:相关字段的最大值. 若没给出这个参数, 该字段将没有最大值限制
• min:相关字段的最小值. 若没给出这个参数, 该字段将没有最小值限制
7 email: 检查给定 String 值是否是一个合法的 email
8 url: 检查给定 String 值是否是一个合法的 url
9 regex: 检查某给定字段的值是否与一个给定的正则表达式模式相匹配.
• expression: 用来匹配的正则表达式
• caseSensitive: 是否区分字母的大小写. 默认为 true
• trim: 是否去除前后空格. 默认为 true
当校验文件的取名为ActionClassName-validation.xml时,会对 action中的所有处理方法实施输入验证。
如果你只需要对action中的某个action方法实施校验,那么,校验文件的取名应为ActionClassName-ActionName-validation.xml,其中ActionName为struts.xml中action的name属性值
UserAction中有以下两个处理方法:
public Stringadd() throws Exception{
....
}
public Stringupdate() throws Exception{
....
} <action的name属性值/>
要对add()方法实施验证,校验文件的取名为: UserAction-addAction(<action的name属性值/>)-validation.xml
要对update()方法实施验证,校验文件的取名为: UserAction-updateAction(<action的name属性值/>)-validation.xml
OGNL表达式语言:
OGNL是ObjectGraphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。Struts2框架使用OGNL作为默认的表达式语言,而不是EL。
OGNL表达式可以完成:
1、访问OGNL上下文(OGNL context)和Action Context;
2、操作集合对象
3 理解OGNL(对象图导航语言)
1)EL只能在JSP中使用。通常EL可取域对象中的属性值,例如${sessionScope.username}
2)OGNL(对象图导航语言),成为struts2的默认导航语言,类似于EL。
3)ValueStack是一个接口,OGNLValueStack是实现类,
4)valueStack是每一个Action的数据中心,一旦Action创建,ValueStack就创建了,放在request域对象中,
一旦Action销毁,ValueStack就销毁,Action和ValueStack随request的结束而销毁
5)值栈分为二部分,一部分是装List类型的数据,一部份装Map类型的数据
在 ValueStack 对象的内部有两个逻辑部分:
• ObjectStack: Struts把动作和相关对象压入 ObjectStack 中--List
• ContextMap: Struts把各种各样的映射关系(一些 Map 类型的对象) 压入 ContextMap 中
Struts 会把下面这些映射压入 ContextMap 中
• parameters: 该 Map 中包含当前请求的请求参数
• request: 该 Map 中包含当前 request 对象中的所有属性
• session: 该 Map 中包含当前 session 对象中的所有属性
• application:该 Map 中包含当前 application 对象中的所有属性
• attr: 该 Map 按如下顺序来检索某个属性: request, session, application
6)<s:property/>在默认情况下,只取值栈中的内容
7)非值栈中取值,需要通过#域对象的名字
attr去以下域对象中查询,找到即此:
page->request->valuestack->session->application->空白字符串
例: 从不同的域对象中取值username,此时如果需要在每个域中都查询,需要加#查找.
ServletActionContext.getRequest().setAttribute("username","request_username");
ActionContext.getContext().getSession().put("username","session_username");
ServletActionContext.getServletContext().setAttribute("username","context_username");
ActionContext.getContext().getValueStack().set("username","value_username");
reqeust: <s:property value="#request.username"/><br/>
valuestack: <s:propertyvalue="#attr.username"/><br/>
session: <s:propertyvalue="#session.username"/><br/>
appliaction:<s:property value="#application.username"/><br/>
8)非值栈中取得,加#,就算是JavaBean对应,也需要通过#来取值
9)#号的语法: 如#{‘foo1’:‘bar1’,‘foo2’:‘bar2’}。
集合的过滤
1) 集合的过滤有以下三种方式:
a.“?#”:过滤所有符合条件的集合,如:users.{?# this.age > 19};
b.“^#”:过滤第一个符合条件的元素,如:users.{^# this.age > 19};
c.“$#”:过滤最后一个符合条件的元素,如:users.{$# this.age > 19} 。
.2) this 表示集合中的元素;
语法一: 从list集合中取出名username的所有用户
<s:iterator var="username"value="#request.userList.{username}">
<s:propertyvalue="#username"/>
</s:iterator>
语法二:从list集合中取出年龄大于30的所有用户
<s:iterator var="username"value="#request.userList.{?#this.age>=30}">
表示只输入age>=30的用户,
?#表示输出所有用户
^#表示输出第一行用户
$#表示输出最后行用户
this表示当前需要迭代的JavaBean
<s:propertyvalue="#username"/>
</s:iterator>
语法三:从list集合中取出年龄大于30的第一个用户
<s:iterator var="user"value="#request.userList.{^#this.age>=30}[1]">
[0]表示第一行记录
[n]表示第n+1行记录
<s:propertyvalue="#user.username"/>---->
<s:propertyvalue="#user.age"/><br/>
</s:iterator>
语法四:从list集合中取出最后一个用户
<s:iterator var="user" value="#request.userList.{$#this.age>=30}">
[0]表示第一行记录
[n]表示第n+1行记录
<s:propertyvalue="#user.username"/>---->
<s:propertyvalue="#user.age"/><br/>
</s:iterator>
10)%号语法
“%”符号的用途是在标签的属性值被理解为字符串类型时,告诉执行环境%{}里的是OGNL表达式。
语法一:
ActionContext.getContext().getValueStack().set("usernameTip","[用户名]");
<s:textfield label="%{usernameTip}"name="username"/>
语法二:
<s:textfieldlabel="%{'usernameTip'}" name="username"/>此时usernameTip为常量
11)$号语法
语法一:
${}可以出现在xml文件中
<param name="min">1000</param>
<paramname="max">5000</param>
<message>薪水必须是${min}-${max}</message>
property标签用于输出指定值:
<s:property value=“#name"default="a default value" />
default:可选属性, 如果需要输出的属性值为null,则显示该属性指定的值
escape:可选属性,指定是否格式化HTML代码。
value: 可选属性,指定需要输出的属性值,如果没有指定该属性,则默认输出ValueStack栈顶的值。
例:
<%
pageContext.setAttribute("username","<a href='/myday34/login.jsp'>超链接</a>");
%> <!—从context域对象中取出用户信息,设置不转义-->
<s:propertyvalue="#attr.username"default="访问页面不存在"escapeHtml="false"/>
Set 语法:
set标签用于将某个值放入指定范围。
var:变量的名字,name,id和var表达的含义是一样的,name,id被var替代
scope:指定变量被放置的范围,该属性可以接受application、session、request、 page。如果没有设置该属性,则默认放置在action中,即值栈。
value:赋给变量的值.如果没有设置该属性,则将ValueStack栈顶的值赋给变量
例:
<%
request.setAttribute("password","654321");
%>
<s:setvar="code" value="#request.password" scope="session"/>
密码:<s:propertyvalue="#session.code"/>
Iterator:语法:
Iterator:标签用于对集合进行迭代,这里的集合包含List、Set,Map和数组。
value:可选属性,指定被迭代的集合,如果没有设置该属性,则使用ValueStack栈顶的集合。
var: 可选属性,引用变量的名称.
status:可选属性,该属性指定迭代时的IteratorStatus实例。该实例包含如下几个方法:
int getCount(),返回当前迭代了几个元素。
int getIndex(),返回当前迭代元素的索引。
boolean isEven(),返回当前被迭代元素的索引是否是偶数
boolean isOdd(),返回当前被迭代元素的索引是否是奇数
boolean isFirst(),返回当前被迭代元素是否是第一个元素。
boolean isLast(),返回当前被迭代元素是否是最后一个元素。
<body>
<%
List<User> userList = new ArrayList<User>();
userList.add(new User("张三",13));
userList.add(new User("李四",25));
userList.add(new User("王五",27));
userList.add(new User("哈哈",30));
session.setAttribute("userList",userList);
%>
<tableborder="1" align="center">
<tr>
<td>序号</td>
<td>索引号</td>
<td>用户名</td>
<td>年龄</td>
<td>最后一行</td>
<td>第一行</td>
</tr>
<s:iteratorvar="user" value="#session.userList" status="sta" begin="0"end="2">
<trstyle='color:<s:propertyvalue="#sta.odd?'red':'blue'"/>'>
<td><s:propertyvalue="#sta.count"/></td>
<td><s:propertyvalue="#sta.index"/></td>
<td><s:propertyvalue="#user.username"/></td>
<td><s:propertyvalue="#user.age"/></td>
<td><s:propertyvalue="#sta.last"/></td>
<td><s:propertyvalue="#sta.first"/></td>
<td>
<s:iftest="#user.age<20">
少年
</s:if>
<s:elseiftest="#user.age>20&& #user.age<40">
中年
</s:elseif>
<s:else>
老年
</s:else>
</td>
</tr>
</s:iterator>
</table>
List 语法:
<%
List<User> userList = new ArrayList<User>();
userList.add(new User("张三",13));
userList.add(new User("李四",25));
pageContext.setAttribute("userList",userList);
%>
集合是否为空: <s:propertyvalue="#attr.userList.isEmpty"/><br/>
集合的长度: <s:propertyvalue="#attr.userList.size"/>
<hr/>
<s:iteratorvar="list" value="{'one','tow','three'}">
<s:propertyvalue="list"/><br/>
</s:iterator>
所有使用到的标签总结:
textfield 标签将被呈现为一个输入文本字段, password 标签将被呈现为一个口令字段,hidden 标签将被呈现为一个不可见字段
password 标签扩展自 textfield 标签, 多了一个 showPassword 属性.该属性是布尔型.
默认值为 false, 它决定着在表单回显时是否显示输入的密码. true显示密码
<body>
<s:formmethod="post" action="#" theme="xhtml">
<s:textfieldlabel="用户名"name="username"/>
<s:passwordlabel="密码"name="password"/>
<s:radiolabel="性别"name="gender" list="{'男','女'}"value="'女'"></s:radio>
<s:checkboxlistlabel="爱好"name="like"
list="#{'sing':'唱歌','dance':'跳舞','play':'打球' }"listKey="key"listValue="value"value="{'play','sing'}"/>
<s:selectlabel="省份"name="province"list="{'广东省','陕西省'}"value="'广东省'"/>
<s:selectlabel="城市"name="city" list="{'西安市','广州市'}"value="'广州市'"/>
<%
List<Area> areaList = new ArrayList<Area>();
areaList.add(new Area(1,"天河区"));
areaList.add(new Area(2,"越秀区"));
pageContext.setAttribute("areaList", areaList);
%>
<s:selectlabel="地区"name="area" list="#attr.areaList" listKey="id" listValue="area"value="'2'"/>
<s:textarealabel="文本框"name="留言板"/>
<s:submitvalue="确认"/>
<s:resetvalue="重置"/>
</s:form>
</body>
5 Struts2主题更换
1)有四种不同的风格:
>>simple(无table>
>>xhtml(默认)(有table)
>>css_xhtml(无table有div+css)
>>ajax
2)局部优先
1、simple: 把 UI 标签翻译成最简单的 HTML 对应元素, 而且会忽视行标属性
2、xhtml: xhtml 是默认的主题. 这个主题的模板通过使用一个布局表格提供了一种自动化的排版机制.
3、css_xhtml: 这个主题里的模板与xhtml 主题里的模板很相似, 但它们将使用 css 来进行布局和排版
4、ajax: 这个主题里的模板以xhtml 主题里德模板为基础, 但增加了一些 Ajax 功能.
修改主题:
A、通过 UI 标签的theme属性(只适用于当前的标签)
<s:textfieldname="username" label="用户名“theme="simple"></s:textfield>
B、在一个表单里, 若没有给出某个 UI 标签的 theme 属性, 它将使用这个表单的主题
(适用于整个form标签)
<s:form action="" method="post"namespace="/ui“ theme="simple">
C、修改 struts.properties 文件中的 struts.ui.theme 属性. (适用整个环境)
<!-- 设置ui标签的主题-->
<constant name="struts.ui.theme"value="simple"></constant>
优先级:A > B > C
*6 Struts2表单重复提交
1)在表单中使用<s:token/>这个标签,来产生客户端和服务端session中唯一的编号
2)在<action/>标签中显示引用token拦截器,因为token拦截器不是默认栈中的拦截器,
同时最好显示引用defaultStack拦截器栈。
例: token_struts.xml文件配置
<s:formaction="TokenAction"method="post">
<s:token />
<s:textfieldlabel="用户名"name="username"/>
<s:password label="密码"name="password"/>
<s:submitvalue="token表单重复提交验证"/>
</s:form>
</body>
<!—配置token_struts.xml文件 -->
<struts>
<packagename="token" namespace="/" extends="struts-default">
<actionname="TokenAction"class="cn.itcast.web.struts2.token.TokenAction"method="execute">
<resultname="success"type="dispatcher">
/WEB-INF/token_success.jsp
</result><!—防止表单重复提交token配置-->
<result name="invalid.token"type="dispatcher">
/WEB-INF/bad.jsp
</result> <!—显示引用默认拦截器 -->
<interceptor-refname="defaultStack"/>
<!-- 引用token拦截器 -->
<interceptor-ref name="token"/>
</action>
</package>
</struts>
Hibernate:
1 ORM思想与hibernate实现
1)JDBC或DBUtils框架有何缺点?
jdbc/dbutil/hibernate主要解决持久层的常见问题
jdbc不足:
原jdbc代码中,出现了面向关系的代码,例如:String sql = "select * from customers表";
原jdbc代码中,关系数据(表名或列名)需要不段变化,不能灵活对待,即[动态生成SQL代码]
原jdbc代码中,对象(O)和关系(R)的操作需要程序员自行解决
dbutil框架不足:
原dbutil代码中,出现了面向关系的代码,例如:String sql = "select * from customers表";
原dbutil代码中,关系数据(表名或列名)需要不段变化,不能灵活对待,即[动态生成SQL代码]
hibernate框架:
不出现所有的面向关系的代码,即sql语句
将面向对象的代码转换成面向关系的SQL语句,因为关系型数据库只识别SQL语句,能够动态生成SQL语句
*2 hibernate快速入门
1)开发步骤:
>>导入hibernate相关的10个jar包.
>>创建表:customers.sql
>>创建实体:Customer.java
>>配置过滤器
文件路径:E:\java\开发类库\struts-2.3.1.1\src\apps\blank\src\main\webapp\
WEB-INF\web.xml
>>创建Customer.hbm.xml映射文件,目的是通知hibernate框架,如何将实体和表关联起来
>>创建hibernate.cfg.xml配置文件, 目的是通知hibernate框架,如何连接不同的数据库和产生不同数据库的SQL命令
>>测试类:CURD操作(CustomerService)
2具体步骤详解
>>jar 包:
antlr-2.7.6.jar 一个语言转换工具,Hibernate利用它实现 HQL 到 SQL 的转换
commons-collections-3.1.jar collections Apache 的工具集,用来增强Java对集合的处理能力
dom4j-1.6.1.jar dom4j XML 解析器
hibernate3.jar 核心包
javassist-3.9.0.GA.jar 代码生成工具, Hibernate用它在运行时扩展 Java类
jta-1.1.jar 标准的 JAVA 事务处理接口
log4j.jar 日志包
mysql-connector-java-5.1.7-bin.jar mysql驱动包
slf4j-api-1.5.8.jar slf4j-log4j12.jar hibernate使用的一个日志系统
>>创建sql表
例: drop database if exists hibernate;
create databaseif not exists hibernate;
use hibernate;
drop tableif exists customer;
create tableif not exists customer(
id int primary key auto_increment,
name varchar(20) default "小小",
age int default 21,
des varchar(100) default "文雅"
);
select * from customer;
>>Customer.hbm.xml文件
1.Hibernate 采用 XML 格式的文件来指定对象和关系数据之间的映射. 在运行时 Hibernate 将根据这个映射文件来生成各种 SQL 语句, 映射文件的扩展名为 .hbm.xml 如;Customer.hbm.xml文件. 如: 以上Customer.hbm.xml文件是Customer类和customers表的对应关系。该文件前缀必须和类名一致,后缀必须是.hbm.xml。文件位置任意
2. property元素:设定持久化类的属性和表字段映射
name属性:指定持久化类的属性的名字
column属性:指定表中字段的名称
type属性指定Hibernate映射类型 Hibernate映射类型是java类型与SQL类型的桥梁
*.hbm.xml文件的来源:
hibernate-distribution-3.5.6-Final-dist.zip\hibernate-distribution-3.5.6-Final\project\tutorials\web\src\main\resources\org\hibernate\tutorial\domain......
注:该映射文件的规范在org.hibernate.hibernate-mapping-3.0.dtd文件中
例:
Customer.hbm.xml文件的属性含义:
<hibernate-mappingpackage="cn.itcast.web.hibernate.character0(实体对象的包名)">
<classname="Customer(类名)" table="CUSTOMERS(表名)">
<idname="id" column="ID" type="integer">
<generator class="increment"/>
</id>
<propertyname="name(属性名)" column="NAME(列名)" type="string(hibernate类型)"/>
<propertyname="age(属性名)" column="AGE(列名)" type="integer(hibernate类型)"/>
<propertyname="des(属性名)" column="DES(列名)" type="string(hibernate类型)"/>
</class>
</hibernate-mapping>
>>hibernate.cfg.xml文件
创建 Hibernate 配置文件,Hibernate 从其配置文件中读取和数据库连接的有关信息, 这个文件应该位于应用的 classpath 下.文件名必须是hibernate.cfg.xml,文件位置任意, 除了使用xml作为配置文件之除,还可以使用hibernate.properties文件(项目中不提倡)。
注:该映射文件的规范在org.hibernate.hibernate-configuration-3.0.dtd文件中,文件的来源: hibernate-distribution-3.5.6-Final-dist.zip\hibernate-distribution-3.5.6-Final\project\tutorials\web\src\main\resources
例:
hibernate.cfg.xml文件的属性含义:
<hibernate-configuration>
<session-factory>
<!-- 配置连接数据库的相关属性 -->
<propertyname="connection.driver_class">com.mysql.jdbc.Driver(驱动类名)</property>
<propertyname="connection.url">jdbc:mysql://127.0.0.1:3306/hibernate(URL)</property>
<propertyname="connection.username">root(访问数据库的用户名)</property>
<propertyname="connection.password">root(访问数据库的密码)</property>
<!-- 通知hibernate动态生成哪个数据库的SQL命令-->
<propertyname="dialect">org.hibernate.dialect.MySQL5Dialect(数据库的方言)</property>
<!-- 显示hibernate动态生成的SQL命令,true表示显示-->
<propertyname="show_sql">true(需要显示sql命令)</property>
<!-- 加载对象和关系的映射文件-->
<mappingresource="cn/itcast/web/hibernate/character0/Customer.hbm.xml(映射文件的类路径)"/>
</session-factory>
</hibernate-configuration>
>>Configuration类:
是hibernate中一个用于加载hibernate.cfg.xml文件的类,config .config(“xml文件的类路径”)方法能够加载指定目录下的hibernate.cfg.xml文件,config.buildSessionFactory()创建一个SessionFactory工厂。该工厂有大量空闲的session.
Configuration 类负责管理Hibernate 的配置信息。包括如下内容:
Hibernate运行的底层信息:数据库的URL、用户名、密码、JDBC驱动类,数据库Dialect,数据库连接池等(对应 hibernate.cfg.xml 文件)。持久化类与数据表的映射关系(*.hbm.xml 文件).
创建 Configuration 的两种方式
• 属性文件(src/hibernate.properties):
Configuration cfg = new Configuration();
• Xml文件(src/hibernate.cfg.xml)推荐
Configuration cfg = newConfiguration().configure();
Configuration cfg = newConfiguration().configure(“目录”);
>>SessionFactory接口
保存了当前的数据库配置信息和所有映射关系以及预定义的SQL语句。同时,SessionFactory还负责维护Hibernate的二级缓存,有且只放置一种类型数据库的相关信息 , 重量级,即创建需要大量时间和资源,因此将创建SessionFactory的工作,放在static块中,只创建一个,便于重用 , SessionFactory是一个线程安全的,所以可以定义成实例变量,无需加synchronized。
创建方式:
Configurationcfg = new Configuration().configure();
SessionFactory sf =cfg.buildSessionFactory();
>>Session
SessionFactory.openSession() ;
Session是由SessionFactory获取而来,它是轻量级,即创建需要少量时间和资源,每次数据库的操作(CURD)都需要一个Session的绑助,没有Session就无法与数据库表操作。
Session 是应用程序与数据库之间交互操作的一个单线程对象,是 Hibernate 运作的中心,所有持久化对象必须在 session 的管理下才可以进行持久化操作。此对象的生命周期很短。Session 对象有一个一级缓存,显式执行 flush 之前,所有的持久层操作的数据都缓存在 session 对象处。相当于 JDBC 中的 Connection.
Session常用的API
session.save(对象) //向数据库插入数据
session.update(对象) //更新数据
session.delete(对象) //删除数据
session.get(对象的字节码class文件,序号) //查询数据
Query query =session.createQuery("hibenate query language语句,不是sql语句")
List<类> list = query.list()
Session是不安全的,不要定义成实例变量,除非加synchronized。
将Session定义成局部变量,即在某个方法内部,就无需加synchronized。
>>Transaction接口(事物)
通过session.getTransaction()取得事务对象,在应用时,除查询外,其它都需要事务的支持,查询对事务的要求不限(可要可不要),
开始事务:t.begin()
提交事务:t.commit()
回滚事务:t.rollback()
显示try..catch...finally...
例:
public void saveCustomer(){
//创建customer对象,并设置属性值
Customer cus = new Customer();
cus.setName("思思");
cus.setAge(21);
cus.setDes("文静");
//创建confingration对象
Configuration config = new Configuration();
//加载配置文件,连接驱动 com.mysql.jdbc.driver,jdbc:mysql//127.0.0.1:3306/hibernate
config.configure("cn/web/hibernate/hibernate.cfg.xml");
//创建sessionfactory
SessionFactory sessionfactory =config.buildSessionFactory();
//创建session
Session session = sessionfactory.openSession();
//创建事物
Transaction transaction=session.getTransaction();
try {
//事物开始
transaction.begin();
//保存实体域对象到表中
session.save(cus);
} catch (HibernateException e) {
//错误的情况下事物恢复到原有的状态
transaction.rollback();
}finally{
//事务结束
transaction.commit();
}
session.close();
}
Hibernate运行过程
Hibernate的运行过程如下:
1、应用程序先调用Configuration类,该类读取Hibernate配置文件(hibernate.cfg.xml)及映射文件(customer.hbm.xml)中的信息,
2、并用这些信息生成一个SessionFactory对象,
3、然后从SessionFactory对象生成一个Session对象,
4、并用Session对象生成Transaction对象;
A、可通过Session对象的get(),load(),save(),update(),delete()和saveOrUpdate()等方法对PO进行加载、保存、更新、删除、等操作;
B、在查询的情况下,可通过Session对象生成一个Query对象,然后利用Query对象执行查询操作;如果没有异常,Transaction对象将提交这些操作到数据库中。
Hibernate是什么
是指面向java环境的对象/关系数据库映射工具。
1.开源的持久层框架.
2.ORM(Object/RelationalMapping)映射工具,建立面向对象的域模型和关系数据模型之间的映射.
3.连接java应用和数据库的中间桥梁.
4.对JDBC进行复杂封装,负责java对象的持久化(CURD).
5.在分层结构中处于持久化层,封装对数据库的访问细节,使业务逻辑层更专注于实现业务逻辑
为什么要用Hibernate
1、Hibernate对JDBC访问数据库的代码做了封装,动态产生SQL, 大大简化了数据访问层繁琐的重复性代码。
2、Hibernate是一个基于jdbc的主流持久化框架,是一个优秀的orm实现,它很大程度的简化了dao层编码工作。
3、Hibernate使用java的反射机制增强程序类实现透明性
4、Hibernate的性能非常好,因为它是一个轻量级框架。映射的灵活性很出色。它支持很多关系型数据库,从一对一到多对多的各种复杂关系。
Java对象持久化概念
(1)hibernate是持久层一个开源框架,能够将Java对象与记录一一对应转换,它封装了JDBC的具体细节,让程序更专注于具体业务的开发,更加面向对象。
(2)Hibernate不和特定的业务领域相关,能够把任意一个Java应用与数据库系统连接,可以理解为是一种中间件。
(3)hibernate对jdbc进行复杂封装,能够动态产生SQL代码,无需程序员自行编写SQL代码
(4)持久层中间件:
1.代码重用性高,可完成所有的数据访问操作。
2.如果需要的话,能够支持多种数据库平台。
3.具有相对独立性,当持久化层变化时,不会影响上层实现。
概念模型:模拟问题域中的真实实体。描述每个实体的概念和属性及实体间关系。不描述实体行为。实体间的关系有一对一、一对多和多对多.
关系数据模型:在概念模型的基础上建立起来的,用于描述这些关系数据的静态结构。有以下内容组成:
1.若干表
2.表与表之间的参照完整性
域模型:在软件的分析阶段创建概念模型,在软件设计阶段创建域模型。
组成部分:
1.具有状态和行为的域对象。
2.域对象之间的关联
域对象(domain object):构成域模型的基本元素就是域对象。对真实世界的实体的软件抽象,也叫做业务对象(Business Object(BO)).域对象可代表业务领域中的人、地点、事物或概念。
域对象分为以下几种:
实体域对象:通常是指业务领域中的名词。(pojo) (plain old java object)。--映射数据库中的表即Customer)
过程域对象:应用中的业务逻辑或流程。依赖于实体域对象,业务领域中的动词。如发出订单、登陆等。(对pojo操作的方法,即CustomerDao)
域对象间的关系
1.关联:类间的引用关系。以属性定义的方式表现
2.依赖:类之间访问关系。无需定义成属性。在A中访问B中的方法或属性,或者A负责实例化B。
3.聚集(Aggregation):整体与部分的关系。例人与手的关系。部分类的对象不能单独存在,他的生命周期依赖于整体类的对象的生命周期,即整体消失时,部分也随之消失。
4.一般化(Generalization):类之间继承关系
域对象的持久化概念
域对象在内存中创建后,不能永久存在。将域对象永久保存起来,就是持久化的过程。通常只有域对象需要持久化,过程域对象和事件域对象一般不需要持久化。广义持久化指增、删、改、查。
将实体域对象,永久保留到数据库表的过程,叫实体域对象的持久化,例如:Customer对象存到数据库表中内存->表
ORM:(Object/Relation Mapping): 对象/关系映射主要解决对象-关系的映射
1.将关系数据库中表中的记录映射成为对象,以对象的形式展现,程序员可以把对数据库的操作转化为对对象的操作。因此ORM的目的是为了方便开发人员以面向对象的思想来实现对数据库的操作。 ORM 通常采用 XML 格式, 并且存放在专门的对象-关系映射文件中.
2.ORM只是一个思想,DBUtils/Hibernate等都是该思想的一个实现者,只是优良程度,灵活程度不同而以
3.ORM(Hibernate)是业务层和数据层的一个中间件(桥梁)
对象-关系映射基础
1.propertye 默认值:表明hibernate通过getXxx和setXxx来访问类属性,推荐使用,提高域对象透明性
<property> insert属性 ,若为false,在insert语句中不包含该字段,该字段永远不能被插入。默认值true。
2. <property> update属性 若为false,update语句不包含该字段,该字段永远不能被更新。默认值为true。
3.<class> mutable属性 若为false,等价于所有的<property>元素的update属性为false,整个实例不能被更新。默认为true。
4. <class> dynamic-insert属性 若为true,等价于所有的<property>元素的insert为true,保存一个对象时,动态生成insert语句,语句中仅包含取值不为null的字段。默认false。
5.<class> dynamic-update属性 若为true,等价于所有的<property>元素的update为true,更新一个对象时,动态生成update语句,语句中仅包含取值不为null的字段。默认false。
6:设置类的包名 ,如果在一个映射文件中包含多个类,并且这些类位于同一个包中,可以设置<hibernate-mapping>元素的package属性,避免为每个类提供完整的类名.
映射对象标识符
1.Java按地址区分同一个类的不同对象.
2.关系数据库用主键区分同一条记录.
3. Hibernate使用OID来建立内存中的对象和数据库中记录的对应关系。对象的OID和数据库的表的主键对应。为保证OID的唯一性,应该让Hibernate来为OID覆值。
键为分二类:
把主键定义为自动增长类型
在my SQL中,把字段设为auto_increment类型,数据库会自动为主键赋值。
在ms SQL server中,把字段设为identity类型,数据库会自动为主键赋值. oracle从序列(sequence)中获取自动增长的描述符 insert into customers values(seq_customer.curval,’..’)
>>自然主键:就是表中含有业务性质的列名,例如:name,age,des等,不提倡使用自然主键
>>[代理主键]:就是表中无业务性质的列名,例如:id,提倡使用代理主键
4)hibernate的主键生成策略
>>increment:是通过[算法]得到的,不是mysql的auto_increment,底层数据库是否有auto_increment机制,对于increment来说,无所谓。
算法:先查询最大的id,再在原有基础上+1,最后的值做为主键值
缺点:在单线中不会有问题,但在多线程并发情况下,会出现在插入主键冲突问题,项目中不提倡使用increment
*>>identity
算法:直接在原有基础上+1,再将新值取走,最后的新值做为主键值
缺点:必须依懒于底层数据库的自动增长机制。如果底层数据库无自动增长机制,则不能使用identity策略
在多线程情况下,可以使用,提倡使用
*>>native:hibernate将自已选择合适底层数据库的ID自动增长策略,程序员无控制权。
>>assigned:解决自然是单个且主键为string类型的问题,此时就不能使用increment,identity,native
>>composite-id:解决自然主键为联合主键且都是string类型的问题
Increment 适用于代理主键。由hibernate自动以递增的方式生成表识符,每次增量为1
Identity 适用于代理主键。由底层数据库生成表识符。条件是数据库支持自动增长数据类型。
Sequence 适用于代理主键。Hibernate根据底层数据库序列生成标识符。条件是数据库支持序列。
Hilo 适用于代理主键。Hibernate根据hign/low算法生成标识符。Hibernate把特定表的字段作为“hign”值。默认情况下,采用hibernate_unique_key表的next_hi字段。
Native 适用于代理主键。根据底层数据库对自动生成表示符的能力来选择identity、sequence、hilo
Uuid.hex 适用于代理主键。Hibernate采用128位的UUID算法来生成标识符。该算法能够在网络环境中生成唯一的字符串标识符,这种策略并不流行,因为字符串类型的主键比整数类型的主键占用更多的数据库空间。
Assigned 适用于自然主键。由java程序负责生成标识符。尽量避免使用自然主键。
映射一对多[双向]关联关系:
1 多对一[单向]关联关系
>>对于多对一[单向]关联,最好先保存单方(客户),后保存多方(订单),提高性能
>>当订单关联客户时,在保存订单时,可以级联cascade客户,此时订单所关联的客户一起保存,
当订单和客户没有关联关系时,保存的只有订单,而此时所引用的外键为null
>>如果一个实体域对象想与数据库交互,必须位于session一级缓存中
>>设置代码如下:
<many-to-one
name="customer"
class="Customer"
column="customers_id"
cascade="save-update"可以级联保存和更新
cascade="delete"可以级联保存和更新/>
>>如果需要将一个客户下的订单更新到新的客户,此时可以只需要查询所需要更新的定单和更新到那个客户即可,此操作不会更新原有的客户,只是订单的更新.
>>如果删除一个订单,相级联删除该订单所关联的客户,可以使用cascade="delete",(前提,无外键引用),可以没有使用cascade="delete",只删除订单,不删除客户
例:
use hibernate;
drop tableif exists orders;
drop tableif exists customers;
create tableif not exists customers(
id int primary key auto_increment,
name varchar(20)
);
create tableif not exists orders(
id int primary key auto_increment,
orderNUM varchar(20),
cid int,
constraint cid_FK foreign key(cid) references customers(id)
);
select * from customers;
select * from orders;
//单方
public class Customer {
private Integer id;//主键
private String name;//用户名
}
<!-- 配置对象/关系映射文件 -->
<hibernate-mappingpackage="cn.web.hibernate.single">
<classname="Customer"table="customers">
<idname="id" column="id" type="integer">
<!-- native表示更具底层的数据库自动增长标示符id -->
<generatorclass="native"/>
</id>
<propertyname="name" column="name" type="string"/>
</class>
</hibernate-mapping>
//多方
public class Order {
private Integer id;//主键
private String orderNUM;//编号
private Customer customer;//用户
}
<!-- 配置对象/关系映射文件 order.hbm.xml-->
<hibernate-mappingpackage="cn.web.hibernate.single">
<classname="Order" table="orders">
<idname="id" column="id" type="integer">
<generatorclass="native"/>
</id>
<propertyname="orderNUM"column="orderNUM"type="string"/>
<!-- 在多对一单向关联的多方使用该标签, name:表示多方中引用的单方的属性, class:表示多方的属性类型 ,column:表示多方表引用的外键, cascade;表示在删除关联的多方的同时连同单方一起删除
cascade="save-update:表示更新保存客户"-->
<many-to-onename="customer"class="Customer"column="cid" cascade="delete"/>
</class>
</hibernate-mapping>
2 一对多[双向]关联关系
>>在对象导航中,保存哪个实体域对象,该实体域对象和其所关联的所有实体域对象,都会被保存,前提是在对应的hbm.xml文件中配置了cascade="save-update",如果没有配置cascade,则不会保存实体域对象,并会出现异常发生
>>位于session一级缓存中的实体域对象有会一个快照(复本),当提交后,会将新旧快照进行比较,不同则更新;相同则不更新。
>> 如果需要通过单方查询所有信息,此时需要配置cascade=”save-update”,例如:通过Customer.中,查询一个客户的所有订单信息,此时需要配置
>>如果需要更新订单为其他客户时,,需要配置inverse:false表示单方来维护关系,无inverse,就各自管理true表示由多方来维护关系
>>如果需要解除订单的关联关系,不删除订单(未配置cascade="delete-orphan"),此时直接查询需要删除的订单和订单对应的客户,设置订单中引用客户的属性为空,并去除客户端的订单即可.如果配置了cascade="delete-orphan",在解除订单的同时把订单一起删除.
>>删除客户联级删除订单
>>单方控制多方的生命周期,即单方销毁,多方可以没有;反之,多方销毁了,单方可以存在。
例:
use hibernate;
drop tableif exists orders;
drop tableif exists customers;
create tableif not exists customers(
id int primary key auto_increment,
name varchar(20)
);
create tableif not exists orders(
id int primary key auto_increment,
orderNUM varchar(20),
constraint cid_FK foreign key(cid) references customers(id)
);
//单方(双向关联)
public class Customer {
private Integer id;
private String name;
private Set<Order> orderset=new HashSet<Order>();
}
<!-- 配置对象/关系映射文件Customer.hbm.xml-->
<hibernate-mappingpackage="cn.web.hibernate.tabledouble">
<!-- name:表示实体域对象的类名, table:表示表名, name=id:表示实体域对象的属性
column="id":表示表中的列名,type="integer":表示hibernate对应的id类型-->
<classname="Customer"table="customers">
<idname="id" column="id" type="integer">
<!-- native:表示选择合适底层数据库的ID自动增长 -->
<generatorclass="native"/>
</id>
<!-- 在多对一双向或着一对多单向关联的单方使用<set>该标签,name:表示单方中的的集合属性,table:多方的实体所对应的表名,cascade="save-update":更新并保存 ,column="cid":多方引用的外键,class:多方的类名,inverse:false表示单方来维护关系,无inverse,就各自管理,true表示由多方来维护关系(推荐),cascade:all-delete-orphan表示分离订单时,同时删除该订单 -->
<setname="orderset"table="orders"cascade="delete-orphan">
<keycolumn="cid"/>
<one-to-manyclass="Order"/>
</set>
<propertyname="name" column="name" type="string"/>
</class>
</hibernate-mapping>
//多方(双向关联)
public class Order {
private Integer id;
private String orderNUM;
private Customer customer;
}
<!-- 配置对象/关系映射文件Order.hbm.xml-->
<hibernate-mappingpackage="cn.web.hibernate.tabledouble">
<classname="Order" table="orders">
<idname="id" column="id" type="integer">
<generatorclass="native"/>
</id>
<propertyname="orderNUM"column="orderNUM"type="string"/>
<!-- 在多对一双向关联的多方使用该标签, name:表示多方中引用的单方的属性, class:表示多方的属性类型,column:表示多方表引用的外键, cascade;表示在删除关联的多方的同时连同单方一起删除
cascade="save-update:表示更新并保存数据"-->
<many-to-onename="customer"class="Customer"column="cid" cascade="save-update" />
</class>
</hibernate-mapping>
3 映射一对一[双向]关联关系
>>一对一关系,需要自已管理自已
use hibernate;
drop tableif exists person;
drop tableif exists cards;
create tableif not exists cards(
id int primary key auto_increment,
location varchar(50) not null default"广州"
);
create tableif not exists person(
id int primary key auto_increment,
name varchar(20) not nulldefault"哈哈",
c_id int,
constraint c_id_FK foreign key(c_id) references cards(id)
);
public class Card {//单方
private int id;//编号
private String location;//地址
private Person person;//人}
<!-- 配置对象/关系映射文件 -->
<hibernate-mappingpackage="cn.web.hibernate.one2one">
<classname="Card" table="cards">
<idname="id" column="id" type="integer">
<generatorclass="native"/>
</id>
<propertyname="location"column="location"type="string"/>
<!--
在一对一双向关联中非外健方使用<one-to-one>标签
name :所关联的属性
class :所关联的属性的类型
property-ref:对方关联的属性,即card
-->
<one-to-onename="person" class="Person" property-ref="card" />
</class>
</hibernate-mapping>
public class Person {//单方
private int id;//编号
private String name;//用户名
private Card card;//身份证}
<!-- 配置对象/关系映射文件 -->
<hibernate-mappingpackage="cn.itcast.web.hibernate.chapter6.one2onedouble">
<classname="Person" table="PERSONS">
<idname="id" column="ID" type="integer">
<generatorclass="native"/>
</id>
<propertyname="name" column="NAME" type="string"/>
<!--
一对一双向关联在含有外健对象的一方使用<many-to-one>标签
name:关联的属性
class :关联的属性的类型
column:外健列
unique:外健列唯一
-->
<many-to-onename="card" class="Card" column="cards_id"unique="true"/>
</class>
</hibernate-mapping>
4 映射多对多[双向]关联关系
>>多对多关系,必须有一方管理,不能各自管理,即一定要写inverse=”true”属性
>>删除管理方允许,但非管理方不能删除
use hibernate;
drop tableif exists middles;
drop tableif exists teachers;
drop tableif exists students;
create tableif not exists teachers(
id int primary key auto_increment,
name varchar(20) not null
);
create tableif not exists students(
id int primary key auto_increment,
name varchar(20) not null
);
create tableif not exists middles(
tid int,
sid int,
primary key(tid,sid),
constraint tid_FK foreign key(tid) references teachers(id),
constraint sid_FK foreign key(sid) references students(id)
);
select * from teachers;
select * from students;
select * from middles;
//多方
public class Student {
private int id;
private String name;
private Set<Teacher> teacherSet = newHashSet<Teacher>();//老师方集合
}
<!-- 配置对象/关系映射文件 student.hbm.xml-->
<hibernate-mappingpackage="cn.web.hibernate.many2many">
<classname="Student"table="students">
<idname="id" column="id" type="integer">
<generatorclass="native"/>
</id>
<propertyname="name" column="name" type="string"/>
<!--
多对多双向关联的多方set属性用<set>标签
name:关联的属性
table:中间表
key-column:学生表外键
class:老师方类
many-to-many-column:老师表外健
inverse:true表示由老师方管理
-->
<setname="teacherSet"table="middles"inverse="true">
<keycolumn="sid"/>
<many-to-manycolumn="tid" class="Teacher"/>
</set>
</class>
</hibernate-mapping>
//多方
public class Teacher {
private int id;//编号
private String name;//姓名
private Set<Student> studentSet= new HashSet< Student>();//学生方集合
}
<!-- 配置对象/关系映射文件 teacher.hbm.xml-->
<hibernate-mappingpackage="cn.web.hibernate.many2many">
<!-- name:实体域对象的类名,
table:表名
name=id:实体域对象的属性
column="id":表中的列名
type="integer":表示hibernate对应的id类型-->
<classname="Teacher"table="teachers">
<idname="id" column="id" type="integer">
<!-- native表示根据底层的数据库自动增长标示符id -->
<generatorclass="native"/>
</id>
<propertyname="name" column="name" type="string"/>
<!--
多对多双向关联的多方set属性用<set>标签
name:关联的属性
table:中间表
key-column:老师表外键
class:学生方类
many-to-many-column:学生表外健
cascade="delete"表示级联删除
-->
<setname="studentSet"table="middles"cascade="delete">
<keycolumn="tid"/>
<many-to-manycolumn="sid" class="Student"/>
</set>
</class>
</hibernate-mapping>
<set>中inverse属性(反转):在hibernate中通过对 inverse属性的值决定是由双向关联的哪一方来维护表和表之间的关系.inverse=false 的为主动方,inverse=true 的为被动方, 由主动方负责维护关联关系,在没有设置 inverse=true 的情况下,父子两边都维护父子关系 , 在 1-n 关系中,将 n 方设为主控方将有助于性能改善(如果要国家元首记住全国人民的名字,不是太可能,但要让全国人民知道国家元首,就容易的多), 在 1-N 关系中,若将 1 方设为主控方 会额外多出 update 语句。
结论:
1.在映射一对多的双向关联关系时,应该在one方把inverse属性设为true ,这可以提高性能。
2.在建立两个对象的关联时,应该同时修改关联两端的相应属性:
customer.getOrders().add(order);
order.setCustomer(customer);
这样才会使程序更加健壮,提高业务逻辑层的独立性,使业务逻辑层的程序代码不受Hibernate实现类的影响。同理,当删除双向关联的关系时,也应该修改关联两端的对象的相应属性:
Customer.getOrders().remove(order);
Order.setCustomer(null);
解除关联关系并删除
当customer.hbm.xml的<set>元素的cascade属性取值为all-delete-orphan,
Hibernate会按照如下方式处理customer对象:
1.当保存或更新customer对象时,级联保存或更新所有关联的order对象,相当于save-update.
2.当删除customer对象时,级联删除所有的order对象,相当于delete。
3.删除不再和customer对象关联的所有order对象。
当关联双方存在父子关系时,就可以把父方的cascade属性设为all-delete-orphan.
Cascade 属性:
操纵持久化对象
1.当某个堆中对象要被GC回收的前提是:该对象无任何引用,只要有对象引用,那么该对象就不会被GC回收
2.Session 接口是Hibernate 向应用程序提供的操纵对数据库的最主要的接口, 它提供了基本的保存, 更新, 删除和加载Java 对象的方法.
session的缓存
1.在 Session 接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存. 只要 Session 实例没有结束生命周期, 存放在它缓存中的对象也不会结束生命周期
2.当session的save()方法持久化一个对象时,该对象被载入缓存,以后即使程序中不再引用该对象,只要缓存不清空,该对象仍然处于生命周期中。当试图load()对象时,会判断缓存中是否存在该对象,有则返回。没有在查询数据库
3 .load()方法,会事先去session缓存中查询一下是否有该持久化对象存在,如果有就直接返回该持久化对象; 如果没有,此时再去查找数据库,加载新的持久化对象。
清理session的缓存
1.flush: 进行清理缓存(此时缓存中的数据并不丢失)的操作,让缓存和数据库同步执行一些列sql语句,相当于发送sql语句,但不提交事务;
2.commit:先调用flush() 方法,然后提交事务. 则意味着提交事务意味着对数据库操作永久保存下来;
3. clear: 清空缓存,即将session中的持久化对象[移出]session缓存,并不是删除持久化对象等价于list.removeAll();
4. session.close(),只是将session缓存销毁,但并没有将缓存中的持久化对象删除
5.session可以利用快照功能,来更新某个持久化对象所对应的记录,这就是快照功能,主要是:当session加载了customer对象后,会为customer对象的值类型的属性复制一份快照。当清理缓存时,通过比较对象的当前属性和快照,来判断对象的那些属性发生了变化。发生变化的执行sql语句, 没有发生变化不再执行语句
java对象在hibernate中会有四种不同的状态
>>临时对象[通过new产生的对象]
(1)OID为null,因为hibernate尚未分配OID给该对象
(2)没有在session缓存中
(3)数据库没有该记录存在
*>>持久化对象[通过save等方法可以将临时对象转成持久化对象]
(1)OID非null,因为hibernate已分配OID给该对象
(2)在session缓存中
(3)数据库有该记录存在
*>>游离化对象[通过clear等方法可以将持久对象转成游离化对象]
(1)OID非null,因为游离对象是从持久化对象转换而来
(2)不在session缓存中
(3)数据库可能有该记录存在
>>删除化对象[通过delete等方法可以将持久对象转成删除化对象]
(1)OID非null
(2)不在session缓存中
(3)数据库可能有该记录存在
hibernate检索方式:
Hibernate 提供了以下几种检索对象的方式
导航对象图检索方式: 根据已经加载的对象导航到其他对象
OID 检索方式: 按照对象的 OID 来检索对象
HQL 检索方式: 使用面向对象的 HQL 查询语言
HQL(Hibernate Query Language) 是面向对象的查询语言, 它和 SQL 查询语言有些相似. 在 Hibernate 提供的各种检索方式中,HQL 是使用最广的一种检索方式.
HQL 检索方式包括以下步骤:
1.通过 Session 的 createQuery() 方法创建一个 Query 对象, 它包括一个 HQL 查询语句. HQL 查询语句中可以包含命名参数
动态绑定参数
2调用 Query 的 list() 方法执行查询语句. 该方法返回 java.util.List 类型的查询结果, 在 List 集合中存放了符合查询条件的持久化对象.
3.Qurey 接口支持方法链编程风格,它的 setXxx() 方法返回自身实例, 而不是 void 类型
HQL vs SQL:
HQL 查询语句是面向对象的, Hibernate 负责解析 HQL 查询语句, 然后根据对象-关系映射文件中的映射信息, 把 HQL 查询语句翻译成相应的 SQL 语句. HQL 查询语句中的主体是域模型中的类及类的属性
SQL 查询语句是与关系数据库绑定在一起的. SQL 查询语句中的主体是数据库表及表的字段.
1.根据编号查询客户 :Object obj = session.get(类名.class,编号)
或者: Objectobj = session.load(类名.class,编号)
例: Customer c= (Customer)session.get(Customer.class, 1);
2.批量插入: Object obj = session.save(Object)
例: 设置session清理缓存的时机,该时机只在显示调用flush方法时清理
session.setFlushMode(FlushMode.NEVER);
Customer c = newCustomer(); session.save(c);
注: 级联保存客户和订单 ;需要设置cascade=”save-update”管理方:invers=”true”
3.根据姓名查询客户: Stringhql = "from 类名别名 where别名.属性='字段'";
例: String hql = "from Customer c where c.name='呵呵'";
Query query = session.createQuery(hql);
List<Customer> list = query.list();
4.查询所有客户和所有订单 : String hql = "from java.lang.Object o";
例: String hql = "fromjava.lang.Object o";
Query query = session.createQuery(hql);
List<Customer> list = query.list ();
5.按编号降序查询所有所有订单 : String hql ="from 类名别名 order by别名.属性 desc";
例: String hql = "fromOrder o order by o.id desc";
Query query = session.createQuery(hql);
List<Order> list = query.list();
15.分页查询所有所有订单 :String hql = "from 类名别名";
setFirstResult(int firstResult): 设定从哪一个对象开始检索, 参数 firstResult 表示这个对象在查询结果中的索引位置, 索引位置的起始值为 0. 默认情况下,Query 从查询结果中的第一个对象开始检索
setMaxResult(int maxResults): 设定一次最多检索出的对象的数目.
例:String hql = "fromOrder o";
Query query = session.createQuery(hql);
//从第几条记录的索引号开始
query.setFirstResult(3);
//每页显示三条记录
query.setMaxResults(3);
List<Order> list = query.list();
7.根据姓名查询用户,通过索引号绑定参数 :String hql ="from类名别名 where
别名.属性='字段'";
例 ; String hql = "from Customer c where c.name=?";
Query query = session.createQuery(hql);
//绑定?所对应的值,?号索引从0开始
query.setString(0,"思思");
List<Customer> list = query.list();
8.根据姓名查询用户,通过别名绑定参数
例: Stringhql = "from Customer c wherec.name=:cname";
Query query = session.createQuery(hql);
//绑定:xx所对应的值
query.setString("cname","三藏");
List<Customer> list = query.list();
9. 根据姓名和编号查询用户:
例: String hql = "from Customer c where c.id=? andc.name=?";
Query query = session.createQuery(hql);
query.setInteger(0,1);//索引号从0开始,查询一条记录
query.setString(1,"悟空");//设置所查询记录的字段
List<Customer> list = query.list();
10.根据姓名和编号查询用户,HQL语句位于Customer.hbm.xml文件中
例: Query query = session.getNamedQuery("findCustomerByIdAndName");
query.setInteger(0,2);
query.setString(1,"八戒");
List<Customer> list = query.list();
注: 该查询需要在*.hbm.xml中设置该标签与<class>同级
<!-- query标签是用于绑定HQL语句 name属性:表示该HQL语句的名字-->
<queryname="findCustomerByIdAndName">
from Customer c where c.id=? and c.name=?
</query>
11. 连接查询姓名和订单号
例:String hql = "fromCustomer c left outer join c.orderSet o where c.id=1";
Query query = session.createQuery(hql);
List<Customer> list = query.list();
12.投影查询姓名和订单号
例:String hql = "selectc.name,o.orderNum from Customer c left outer join c.orderSet o where c.id=1";
Query query = session.createQuery(hql);
List<Customer> list = query.list();
13.获取订单的个数
例: String hql = "select count(*) from Order o";
Query query = session.createQuery(hql);
query.setMaxResults(1);
long sum = (Long) query.uniqueResult();
14.求和
例:String hql = "selectsum(o.id) from Order o";
Query query = session.createQuery(hql);
query.setMaxResults(1);
double avg = (Double) query.uniqueResult();
15.分组查询
例:按customers_id分三组
Stringsql = "select count(*) from orders group by customers_id";
String hql = "select count(*) from Order o group byo.customer.id";
Query query = session.createQuery(hql);
List<Long> orderList = query.list();
其他查询:
from Customer c where 1 < (select count(o) from c.orders o);
hibernate的检索策略
1)检索策略有二种:
>><class lazy=false/>立即检索
session.load()时,查询数据库
>><class lazy=true/>延时检索
session.load()时,不查询数据库,但对象非空,是一个hibernate产生的动态代码对象
2)类立即检索,集合延迟检索,这是项目中[推荐]使用,因为有时查询出对象后,不需要马上取得对应的集合。
例: 类延迟[lazy="true"]+集合延迟[lazy="true"]
当类延迟检索时,在需要取得属性时才查询数据库
当集合延迟检索时,在需要取得属性时才查询数据库
映射继承关系
Hibernate支持三种继承映射策略:
1.每个具体类一张表(table per concrete class) 将域模型中的每一个实体对象映射到一个独立的表中。
2.每个类分层结构一张表(table per class hierarchy) 对于继承关系中的子类使用同一个表,这就需要在数据库表中增加额外的区分子类类型的字段。
3.每个子类一张表(table per subclass) 域模型中的每个类映射到一个表,通过关系数据模型中的外键来描述表之间的继承关系。这也就相当于按照域模型的结构来建立数据库中的表,并通过外键来建立表之间的继承关系。
采用 subclass 元素的继承映射策略
1).采用subclass 的继承映射可以实现对于继承关系中父类和子类使用同一张表,因为父类和子类的实例全部保存在同一个表中,因此需要在该表内增加一列,使用该列来区分每行记录到底是哪个类的实例这个列被称为辨别者列(discriminator).
2).在这种映射策略下,使用subclass来映射子类,使用 class 或 subclass的 discriminator-value属性指定辨别者列的值
3).所有子类定义的字段都不能有非空约束。如果为那些字段添加非空约束,那么父类的实例在那些列其实并没有值,这将引起数据库完整性冲突,导致父类的实例无法保存到数据库中
1. 继承映射
第一种策略: 所有类对应一张表使用的<subclass>
例:
use hibernate;
drop tableif exists employees;
create tableif not exists employees(
emp_id int primary key auto_increment,
emp_eType archer(20),
empennage archer(20),
emp_salary double,
emp_hour double
)
//员工(父类)
public class Employee {
public Integer id;//编号
public String name;//姓名
}
//小时员工(子类)
public class HourEmployee extends Employee{
private Double rate;//时薪
}
//月薪员工(子类)
public class SalaryEmployee extends Employee{
private Double salary;//月薪
}
<!-- 配置对象/关系映射文件 Employee.hbm.xml-->
<hibernate-mappingpackage="cn.web.hibernate.type">
<classname="Employee"table="employees"discriminator-value="employee">
<idname="id" column="emp_id" type="integer">
<generatorclass="native"/>
</id>
<!-- 区分员工的字符串,只能放在id的后面-->
<discriminator column="emp_eType"type="string"/>
<propertyname="name" column="emp_name" type="string" />
<!-- 定义子类小时员工 ,name:小时员工类的类名,discriminator-value:区分小时员工的字符串 lazy=”false”:立即查询,true,延时查询-->
<subclass name="HourEmployee"discriminator-value="hour"lazy="false">
<propertyname="rate" column="emp_hour" type="double"/>
</subclass>
<!-- 定义子类月薪员工 ,name:月薪员工类的类名,discriminator-value:区分月薪员工的字符串-->
<subclassname="SalaryEmployee"discriminator-value="salary"lazy="false">
<propertyname="salary" column="emp_salary" type="double"/>
</subclass>
</class>
</hibernate-mapping>
采用 union-subclass 元素的继承映射
1.采用union-subclass 元素可以实现将每一个实体对象映射到一个独立的表中。
2.子类增加的属性可以有非空约束 --- 即父类实例的数据保存在父表中,而子类实例的数据保存在子类表中。
3.子类实例的数据仅保存在子类表中, 而在父类表中没有任何记录
4.在这种映射策略下,子类表的字段会比父类表的映射字段要多,因为子类表的字段等于父类表的字段、加子类增加属性的总和
5.在这种映射策略下,既不需要使用鉴别者列,也无须使用 key 元素来映射共有主键.
6.使用union-subclass 映射策略是不可使用 identity 的主键生成策略, 因为同一类继承层次中所有实体类都需要使用同一个主键种子, 即多个持久化实体对应的记录的主键应该是连续的. 受此影响, 也不该使用 native 主键生成策略, 因为 native 会根据数据库来选择使用 identity 或 sequence.
第二种策略:每个类映射到不同的表<union-subclass>
例: use hibernate;
drop tableif exists hourEmployees;
drop tableif exists salaryEmployees;
drop tableif exists employees;
create tableif not exists employees(
eid int primary key auto_increment,
name varchar(20)
);
create tableif not exists hourEmployees(
hid int primary key auto_increment,
name varchar(20),
rate double,
constraint hid_FK1 foreign key(hid) references employees(eid)
);
create tableif not exists salaryEmployees(
sid int primary key auto_increment,//sid即是主键又是外键
name varchar(20),
salary double,
constraint sid_FK2 foreign key(sid) references employees(eid)
);
select * from employees;
select * from hourEmployees;
select * from salaryEmployees;
//员工(父类)
public class Employee {
public Integer id;//编号
public String name;//姓名
}
//小时员工(子类)
public class HourEmployee extends Employee{
private Double rate;//时薪
}
//月薪员工(子类)
public class SalaryEmployee extends Employee{
private Double salary;//月薪
}
<!-- 配置对象/关系映射文件Employee.hbm.xml -->
<hibernate-mappingpackage="cn.web.hibernate.type1">
<classname="Employee"table="employees">
<idname="id" column="eid" type="integer">
<generatorclass="increment"/>
</id>
<propertyname="name" column="name" type="string" />
<!-- 映射SalaryEmployee子类,name:子类类名 ,table:子类映射的表名-->
<union-subclass name="SalaryEmployee"table="salaryEmployees">
<propertyname="salary" column="salary" type="double"/>
</union-subclass>
<!-- 映射HourEmployee子类,name:子类类名 ,table:子类映射的表名-->
<union-subclass name="HourEmployee"table="hourEmployees">
<propertyname="rate" column="rate" type="double"/>
</union-subclass>
</class>
</hibernate-mapping>
采用 joined-subclass 元素的继承映射
1.采用 joined-subclass 元素的继承映射可以实现每个子类一张表
2.采用这种映射策略时,父类实例保存在父类表中,子类实例由父类表和子类表共同存储。因为子类实例也是一个特殊的父类实例,因此必然也包含了父类实例的属性。于是将子类和父类共有的属性保存在父类表中,子类增加的属性,则保存在子类表中。
3.在这种映射策略下,无须使用鉴别者列,但需要为每个子类使用key 元素映射共有主键,该主键必须与父类标识属性的列名相同。但如果继承树的深度很深,可能查询一个子类实例时,需要跨越多个表,因为子类的数据一次保存在多个父类中。
4.子类增加的属性可以添加非空约束。因为子类的属性和父类的属性没有保存在同一个表中
第三种策略:每个子类各自对应一张表<joined-subclas>
例:
Sql及各类的创建如上第二种策略
<!-- 通过映射文件自动产生sql hibernate.hbm.xml-->
<propertyname="hbm2ddl.auto">create</property>
<!-- 配置对象/关系映射文件 -->
<hibernate-mappingpackage="cn.web.hibernate.type2">
<classname="Employee"table="employees">
<idname="id" column="eid" type="integer">
<generatorclass="increment"/>
</id>
<propertyname="name" column="name" type="string" />
<!-- 映射SalaryEmployee子类
name:子类类名
table:子类映射的表名-->
<joined-subclassname="SalaryEmployee"table="salaryEmployees">
<keycolumn="sid"/>
<propertyname="salary" column="salary" type="double"/>
</joined-subclass>
<!-- 映射HourEmployee子类
name:子类类名
table:子类映射的表名-->
<joined-subclassname="HourEmployee"table="hourEmployees">
<keycolumn="sid"/>
<propertyname="rate" column="rate" type="double"/>
</joined-subclass>
</class>
</hibernate-mapping>
继承映射使用的主要标签:
<property name="hbm2ddl.auto">create</property>
在运行时,总是创建新的表,如果原来有,则删除
<propertyname="hbm2ddl.auto">update</property>(推倡)
(1)如果原来无,则创建
(2)如果原有有,则插入
<propertyname="hbm2ddl.auto">none</property>
(1)真接插入数据
>>将继承结构中的所有子类都定义在各自不同的表中,主键不一至,但主键又是一个外健,可以看作(二张表)
总结:
1)每个类对应一张表:
表数目多,不便于查询,维护较差
2)所有类对应一张表:
表数目少,表中数据混乱,不符合设计原则,我们希望将相关的数据放入在不同的表中
3)每个[子类]对应一张表:
表数目中等,表中有且中包含该子类的信息,且通过外键关联其它表
1.配置C3P0连接池
在hibernate.cfg.xml文件中增加如下配置
1,在该配置文件中查找对应的c3p0资源:
hibernate-distribution-3.5.6.jar\project\etc\hibernate.properties
2,查看c3p0连接状态的日志文件:主要显示记录信息 log4j.propertie
E:\java\开发类库\hibernate-distribution-3.5.6-Final\project\etc
3,导入c3p0.jar包 ,E:\java\开发类库\c3p0-0.9.1.2\lib\c3p0-0.9.1.2.jar
<!-- C3P0连接池设定-->
<!-- 使用c3po连接池 配置连接池提供的供应商-->
<propertyname="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider/>
<!--在连接池中可用的数据库连接的最少数目 -->
<propertyname="c3p0.min_size">5</property>
<!--在连接池中所有数据库连接的最大数目 -->
<propertyname="c3p0.max_size">20</property>
<!--设定数据库连接的过期时间,以秒为单位,
如果连接池中的某个数据库连接处于空闲状态的时间超过了timeout时间,就会从连接池中清除 -->
<propertyname="c3p0.timeout">120</property>
<!--每3000秒检查所有连接池中的空闲连接 以秒为单位-->
<propertyname="c3p0.idle_test_period">3000</property>
<!-- 设置自动提交 -->
<propertyname="connection.autocommit">true</property>
2 配置隔离级别
<!-- 事务的隔离级别为Readcommitted (可以解决脏读,其它的无法解决)-->
<property name="connection.isolation">2</property>
<!-- 事务提交后,自动关闭session -->
<propertyname="transaction.auto_close_session">true</property>
隔离级别
read_uncommited 1: 允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读
read_committed 2:允许在并发事务已经提交后读取。可防止脏读,但幻读和 不可重复读仍可发生
repeatable_read4:对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,但幻读仍可能发生。(数据库默认)
serializable 8:完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。这在所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表来完成的。
3 .session管理和本地线程的绑定
管理:
Session 对象的生命周期与本地线程绑定
Session 对象的生命周期与 JTA 事务绑定
Hibernate 委托程序管理Session 对象的生命周期
线程的绑定配置:
在hibernate.cfg.xml文件中增加
<!-- 配置session的线程本地化threadLocal -->
<propertyname="current_session_context_class">thread</property>
不是调用sessionFactory.openSession().而是调用
sessionFactory. getCurrentSession().获取session对象.从当前的线程提取session, 当前线程如果存在session对象,取出直接使用.
当前线程如果不存在session对象,获取一个新的session对象和当前的线程绑定
1)默认的情况下,从sessionFactory中取得的session,每个都不相同
2)每个线程从始到终都和各自的session绑定在一起,直到线程销毁,才分离session
3)hibernate有二个级别的缓存
Hibernate中提供了两个级别的缓存
第一级别的缓存是 Session 级别的缓存,它是属于事务范围的缓存。这一级别的缓存由 hibernate 管理的,一般情况下无需进行干预,
第二级别的缓存是 SessionFactory级别的缓存,它是属于进程范围的缓存
SessionFactory 的缓存可以分为两类:
内置缓存: Hibernate自带的, 不可卸载. 通常在 Hibernate 的初始化阶段, Hibernate 会把映射元数据和预定义的 SQL 语句放到SessionFactory 的缓存中, 映射元数据是映射文件中数据的复制, 而预定义 SQL 语句时 Hibernate 根据映射元数据推到出来的. 该内置缓存是只读的. 注:程序读取的hibernate.cfg.xml文件和所有的*.hbm.xml文件都存于二级缓存中.
外置缓存(二级缓存): 一个可配置的缓存插件. 在默认情况下, SessionFactory 不会启用这个缓存插件. 外置缓存中的数据是数据库数据的复制, 外置缓存的物理介质可以是内存或硬盘,
注:ehcache外置,可以通过以配置的方式,插入到hibernate中,此时hibernate就可以使用外置二级缓存了.
Oracle简介:
2008年1月,Sun收购MySql(10亿美元);
2008年1月,Oracle收购BEA(WebLogic)85亿美元
2009年04月下旬,Oracle收购Sun(74亿美元);
常用数据库:
美国Sybase:Sybase( PowerBuilder+Sybase)
Sun:My SQL(曾经是瑞典AB公司)
美国Oracle:Oracle
微软:SQL Server及Access
IBM:DB2,Informix
分类:
根据 负载/成本/安全/应用平台
小型:access
中型:mysql/sql server/informix
大型:sybase/oracle/db2
数据库的概念: 数据库(DB)是一个按数据结构来存储和管理数据的计算机软件系统。
1. 数据库管理系统(Database Management System)
数据库管理系统(DBMS)是专门用于管理数据库的计算机系统软件。数据库管理系统能够为数据库提供数据的定义、建立、维护、查询和统计等操作功能,并完成对数据完整性、安全性进行控制的功能。
2. 数据库应用系统(Database Application System)
数据库应用系统:就是使用数据库技术的系统; 数据库应用系统有很多,基本上所有的信息系统都是数据库应用系统。它通常由软件、数据库和数据管理员组成
3. 数据库管理员(DBA)
数据管理员负责创建、监控和维护整个数据库,使数据能被任何有权使用的人有效使用。数据库管理员一般是由业务水平较高,资历较深的人员担任 。
4. 数据库系统
一个数据库系统应由计算机硬件、数据库(DB)、数据库管理系统(DBMS)、数据库应用系统(DAS)和数据库管理员(DBA)五部分构成 。
Oracle数据库的特点
支持大数据量、多用户的高性能的事务处理
Oracle遵守数据存取语言、操作系统、用户接口和网络通信协议的工业标准
实施安全性控制和完整性控制
支持分布式数据库和分布处理
具有可移植性、可兼容性和可连接性
全球化、跨平台的数据库
本机安装:
Enterprise Manager Database Control URL - (orcl) :http://PP:1158/em 数据库配置文件已经安装到 D:\oracle\product\10.2.0,同时其他选定的安装组件也已经安装到 D:\oracle\product\10.2.0\db_1。 iSQL*Plus URL 为: http://PP:5560/isqlplus iSQL*Plus DBA URL 为:http://PP:5560/isqlplus/dba
数据库的操作:
1.登录网页版的OEM http://daxia:1158/em/ --à该网页只能是管理员登陆
用户名: system 密码: oracle
2. iSQL*Plus URL 为: http://PP:5560/isqlplus
3. Oracle安装成功后的服务
OracleServiceORCL--->Orcalce服务(一个数据库实例) 可以不启动
OracleOraDb10g_home1iSQL*Plus--->运营网页版本的sqlplus的web服务器。可以不启动
OracleOraDb10g_home1TNSListener--->让其它程序连接数据库,必须取另。监听服务(必须开启)
OracleDBConsoleorcl--->网页版的OEM的web服务器及相同控制台的功能。(必须开启)
4. 连接Oracle的几种方式 :
Sqlplus : dos命令行连接数据库
Sqlplusw : 图行化界面连接数据库
oracle sql developer-oracle官方免费
Oracle客户机OEM
pl/sql developer
5、使用sqlplus连接oracle数据库
sqlplus 用户名 : scott /密码 :oracle (查看oracle提供的表)
show user //使用oracle提供的use表
desc emp; //查看表的结构
select * from emp; //查看整个表中的数据
退出:exit
连接其它服务器上的Oracle数据库:sqlplus 用户名/密码@服务器ip:端口号/数据库服务名
比如:sqlplusscott/tiger@127.0.0.1:1521/orcl
6, dos操作 :
(1)Dos窗口调整—打开dos窗口-à点击左上角按钮-à选中快速编辑模式(主要作用是在复制和粘贴) /
调整屏幕大小-à布局中设置快读和高度。
(2)忘记的解锁及密码:(次修改是scott的设置, 如果在安装时设置了用户名和密码,无需设置,直接登录即可)
操作管理员登录 sqlplus 用户名/密码 as sysdba 或者sqlplus / as sysdba
解锁用户 : alter user 用户名 accountunlock;
修改密码: alter user 用户名identified by 新密码;
3) 记录日志 : spool , spool 文件名 .操作的内容 .
操作结束使用spool off(表示将所有的操作的容写入到指定的文件中).
例如 : spool c:\ oracle.text spool off
(4)、注释 : 单行注释: --
多行注释:/* 注释的内容 */
(5)、基本命令
清屏:host cls =>host 后面是操作系统的命令。
保存sql语句:save 文件名
加载一个已有的sql文件:@ 文件名
查看用户有哪些表:
select * from tab;
(7)、环境设置-(set及show命令)
set linesize 100 //设置行间距
set pagesize 30 //设置页面的显示大小
(8)、修改sql
直接改某一行:
输行号:2
输入命令:c /form/from
打开系统的文件编辑器来改:ed。
设置某列的宽度:
column job format a15 或 简写: col job for a15
column SAL format 9999 或 col sal for 9999
格式:
col[umn] 列名 for[mat] 格式
对于字符串:
A15,15是一个数据,表示15个字符的宽度。
对于数字:
9表示一位,有几个,就是多宽。
如果实际值小于所设置的宽度,就显示原值。
如果实际值超出所设置的宽度,就会显示若干个'#'号。
0表示一位,有几个,就是多宽。
如果实际值小于所设置的宽度,前面用0补足到指定的宽度。
如果实际值超出所设置的宽度,就会显示若干个'#'号。
如果是00.00或是99.00: 均表示小数点后面有两位,如果不够,则后面补0;如果超过,则会四舍五入。
(8)、修改sql
1,如果在dos命令行中输入的语句错误,此时可以直接输入行号进行更改错误信息.
2, 直接输入ed : 该命令会弹出一个编辑框,直接值编辑框中修改,保存关闭,此时更正后的数据会自动出现在dos命令行。
9)查询当前的日期格式
select sysdate from dual; 或者 select * fromv$nls_parameters;
alter session setNLS_DATE_FORMAT='yyyy-mm-dd'; 设置日期的格式
修改当前session的一些参数(v$nls_parameters:语言,货币,日期等格式)
alter session setnls_date_format='yyyy-mm-dd';
也可以加到glogin.sql中,注意最后要有分号。
查询语句:
Select 列名 from 表名
Select :表示查询哪个列 from : 查询那张表
例: select * from emp : 查询所有
select ename,job,hiredate,sal,comm,deptno from emp; 查询表中的详细信息,在少量的查询中最好使用全名,此方法查询速度最快,
对于多表查询 ,则使用表的别名查询: select e.ename,e.sal from emp e;
例如: select ename as 姓名,job as 工作,hiredate as 日期,sal as 薪水,comm as 年薪,deptno as 雇员 from emp;
算术运算符: + - * /
例: select 1+2,3*5,8/2 fromdual;
1+2 3*5 8/2
------ ---------- ----------
3 15 4
在表达式中要注意处理null值.
1. null <> null ,比较时不相等
2.null在算法运算中返回null.
3.is null,is not null是对空值进行比较的方法
关于null值的查询
---任何值与null进行比较都不相等,null与null比较也不相等.
例; select * from emp wherecomm is null; //查询奖金不为空的的所有用户
select * from emp where comm is not null;//查询奖金为空的的所有用户
连接字符查询: 函数:select concat(“ ”,” ”) from表名
连接符mysql中的连接函数concat('hello','world')
例: select concat(ename,job)from emp;
Oracle中提供 ||代表连接符,用来连接字符串
例: selectename||job from emp;
注: 在连接查询中,如果有中文或者字符串时, 需要使用单引号将字符串引在单引号中间
日期查询:
select sysdate from dual; //24-月-12
select '今天是:'||sysdatefrom emp; //今天是:24-8月 -12
删除重复行
在 SELECT 子句中使用关键字‘DISTINCT’ 删除重复行
例: select distinct ename,deptno,job from emp; //查询去除重复的用户名
显示表结构
使用 DESCRIBE 命令,表示表结构
desc tableName(表名)
过滤和排序数据
使用WHERE 子句,将不满足条件的行过滤掉, where 语句跟在from后面
例: select * from emp where deptno=20;
字符和日期
字符和日期要包含在单引号中。
字符大小写敏感,日期格式敏感。
默认的日期格式是 DD-MON-RR
例: select * from emp where hiredate!='20-2月-81';
select * from emp where ename='SMAME';
比较运算符
大于 > , 小于 < , 大于等于>= , 小于等于<= , 不等于<> (或者!=)
例: select * from emp where sal>1500;
1.查询在一定范围内的值:
BETWEEN ...AND... 即: select * from表名 where 列名 between值 and 值或者 and
例; select * from emp where sal>1500 andsal<5000;// 查询在薪水在1500-5000的所有用户
或者; select * from emp where sal between 1500 and5000; //查询在薪水在1500-5000的所有用户
2.-between and对日期型的支持
select * from emp where hiredatebetween '01-1月-81' and'31-12月-82';
3. between对字符串的处理
select * from emp where job between 'm' and 's';
注: between and 在查询字符串和日期是必须使用单引号引起来, 该查询包含开始和结束
2. IN(set) 等于值列表中的一个或者使用 or
例: select * from emp where sal in(1500,3000); //查询薪水为1500 和3000 的用户
查询10号及20号部门的员工
select * from emp where (deptno=20 or deptno=10);
Not(Set) in不是集合中任何元素
例; select* from emp where deptno not in(10,20);
3. LIKE 模糊查询 : Select列名 from 表名 where列名 like ‘_%’
主要用字符数据.
%代表0或多个任意字符; _代表任意一个字符 ,下划线和%可以同时使用
例: select* from emp where ename like 'M%'; //查询用户名为M的用户
select * from emp where deptnolike '3%';
注: 在模糊查询中不管是数字还是字符, 需要使用单引号将所要查询的内容引起来.
转义字符:ESCAPE
回避特殊符号的:使用转义符。例如:将[%]转为[\%]、[_]转为[\_],然后再加上[ESCAPE ‘\’] 即可
例: select* from emp2 where ename like 'He\_w%' escape '\';
创建表:
createtable emp2 as select * from emp; //通过查询其他表创建一张新表
create table emp3 as select * from emp3; //循环成倍数增加复制一张表中的数据
4. 多个列排序:ORDER BY
Select 列名 from表名 order by 列名 desc; desc升序, asc :降序
按照ORDER BY 列表的顺序排序
SELECT last_name, department_id, salary(列名)
FROM employees(表名)
ORDER BY department_id, salary DESC;(升序)
例: selectename,sal from emp order by sal desc; //对用户名和薪水进行排序
select ename,sal from emp order by sal DESC,ename; // 只是针对sal薪水排序
select ename,sal from emp order by 2ASC; // 对用户薪水排序, 2表示第二列即sal
select ename,sal 工资 from emporder by 工资;
5.排序中空值的使用:
SQL> ---在排序中使用添加nullslast把含null的行放到最后去;
例: select * from emp order by comm DESC nullslast; ..将为空的用户放在最后
SQL> --nulls first,这是默认值,把null值放前面.
例: select * from emp order by comm DESC nullsfirst;
TO_CHAR 函数对日期的转换
格式: TO_CHAR(date,'format_model')
SELECT 表名TO_CHAR(hire_date, 'DD Month YYYY') AS HIREDATE FROM 表名;
注:
必须包含在单引号中而且大小写敏感。
可以包含任意的有效的日期格式。
日期之间用逗号隔开
TO_CHAR 函数中经常使用的几种格式:
数字0 , 9 $美元符 L本地货币 . 小数点 , 千分位符号
例: select ename,to_char(hiredate,'yyyy-mm-dd')from emp; //将日期格式化为指定的模式
日期的格式:
HH24:MI:SS AM
日期中添加字符:
DD "of" MONTH
SQL> select to_char(sysdate,'YYYY-MM-DD hh24:mi:ss') from dual;
To_char() 对数字的转换:
SELECT TO_CHAR(salary, '$99,999.00') SALARY
FROM employees
WHERE last_name = 'Ernst';
SQL> select ename,to_char(sal,'L9,999.99') from emp;
ENAME TO_CHAR(SAL,'L9,999
---------- -------------------
SMITH ¥800.00
ALLEN ¥1,600.00
4. 字符转换
大写:Upper() 小写: lower() 首字母大写: initcap()
例: SQL> select upper('Helloworld') 大写,lower('Hello world') 小写,initcap('Hello world') 首字母大写 from dual;
大写 小写 首字母大写
----------- ----------- ----------- ----------------------
HELLO WORLD hello world Hello World
5. 截取字符串: substr(“字符串 ”,角标位置)
concat()等于|| :连接字符串
SQL> select substr('Hello world',3) 从第三个字符截, substr('Hello world',3,5) from dual;
从第三个 SUBST
--------- -----
llo world llo w
6. length()--返回字符数
lengthb()--返回字节数
例:
select length('你好 word') L,lengthb('你好 world') B from dual;
L B
------ ----------
7 10
7. instr()查找一个字符串的,返回找到的位置,如果找不到返回0
SQL> select instr('Helloworld','l') l1,instr('Hello world','o',5) l2from dual;
L1 L2
---------- ----------
3 11
select ename from emp where instr(ename,'I')>0; // 查找eaname列中 ,字段中包含’I’的字段.
8. lpad()左填充,
rpad()右填充.
SQL> select lpad('hello',10) 填充空格,lpad('hello',10,'*') from dual;
填充空格 LPAD('HELL
---------- ----------
hello *****hello
SQL> select lpad('hello',10) 左填充空格,lpad('hello',10,'*') 左填充星号,rpad('hello',10)右填充空格 ,rpad('hello',10,'*') 右填充星号 from dual;
左填充空格 左填充星号 右填充空格 右填充星号
---------- ---------- ---------- ----------
hello *****hellohello hello*****
9. trim()去除两边的空格或者其它字符
SQL> select trim(' hello') 去除左右空格,trim('hello') 去除左右指定的字符 from dual;
去除 去除
----- -----
hello hello
SQL> select trim(' hello') 去除左右空格,trim('h' from 'hello') 去除左右指定的字符 from dual;
去除 去除
----- ----
hello ello
去除左右指定的字符:tirm(‘指定的去除字符’ from ‘字符串’)
SQL> select trim(' hello') 去除左右空格,trim('h' from'hhellohh') 去除左右指定的字符 from dual;
去除 去除
----- ----
hello ello
10. -replace替换字符串
SQL> select replace('hello world','ell','hide') from dual;
REPLACE('HEL
------------
hhideo world
10. Round():
四舍五入,截断到指定的位数,如果后面的值大于5,就+1,
TRUNC()直接截断
SQL> select round(45.926,2) 两位小数,round(45.926,1) 一位小数,round(45.926,0)零位小数,round(45.926,-1) 负一,round(45.926,-2) 负二 from dual;
两位小数 一位小数 零位小数 负一 负二
---------- ---------- ---------- ---------- ----------
45.93 45.9 46 50 0 (是因为-2值得的位数是4,小于5,所以为0)
11. 用 TO_DATE函数将字符转换成日期:
格式: TO_DATE(char[, 'format_model'])
日期数据类型的加减运算
日期可以加减数字,表示在日期的后面加减多少天
日期可以减日期,表示两个日期相关的天数
SQL> select round(to_date('7-7月 -12'),'YEAR') from dual;
SQL> select sysdate-1 昨天,sysdate 今天,sysdate+1明天 from dual;
日期的格式化:
SQL> select to_char(sysdate,'YYYY-MM-DD hh12:mi:ss DY') fromdual;
12. add_months(); 向指定日期中加上若干月数
next_day(); 指定日期的下一个日期
last_day(); 本月的最后一天
SQL> select add_months(sysdate,30)"30个月后日期",next_day(sysdate,'星期三') 下个星期三,last_day(sysdate) 本月的最后一天 from dual;
13. 四舍五入用在日期型数
在日期上加上或减去一个数字结果仍为日期。
两个日期相减返回日期之间相差的天数。
可以用数字除24来向日期中加上或减去小时
SQL> select round(to_date('10-5月 -12'),'MONTH') from dual;
ROUND(TO_DATE(
--------------
01-5月 -12
注:在四舍五入日时,如果指定了月份,则主要看日期的变化,日期大于月中旬的,则月份加1,小于则日期为1
SQL> select round(to_date('7-7月 -12'),'YEAR') from dual;
ROUND(TO_DATE(
--------------
01-1月 -13
指定年的主要看月份的变化, 月份大于7,则年份加1 ,否则
14 . Months_between(); 两个日期相差的月数
SQL> select ename,hiredate,sysdate-hiredate 工作天数,months_between(sysdate,hiredate)工作大约月数,months_between(sysdate,hiredate)/12年 from emp;
子符串与日期的连接:
SQL> select 'hell'|| to_char(sysdate) from dual;
'HELL'||TO_CHAR(SY
------------------
hell24-8月 -12
通用函数
1.NVL (expr1, expr2) 如果e1为空则输出e2,否则返回e1
NVL2 (expr1, expr2, expr3) 如果e1为空,返回e2,否则返回e3.
NULLIF (expr1, expr2) 相等返回NULL,不等返回expr1
COALESCE (expr1, expr2, ..., exprn) 返回第一个不为空的值
例: SQL> selectename,sal*12,sal*12+nvl(comm,0) 年薪 from emp; // 奖金和年薪之和不为空
2.COALESCE的特点:
COALESCE 与 NVL 相比的优点在于 COALESCE 可以同时处理交替的多个值。
如果第一个表达式为空,则返回下一个表达式,对其他的参数进行COALESCE 。
即:找第一个不为空的值。
SQL> select ename,sal*12,sal*12+coalesce(comm,null,0)年薪 from emp;
select语句中使用表达式
使用if then elseif
方式1;CASE 表达式,SQL99
方式2:decode函数,Oracle自带.
3. CASE expr WHEN comparison_expr1 THENreturn_expr1
[WHEN comparison_expr2 THEN return_expr2
WHEN comparison_exprn THEN return_exprn
ELSE else_expr]
END
DECODE(col|expression, search1, result1
[, search2, result2,...,]
[, default])
例:
SQL> ---涨工资:job为SALESMAN的涨500,为MANAGER涨800,其它涨300
SQL> ---查出员工的姓名,当前工资,及涨后工资
1 select ename 姓名,sal 月薪 ,(case job when 'SALESMAN'then 500
2 when 'MANAGER' then 800 else 300 end)每个员工涨的工资,sal+(case job when 'SALESMAN' then 500
3 when 'MANAGER' then 800 else 300 end)涨后的工资
4* from emp
SQL> /
姓名 月薪 每个员工涨的工资 涨后的工资
---------- ---------- ---------------- ---------- ---------------- ----------------
SMITH 800 300 1100
ALLEN 1600 500 2100
WARD 1250 500 1750
JONES 2975 800 3775
例: decode() 相当于if else //查询所有奖金不为空的,涨工资工资的员工
SQL> select ename,job,sal,comm,deptno,decode(job,'CLERK',500,'MANAGER',800,300)涨后的工资 from emp wherecomm is not null;
2012-8-25 day40:
分组函数: 分组函数作用于一组数据,并对一组数据返回一个值。
求总数: count() , 求平均值:avg() 求和:sum()
SQL> select count(*) 员工的总数 ,sum(sal),avg(sal)平均工资,sum(sal)/count(*)from emp;
员工的总数 SUM(SAL) 平均工资 SUM(SAL)/COUNT(*)
---------- ---------- ---------- ----------------- ----------------- -----------------
14 29025 2073.21429 2073.21429
注: 分组函数不会把带有null 值的统计计算
分组查询 grounp by
1.SQL> select deptno,sum(sal) from empgroup by deptno; //按照员工的部门进行分组查询计算总工资
2.多条件分组查询:
统计出员工部门,职位所对应的工资的总和,并排序
SQL> select deptno,job,sum(sal) from emp group by deptno,joborder by deptno;
3.在分组函数中, where 语句中不能使用别名查询, 组函数不能使用.
查询语句中,where里面的内容不能包含别名
select * from 表名 where条件 group by 分组 having 过滤分组结果
例: SQL> select deptno ,avg(sal) from empgroup by deptno having avg(sal)>2000;
SQL> select deptno,avg(sal) from emp group by deptno having deptno<>20; //部门编号不等于20的所有员工
having子句中出现的过滤条件只能是group by中的分组列及其它分组函数
例:1 查询各部门中的信息:部门编号、总人数、最大薪水、最小薪水、平均薪水、薪水总和,不显示不属于任何部门的信息。
SQL> select deptno,count(*),max(sal),min(sal),sum(sal) from empwhere deptno is not null group by deptno;
或者:
SQL> select deptno, count(*) ,max(sal) ,min(sal) ,sum(sal) fromemp group by deptnohaving deptno is not null;
例:2:--查询各部门的信息:部门编号、其中的员工数,只显示人数大于2的结果,并且按人数升序排序。
Select deptno,count(*) from emp group by deptno having count(*)>2order by count(*) desc;
DEPTNO COUNT(*)
------ ---------------- ----------
30 6
20 5
10 3
5) 使用增强查询groupby rollup(“ ”,” ”)
sqlplus表报表查询:
Break on 列名 skip 行; 例; break on deptno skip1;
SQL> selectdeptno,job,sum(sal) from emp group by rollup(deptno,job);
SQL> ---使用sqlplus提供报表功能生成结果
break on deptno skip 2;
取消设置
break on null;
结果:
DEPTNO JOB SUM(SAL)
----------------- ----------
10 CLERK 1300
MANAGER 2450
PRESIDENT 5000
8750
20 CLERK 1900
ANALYST 6000
MANAGER 2975
10875
多表查询:
Oracle 连接:
Equijoin:等值连接 , Non-equijoin:不等值连接 , Outer join:外连接 ,Self join:自连接
1.笛卡尔集合查询;
2. 使用连接在多个表中查询数据。(多表中的操作)
语法:
SELECT table1.column,table2.column
FROM table1, table2
WHERE table1.column1 =table2.column2;
在 WHERE 字句中写入连接条件。
在表中有相同列时,在列名之前加上表名前缀
例:SQL> select e.ename,e.deptno,e.sal,d.deptno,d.dname from mep4e,dept d;
如果要返回的列中包含两个表都具有列名,就需要指定列时,需要指定完整表名
等值连接: 查询员工信息,要求显示员工的编号,姓名,月薪和部门名称
例: selectempno,ename,sal,dname from emp,dept whereemp.deptno=dept.deptno;
当表名比较多时,可以给表取别名
例: SQL> select e.empno,e.ename,e.sal,e.deptno,d.dname from emp e,dept d where e.deptno=d.deptno;
非等值连接 : 查询员工的工资级别:编号 姓名 月薪和级别
例: SQL> select e.empno,e.ename,e.sal,g.grade from emp e,salgrade g
EMPNO ENAME SAL GRADE
---------- -------------------- ----------
7369 SMITH 800 1
7499 ALLEN 1600 1
例:通过不同的表查询部门名称及平均工资
SQL> selectd.dname,avg(sal) from emp,dept d group by d.dname;
2.外连接语法
外连接的符号是 (+)。
SQL> ---左外,右外,全连接
SQL> --左外(左连接)---左边的表里面不符合连接条件的数据也要出来
SQL> --右外(右连接)---右边的表里面不符合连接条件下数据也要到结果集中
SQL> --全连接---左右两边表里面不符合连接条件,均放到结果集中
左连接:
例:---查出姓名,工资,部门编号,部门名称
SQL> selectename,sal,d.deptno,d.dname from emp e, dept d where d.deptno=e.deptno(+);
右连接:
SQL> selectename,sal,d.deptno,d.dname from emp e, dept d where d.deptno(+)=e.deptno;
内连接写法:select *from emp ejoin dept one.deptno=d.deptno;
外连接,(左外连接) select列名 from 表名left join表名 on条件
使用 emp e outer left|right join dept d on....
SQL> selecte.ename,d.dname from emp e left join dept don e.deptno=d.deptno;
右外连接: select 列名 from表名 right join 表名 on 条件
SQL> selecte.ename,d.dname from emp e right join dept don e.deptno=d.deptno;
全连接: select 列名 from表名full join 表名 on 条件
SQL> selecte.ename,d.dname from emp e full join dept d one.deptno=d.deptno;
自连接:
SQL> --显示员工的姓名及他的上级的姓名
select e.empno,e.ename,e.mgr,m.ename from emp e,emp m wheree.mgr=m.empno;
层次查询
SQL> selectempno,ename,mgr from emp connect by prior empno=mgr start with mgris null;
子查询:
特点:1、子查询使用(括起来)
2、子查询位位置select from where having 放子查询。
3、不可以在主查询的group by 放子查询
4、主查询和子查询可以不是用一张表,只要子查询返回的结果主查询可以使用即可
5. 单行操作符对应单行子查询,多行操作符对应多行子查询。
单行子查询:使用的运算符:大于> , 小于 < ,大于等于>= ,小于等于 <= ,不等于!=或者 <>
例:查出比Scott工资高的员工?
SQL> ---1、先查出SCOTT的工资是多少
SQL>--select sal from emp where ename='SCOTT';
SQL> --2、select * from emp where sal>scott的工资
SQL> select* from emp where sal>(select sal from EMP where ename='SCOTT');
例;10号部门的员工姓名、工资及部门名称
SQL> select ename,sal,dname from emp e join dept d on e.deptno=d.deptno and e.deptno=10 //方法1; 使用连接查询
SQL> selectename,sal ,(select dname from dept where deptno=10)fromemp where deptno=10;
1.from 后面放子查询:
例:查询员工的编号和姓名
SQL> select* from (select ename,deptno from emp); //次查询没有意义,只是为了说明子查询的放置位置。
2. 主查询和子查询可以不是用一张表,只要子查询返回的结果主查询可以使用即可
SQL> --查询部门名称为ACCOUNTING的员工信息
SQL>1.查询名称为ACCOUNTING的部门
SQL> 2.根据部门查询员工的信息
SQL> select* from emp e where e.deptno=(select deptno from dept dwhere d.dname='ACCOUNTING');
子查询中使用子函数:
例;查询工资大于平均工资的员工
SQL> select* from emp where sal>(select avg(sal) from emp);
例:查询每一个部门的最低工资,要求最低工资大于20号部门的最低工资
Sql>select deptno,min(sal) from emp group by deptno havingmin(sal)>(select min(sal) from emp where deptno=20);
多行子查询
1.子查询返回的结果是多行数据
2.IN==等于列中的一个值
3.ANY---和子查询返回的任意一个值比较
4. ALL--和子查询返回的所有值比较
例:查询部门名称为A"CCOUTING"、SALES的员工信息
select * fromemp where deptno in (select deptno from dept where dname='ACCOUNTING' ordname='SALES');
-any用在小于,小于集合中最大值 , 用在大于,大于集合中的最小值
SQL> ---any用于大于,
例:查询工资比10号部门任意一个员工工资低的员工信息
SQL>Select *from emp where sal< any (select sal from empwhere deptno=10)
-all用在大于,大于最大的 ,all用在小于,小于最小的
QL> --all: 和子查询中所有值比较
SQL> --查询工资比10号部门所有员工工资低的员工信息
1 select * from emp where sal< all (select sal from emp where deptno=10)
使用not in时,后面的子查询返回的结果中不能有null值,否则找不到。
SQL> ----查询不是经理的员工信息
SQL> select* from emp where empno not in(select mgr fromemp where mgr is not null);
//查询各部门员工的工资大于任何一个部门的平均工资.
SQL> selecte.ename,e.sal,ae.deptno,ae.avgsal from emp e,(select deptno,avg(sal) avgsalfrom emp group by deptno) ae wheree.deptno=ae.deptno and e.sal>ae.avgsal
rownum作<=比较,不能作>比较(隐式于表中,作为表中编号的)
注意1:
在rownum之前不能加上表的别名,如 from emp e whre e.rownum<=3,这是错误的。
注意2:
rownum是在where过滤后返回符合条件的结果生成的,第一个返回的为1,第二个为2...。
但要注意,是先生成rownum,然后再进行order by操作。
注意3:
rownum是在符合条件后返回时生成的一个行号。
但如果在where中判断时用到了rownum时,会使用这个值:如果本条记录符合条件能返回,则rownum应该是多少,用这个值参与判断。如果不成立,就对下一条记录也这样做。如果写成whererownum>1,则不会有结果。
语法:SELECT [column_list], ROWNUM
FROM (SELECT [column_list]
FROM table
ORDER BY Top-N_column)
WHERE ROWNUM <= N;
SQL> select rownum,ename,job,sal,comm,deptno from emp wheresal>1500;
ROWNUM ENAME JOB SAL COMM DEPTNO
-------------------- --------- ---------- ---------- ----------
1 ALLEN SALESMAN 1600 300 30
2 JONES MANAGER 2975 20
3 BLAKE MANAGER 2850 30
TOP-IN
分页
如果不需要排序:
第一页:
select * from emp where rownum<=10;
以后页:
select * from (select rownum rn,e.* from emp e) where rn>=? and rn<=?
通用:
select * from (select rownum rn,e.* from emp e) where rn>=? and rn<=?
如果需要排序:
第一页:
select * from (select * from emporder by sal) where rownum<=10;
以后页:
select * from (select rownum rn,e.* from (select * from emp order by sal) e) where rn<=? and rn>=?;
内:负责排序
中:负责生成行号
外:负责过滤出结果
通用:
select * from (select rownum rn,e.* from (select * from emp order by sal) e) where rn<=? and rn>=?;
集合运算:
union all 并集 :返回两个集合中所有记录,包括重复元素
Union并集:返回两个集合中的去除重复元素的所有记录
INTERSECT 交集:返回同时属于两个集合的所有记录
MINUS 差集: MINUS返回属于第一个集合,但不属于第二个集合的记录
Union all用法;返回两个集合中所有记录,包括重复元素
例:查询10号部门及20号部门的员工
SQL> Select * from emp where deptno=10 unionselect * from emp where deptno=20;
union all 并集:返回两个集合中所有记录,包括重复元素
SQL> Select * from emp where deptno=10 union all select * from emp where deptno=20;
INTERSECT 交集:返回同时属于两个集合的所有记录
SQL> Selectjob from emp where deptno=10 intersect selectjob from emp where deptno=20;
例;显示薪水同时位于级别1(700~1300)和级别2(1201~1400)的员工信息。
Sql>select *from emp where sal between 700 and 1300 intersectselect* from emp where sal between 1200 and 1500
MINUS 差集: MINUS返回属于第一个集合,但不属于第二个集合的记录
SQL> Select* from emp where deptno=10 minus select * fromemp where deptno=20;
可以使用括号改变集合执行的顺序
1 select job from emp where deptno=10
2 union
3 (select job from emp where deptno=20
4 minus
5* select job from emp where deptno=30)
以上执行顺序:首先是进行差集运算,即即结果为 deptno=20的部门,然后在于deptno=10的集合求并集
如果有order by子句,必须放到最后一个查询语句后
Sql>select job from emp wheredeptno=20
union (selectjob from emp where deptno=10) order by job;
集合中的报表查询:
1 select deptno,job,sum(sal)from emp group by deptno,job
2 union
3 select deptno,to_char(null),sum(sal) from emp group by deptno
4 union
5* selectto_number(null),to_char(null),sum(sal) from emp
Break on deptnoskip 1;
取消 break on null;
查看sql语句执行时间:
Set timing on 结束: set timing off;
数据控制语言
1.DML(Data Manipulation Language – 数据操作语言) 可以在下列条件下执行:
向表中插入数据,修改现存数据 , 删除现存数据
2.事务是由完成若干项工作的DML语句组成的
INSERT 语句语法
INSERT INTO table [(column [, column...])] VALUES (value[, value...]);
特点:为每一列添加一个新值。
按列的默认顺序列出各个列的值。
在 INSERT 子句中随意列出列名和他们的值。
字符和日期型数据应包含在单引号中。
例:SQL> insert 表名(‘ename’,’deptno’)values('aaa',555);
Delete语句语法
1.DELETE [FROM] table [WHERE condition];
2.在 DELETE 中使用子查询,使删除基于另一个表 中的数据。
DELETE FROM employees
WHERE department_id =
(SELECTdepartment_id
FROM departments
WHERE department_name LIKE'%Public%');
例: delete from emp4 where sal>(select avg(sal) from emp);
注:Delete删除表中的所有数据,他是一条DML语句,支持事务。 而Truncatetable达到清空表数据的效果.他是一条DDL语句. 清空的数据不能闪回
Update语句语法
UPDATE table SET column = value [, column = value, ...] [WHERE condition];
SQL> update 表名 set 列名=值,列2=值2 where 过滤
例: update emp4 set sal=(select avg(sal) fromemp) where ename='CLARK';
复制表:
Insert into empselect * from emp; //循环成倍数的复制表中的数据
创建表:
SQL> createtable emp4 as select * from emp where deptnoin(10,20);
Sql> createtable emp2 as select distinct * from emp;
注在创建表,复制表,更新表,等只要是符合完整的语句,在该语句中可以使用任何操作
& :地址符号的使用:
例 :插入数据:SQL> insert into emp4(empno,ename,sal)values(&empno,&ename,&sal);
查询数据:SQL> select * fromemp4 where deptno=&deptno;
数据库事务
事物的提交:Commit 事物的回滚:rollback
数据库事务由以下的部分组成:
1.一个或多个DML 语句
2.一个 DDL(Data DefinitionLanguage – 数据定义语言) 语句
3.一个 DCL(Data Control Language –数据控制语言) 语句
COMMIT和ROLLBACK语句的优点
1.确保数据完整性。
2.数据改变被提交之前预览。
3.将逻辑上相关的操作分组
回滚点:
1.使用 SAVEPOINT 语句在当前事务中创建保存点。
2.使用 ROLLBACK TO SAVEPOINT 语句回滚到创建的保存点。
事物的隔离机制:
1.脏读: 对于两个事物 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段. 之后, 若 T2 回滚, T1读取的内容就是临时且无效的.
2.不可重复读: 对于两个事物 T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段. 之后, T1再次读取同一个字段, 值就不同了.
3.幻读: 对于两个事物 T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行. 之后, 如果 T1 再次读取同一个表, 就会多出几行
Oracle 中事物的隔离级别:
1. Oracle 支持的 2 种事务隔离级别:READ COMMITED, SERIALIZABLE. Oracle 默认的事务隔离级别为:READ COMMITED
2. Mysql 支持 4 中事务隔离级别. Mysql 默认的事务隔离级别为:REPEATABLE READ
相关子查询 :
特点: 1.先执行主查询,返回一行数据,2.把一行的数据放到子查询中,让子查询运行, 3.再把子查询的结果放回到主查询中进行运算,依次循环,
例1: 查找每个部门中薪水最高的雇员,显示姓和薪水。
SQL> select ename,sal from emp e wheresal=(select max(sal) from emp m where e.deptno=m.deptno);
2.查询各部门中工资比本部门平均工资高的员工的员工号, 姓名和工资。
SQL> selecte.deptno,e.ename,e.sal from emp e where sal>(select avg(sal) from emp mwhere e.deptno=m.deptno);
面试题1,第二行数据的工资等于上一行数据的工资
SQL> selectename,sal,(select sal from emp where id=e.id-1 ) from emp e;
Wm_concat(列) :将列转换成行
例:对各部门的员工进行分组,
SQL> Selectwm_concat(deptno) d from emp group by deptno;
例2: 删除表中的重复数据
第一种: 1.复制原有的表,并删除表中的数据, 2.在查询原有表中取出重复数据的数据, 3. 将去除重复的数据插入到复制的表中.
Sql> createtable copyEmp as select * from emp ;
Sql> deletefrom copyEmp;
Sql> insertinto copyEmp select distinct * from emp;
Rowid :是一个物理层在一个列,用来记录每一行数据的地址;
第二种: 使用相关子查询: 通过找rowid最大值进行排除重复元素,
Sql>select rowid,name,age fromt_student t1 where rowid in(select max(rowid) fromt_student
where name=t1.name and age=t1.age);
第三种: 直接删除表中取出重复数据的几条外
Sql> delete from t_student t1 where rowid not in(select max(rowid) fromt_student
where name=t1.name and age=t1.age)
Oracle中用户权限系统:
1. 权限的基本应用
1) 创建用户
Create user test identifiedby test
Alter user test account lock // 对用户加锁
Alter user test account unlock // 解锁
2.授权:
Grant createsession to test //将创建会话的权限付给test用户
Grant createsession, create any table to test
3.回收权限:
Revoke createsession from test
Revoke createsession,create any table from test;
4.设置用户的表空间访问配额
Alter user testquota unlimited on userts ;
注: 在oem创建用户时,会给用户添加一个connect角色,从而具有createsession的权限.
5.用户权限:
1)系统权限: grant create table to test
2)对象权限: grant select on soctt.emp;
3)权限的级联: grant create table to testwith admin option
Grant select onscott.emp to test with grant option
注: 系统权限不会级联回收.WIDTH ADMIN OPTION让用户可以把权限继续授给其它人
对象权限会级联回收。WITH GRANT OPTION来让用户把对象权限授予其它用户
6.角色: 用来定义相同功能的集合
Create rolerole //创建角色;
添加角色的权限:
Grant alteruser to role
grant createsession to role // 将创建会话的权限付给角色
grant select onscott.emp to role;
表空间(tablespace)
1.表空间:数据库的逻辑组成部分;数据库是存放在表空间中,一个表空间由一个或多个物理文件组成。
2.Oracle表空间的逻辑结构:库->表空间->段->区和块
3.表空间的作用:1、控制数据库占用的磁盘空间;2、Dba可以将不同类型的数据对象部署到不同的位置,这样有利于提高i/0性能,并有得于备份和恢复管理
建立表空间:Createtablespace
Create tablespace data01 datafile 'd:\test\data01.dbf' size 20muniform size 128k(文件的储存位置)
Uniform表示区的大小为128K。
每个文件有一定的限制,不要超过500M。
1.使用表空间:
Create table test001(id number(4),name varchar2(50)) tablespacedata01;
2、表空间及表的查询
A、查找表空间中的表
Select * from all_tables where tablespace_name='DATA01';
B、查看一个表在哪个表空间
Select tablespace_name from user_tables where table_name='EMP';
C.修改表空间中数据文件的位置
host move f:\test\*.* d:\test
Alter tablespace data01 rename datafile 'F:\TEST\DATA02.DAT' to 'D:\TEST\DATA02.DAT';
使表空间脱机:
Alter tablespace data01 offline; // 脱机
Alter tablespace data01 online; //上线
Alter tablespace data01 read only;//只读
Alter tablespace data01 read write//读写
3、表空间的扩展
A、增加数据文件
Alter tablespace data01 add datafile 'd:\test\data0102.dat' size20m;
B、把已有的数据文件内容加大
alter database datafile'd:\test\data0102.dat' resize 50m;
表的创建
Create table 表名(‘字段’ ‘类型’);
例: create table tmp (id number(5), name varchar(20));
表中插入数据:
Insert into 表名 values(‘字段’,.......);
增加一列:
alter table test add (name varchar2(20));
删除表:
Drop table 表名 例: drop table test;
Drop table testpurge //永久删除
alter table test drop column name; // 删除一列
修改表:
alter table test modify name varchar2(50);
修改变名
Rename 表名 to 新表名; ..给Oracle中的数据库对象更名均可以使用
清空表
TRUNCATE TABLE 表名
表中的数据类型:
Varchr2(size) 可变长度数据
Char(size) 定长字符数据
Number 可变数值数据
Date 日期类型
Long 可变长字符数据,最大可达到2G
CLOB 字符数据,最大可达到4G
RAW and LONG RAW 原始的二进制数据
BLOB 二进制数据,最大可达到4G
BFILE 存储外部文件的二进制数据,最大可达到4G
ROWID 行地址
表定义约束
约束是一种数据库对象,他是作用于表的数据库对象
约束主要作用是用来维护表数据的完整性,合法性。
约束类型:
NOT NULL,UNICODE,PRIMARY KEY,FORGIN KEY,CHECK
例:创建一个表使用几个约束
create table dept(deptno NUMBER(4) primarykey,name varchar2(100)not null);
constraint使用:
alter table emp add constraint FK_000004 foreign key (deptno)references dept(deptno); //给emp表增加一个外键
CHECK使用:
alter table emp add sal number(4) CHECK(sal>2000);
视图
1.视图是一种虚表.
2.视图建立在已有表的基础上, 视图赖以建立的这些表称为基表。
3.向视图提供数据内容的语句为SELECT 语句, 可以将视图理解为存储起来的 SELECT语句.
4.视图向用户提供基表数据的另一种表现形式
A. 试图就是将我们经常需要使用到的查询语句进行封装起来,一边在下次查询是不用写太多的代码.方便操作
例: 创建一个能够查到各部门比部门平均工资高的员工的工号姓名及工资的视图
create orreplace view emp_avgsal_higer as select e.empno,e.ename,e.sal,e.deptno from empe,(select deptno,avg(sal) avgsal from emp group by deptno) a where e.deptno=a.deptno ande.sal>a.avgsal;
b.试图也可以使用select语句直接查询: 例: select * from 视图名称
c. 往视图中执行dml语句
例: insert into emp20(empno,ename,job) values(8766,'dddd','ddd');
D. 创建不允许执行DML语句的视图
SQL> createor replace view emp20 as select * from emp where deptno=20 with read only;
创建序列
create sequencemysequence;
使用序列
序列的两个属性NEXTVAL(产生下一个值,返回当前值);CURRVAL(获得当前的值)
SQL> selectmysequence.nextval from dual;
清空回收站: purge recyclebin
永久性删除表: drop table 表名 purge;
数据库的导入和导出:
Exp : 导出: exp 用户名/密码导出的文件路径表名=(列名1,列名2,.....n);
例:exp scott/oracle file=f:/test/1.dmp log=f:/test/1.log tables=(emp,dept);
emp: 导入: emp 用户名/密码导入的文件路径
例: imp scott/oracle file=f:/test/1.dmp
导入到其它用户(DBA操作) :
imp system/orcl file=f:/test/1.dmp fromuser=scott touser=test//从一个用户导入到另外一个用户
闪回:flashback
1.传统方式: 备份:增量备份|全部备份,恢复的数据文件比较大,时间长了
2.oracle9àflashback
a. 对表中的数据进行一些Dml操作,而且每一次的操作都已经提交,而在一个时间段内的数据错误,需要把数据回到指定的回滚点,
b. 把数据误删除,而且还可能有另外创建一个同名的表.
C.需要查看以前的记录
D.执行很多事物,而其中的一个事物发生错误,此时需要修改出错的数据
闪回表(Flashback):
闪回表语法:
FLASHBACK TABLE[schema.]<table_name> TO
{[BEFORE DROP [RENAME TO table]] [SCN|TIMESTAMP]expr [ENABLE|DISABLE]TRIGGERS}
schema:模式名,一般为用户名。
TO TIMESTAMP:系统邮戳,包含年、月、日、时、分、秒。
TO SCN:系统更改号,
ENABLE TRIGGERS:表示触发器恢复以后为enable状态,而默认为disable状态。
TO BEFORE DROP:表示恢复到删除之前。
RENAME TO table:表示更换表名。
闪回表: 实际上是将表中的数据快速回复到过去的一个焦点或者系统的改变号scn .
Show recyclebin :显示回收站删除的内容
a.在删除表的情况下,
b.查看回收站中的内容:show show recycle;
c.查看回收站中删除的表数据 :例: select *from “BIN$VYLpw5k/QpiNpWp5a/2IIg”==$0”
d.闪回删除的表: 使用表的原始名字及回收站中的名称均可以闪回 flashbacktable 删除的表名 tobefore drop. 或者 Flashbacktable 回收站中表名称 tobefore drop rename to 表名
例: flashback table temp to to beforedrop; 或者 flashbacktable “BIN$VYLpw5k/QpiNpWp5a/2IIg”==$0” to before drop;
c.查看闪回的结果 :select * from 表名; 例: select * from temp;
闪回删除(to beforedrop[reneme to])
a.Show parameter undo //查看撤销表空间
b. 对一张表进行一系列的操作(增,删,该,查) ;
c. 查询Scn系统该编号: selectsysdate,to_char(sysdate,’yyyy-mm-dd hh24:mi:ss’) scn from dudal;
d. 启动行移动: altertable temp enable row movement;
e. Flashback table 表名 to scn scn编号; 例: Flashback table temp to scn65970;
闪回版本的查询
: 语法:select column_name[,column_name,...] from table_name
versions between [SCN|TIMESTAMP][expr|MINVALUE]
and [epxr|MAXVALUE] as of [SCN|TIMESTAMP] expr;
其中:column_name列名;table_name表名;between...and时间段;SCN系统改变号;TIMESTAMP时间戳;AS OF表示恢复单个版本;MAXVALUE最大值;MINVALUE最小值;expr指定一个值或者表达式。
a.创建一张新表 :create table temp2(vid int,name,varchar2(20));
b. 对表进行操作,
如: insert into temp2values(1,'aaa'); commit
insert into temp2values(2,'bbb'); commit;
c.desc flashback_transaction_query; //查看事物历史记录
d.查询一个表中的操作历史:
Select column(列名......n)from 表名 versionbetween scn minvalue and maxvalue;
对版本进行查询是,可以增加一个伪列查询版本操作的详细信息
例: selectvid,name,versions_xid,versions_operation from temp2 versions between scnminvalue and maxvalue;
闪回事物:(Flashacktransaction query):
a.flashback_transaction_query视图,从该视图中可以获取事务的历史操作记录以及撤销语句(UNDO_SQL)。
b.对版本进行查询是通过查询版本获得事务id,也就是versions_xid详细信息,
例: select vid,name,versions_xid,versions_operation from temp2 versionsbetween scn minvalue and maxvalue;
注: 事物查询中可以增加一个伪列查询版本操作的
c.执行闪回版本, 根据xid的值来查看undo_sql的列的值,利用UNDO_SQL撤销事务操作
select undo_sql from flashback_transaction_query wherexid='02001B007B01000002001B007B010000';
commit;
PL/SQL(Procedure Language/SQL) :
指在SQL命令语言中增加了过程处理语句(如分支、循环等),使SQL语言具有过程处理能力
1.PL/SQL的结构:
declare
说明部分 (变量说明,光标申明,例外说明 〕
begin
语句序列 (DML语句,if , for ,while〕…
exception
异常处理语句
end;
2.变量的声明:
setserveroutput on; --设置输出语句
declare
v_name varchar2(20);
v_now datedefault sysdate;
v_salnumber(4);
v_enameemp.ename%TYPE; --引用型变量
rec_empemp%ROWTYPE; --记录型变量
setserveroutput on; --设置输出语句
declare
v_namevarchar2(20);
v_now datedefault sysdate;
v_salnumber(4);
v_enameemp.ename%TYPE; --引用型变量
rec_empemp%ROWTYPE; --记录型变量
begin
--给变量赋值
v_name:='helloworld';
select avg(sal)into v_sal from emp; --通过查询emp表中的平均并付给v_sal
select enameinto v_ename from emp where empno=7369; 通过查询emp表将值付给v_ename;
dbms_output.put_line(v_name);--输出语句
dbms_output.put_line('平均工资sal='||v_sal);
select * intorec_emp from emp where empno=7521; //查询的多值付给指定的变量rec_emp
dbms_output.put_line('工号empno:'||rec_emp.empno||', 姓名:'||rec_emp.ename||', 工资:'||rec_emp.sal);
end;
begin
--给变量赋值
v_name:='helloworld';
select avg(sal)into v_sal from emp; --通过查询emp表中的平均并付给v_sal
select enameinto v_ename from emp where empno=7369;
dbms_output.put_line(v_name);--输出语句
dbms_output.put_line('平均工资sal='||v_sal);
select * intorec_emp from emp where empno=7521;
dbms_output.put_line('工号empno:'||rec_emp.empno||', 姓名:'||rec_emp.ename||', 工资:'||rec_emp.sal);
end;
IF语句:
1. If 条件 then
语句1;
语句2;.......语句n;
End if
2. If 条件 then 语句;
Elsif 条件 then语句;
Else 语句;
End if ;
3. IF 条件 THEN 语句序列1;
ESLE 语句序列 2;
END IF;
从键盘输入:accept num prompt '请输入数字:';
取得输入的值: v_num number(5) :=#
例:
setserveroutput on;
accept numprompt '请输入数字:';
declare
v_num number(5) :=#
begin
if(v_num=0) then
dbms_output.put_line('输入的是零');
elsif(v_num<0) then
dbms_output.put_line('输入的是负数');
else
dbms_output.put_line('输入的是正数');
end if;
end;
循环语句:
1. While 条件 loop 语句;
End loop;
2. loop 语句;
exit when 条件;
end loop;
3. for 变量 in 语句序列 loop 语句;
end loop;
例:
setserveroutput on;
declare
v_i number(5);
begin
v_i:=1;
for v_i in 1..5loop
dbms_output.put_line(v_i);
end loop;
end;
光标(cursor)==resultSet
语法:
CURSOR 光标名 [ (参数名 数据类型[,参数名 数据类型]...)] IS SELECT 语句;
例: cursor curs is select ename from emp;
作用: 主要用于储存查询返回的多行数据.
游标的属性:
%ISOPEN BooLEAN 打开游标则为true
%NOTFOUND Boolean 如果提取的记录没有返回值,则为true
%FOUND Boolean 一直为true,知道没有返回值
%ROWCOUNT Number 提取的总行数
打开光标执行查询: open curs
取一行光标的值: fetch curs into 引用型变量(e_job emp.job%type);
关闭光标: close curs
例: set serveroutput on;
declare
--带参数的游标
cursor c_emp isselect deptno,job,avg(sal) from emp group by deptno,job;
--引用型变量的声明
v_deptnoemp.deptno%TYPE;
v_jobemp.job%TYPE;
v_avgsalemp.sal%TYPE;
begin
--打开游标
open c_emp;
loop
--取出一行数据
fetch c_emp into v_deptno,v_job,v_avgsal;
dbms_output.put_line('工号:'||v_deptno||', 职位:'||v_job||', 薪水:'||v_avgsal);
exit when c_emp%NOTFOUND;--只到没有值为至
end loop;
end;
Oracle的异常处理
系统内部定义的异常:
NO_data_found(没有找到数据);;
Too_many_rows (select ...into 语句匹配多个行)
Zero_Divide ( 被零除)
Value_error (算术或转换错误)
Timeout_on_resource (在等待资源时发生超时)
例: set serveroutput on;
declare
v_i number(5);
v_ename varchar(20);
begin
v_i:=5/0;
select ename into v_ename from emp;
dbms_output.put_line('除数为零了');
exception
when Too_many_rows then
dbms_output.put_line('行数太多了');
when others then
dbms_output.put_line('除数为零了');
end;
自定义异常步骤:
a.在declare中定义 如: out_of exception;
b.在可行的语句begin中使用raise引用 如: raise out_of;
c.在exception中处理异常 如:; when out_of then 语句:
存储过程
语法:create [or replace] PROCEDURE 过程名(参数列表) AS PLSQL子程序体;
例如1:
create or replace PROCEDURE raisesal(v_deptno in number) ---- v_deptno innumber传入的参数
as
cursor c_emp is select * from emp;
rec_emp emp%ROWTYPE;
v_sal emp.sal%TYPE;
v_total emp.sal%TYPE:=0;
begin
open c_emp;
loop
fetch c_emp into rec_emp;
exit when c_emp%notfound;
v_sal:=rec_emp.sal*0.3;
v_total:=v_total+v_sal;
exit when v_total>3000;
dbms_output.put_line('empno:'||rec_emp.empno||', 工资:'||rec_emp.sal||', 张后:'||(v_sal+rec_emp.sal));
update emp set sal=sal+v_sal whereempno=rec_emp.empno;
end loop;
close c_emp;
end raisesal;
1.存储过程的调用: 在dos窗口使用: set serveroutput on;
exec 过程名(参数列表) 如: exec raisesal(20);
存储函数:
建立存储函数的语法:
CREATE [OR REPLACE] FUNCTION 函数名(参数列表)
RETURN 函数值类型
AS PLSQL子程序体;
1.sqldevloper中--à点击函数à新建-à给函数起一个名字-à参数设置(类型,参数名)-à确定-à自动生成一个模板
例: CREATE OR REPLACE FUNCTION MAXSAL
(
V_DEPTNO IN NUMBER
) RETURN NUMBERAS
v_maxsalemp.sal%TYPE; --声明一个引用型变量
BEGIN
select max(sal) into v_maxsal from emp wheredeptno=v_deptno; //查询语句
RETURN v_maxsal;
END MAXSAL;
2.存储函数的调用: 在dos窗口: select 函数名(参数) from dual; 如: select maxsal(20) from dual;
JAVA中存储过程的调用:
1.导入jar包; 该jar包在安装目录下:D:\oracle\10.2.0\db_1\jdbc\lib\ classes12.jar
2.注册驱动在该jar包下: oracle.jdbc.driver.OracleDriver
例: 存储过程如上例1, 通过程序调用存储过程如下:
public class OracleFunction {
private String driverClass="oracle.jdbc.driver.OracleDriver";
private String url="jdbc:oracle:thin:@localhost:1521:orcl";
private String username="scott";
private String password="oracle";
@Test
public void call(){
try {
Class.forName(driverClass);
Connection conn =DriverManager.getConnection(url,username,password);
CallableStatement cstmt = conn.prepareCall("{call raisesal(?)}");
//设置调用函数占位符?的参数
cstmt.setObject(1, 20);
int result = cstmt.executeUpdate();
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.以上代码直接运行即可调用callraisesal(参数)进行增删该查.
例2: 多个参数的存储过程的调用
1).储存过程:
CREATE OR REPLACE PROCEDURE DEPTSAL
(
V_DEPTNO IN NUMBER
, V_NAME IN OUT VARCHAR2
, V_AVGSAL IN OUT NUMBER
) AS
BEGIN
select dname into v_name from dept wheredeptno=V_DEPTNO;
select avg(sal) into v_avgsal from emp wheredeptno=V_DEPTNO;
END DEPTSAL;
2).Java程序的调用多个参数过程如下:
@Test
public void paramFunction(){
try {
Class.forName(driverClass);
Connection conn = DriverManager.getConnection(url,username,password);
CallableStatement cstmt = conn.prepareCall("{call DEPTSAL(?,?,?)}");
//设置调用函数占位符?的参数
cstmt.setObject(1, 20);
cstmt.registerOutParameter(2,OracleTypes.CHAR);
cstmt.registerOutParameter(3,OracleTypes.NUMBER);
int result = cstmt.executeUpdate();
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
JAVA中函数的调用:
例:存储函数如上例1,程序的方式调用函数如下:
@Test
public void callFunction(){
try {
Class.forName(driverClass);
Connection conn =DriverManager.getConnection(url,username,password);
CallableStatement cstmt = conn.prepareCall("{?=call maxsal(?)}");
//设置调用函数的参数
cstmt.registerOutParameter(1,OracleTypes.NUMBER);
cstmt.setObject(2, 20);//第二个参数
int result = cstmt.executeUpdate();
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
触发器:
数据库触发器是一个与表相关联的、存储的PL/SQL程序。每当一个特定的数据操作语句(Insert,update,delete)在指定的表上发出时,Oracle自动地执行触发器中定义的语句序列
分类: 语句触发器和行触发器(foreach row)
语法: CREATE [or REPLACE] TRIGGER 触发器名
{BEFORE | AFTER}
{DELETE | INSERT | UPDATE [OF 列名]}
ON 表名
[FOR EACH ROW [WHEN(条件) ] ]
PLSQL 块
触发语句: :old :new
insert 所有字段都是空(null) 将要插入的数据
Update 更新以前该行的值 更新后的值
Delete 删除以前该行的值 所有字段都是空(null)
例: 1.使用plsqldepevloper 在触发器中创建à点击触发器,右键-à新建-à填写表名,选择表--à选择需要的操作(增,删改查)àok
CREATE ORREPLACE TRIGGER UPDATESAL
BEFORE UPDATEON EMP for each row
BEGIN
dbms_output.put_line('准备更新数据');
if(:new.sal<3000) then
raise_application_error(-20000,'工资小于2000不能更新');
end if;
END;
注:
raise_application_error是关键字, -20000 到 -20999
Spring:应用于开发
Spring: 开源框架.他是即IOC(依赖注入)和AOP(面向切面技术)等分层架构的系统框架,为框架的开发而简化代码的书写.
Spring框架:
资源包:
dist--spring发布包,jar包,可以分模块存放。
lib---Spring所依赖的第三方包
samples--spring框架一些应用示例。
docs--文档目录
spring 开发过程:
1,导入相关的jar包.
A、Spring的基本jar包
commons-logging.jar---日志
org.springframework.core-3.1.1.RELEASE.jar--spring核心
org.springframework.asm-3.1.1.RELEASE.jar --
org.springframework.beans-3.1.1.RELEASE.jar--bean工厂
2.写一个业务接口
public interfaceITempSpring {
public abstract void syaHello();
}
3.定义一个实现该业务接口的类.
public class TempSpring implements ITempSpring {
private String msg;
private int time=1;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getTime() {
returntime;
}
public void setTime(int time) {
this.time = time;
}
@Override
public void syaHello(){
for(int i=0;i<time;i++){
System.out.println(msg);
}
}
}
1. 写一个测试类.
public class SpringMain {
public static void main(String[] args) {
//取得资源文件
Resource resource = new ClassPathResource("beans.xml");
//根据xml文件加载spring容器
BeanFactory beanFactory = newXmlBeanFactory(resource);
//通过spring容器获取组建
ITempSpring it = (ITempSpring)beanFactory.getBean(TempSpring.class);
it.syaHello();
}
}
xmlBeanFactory: 根据xml文件读取配置信息的加载.
2. 配置beans.xml文件:
配置*.xml文件的来源:spring-beans-3.0.xsd ,主要
配置:
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- 通过setter注入属性 class:类名,<property>表签 name:属性名,value:属性值-->
<beanclass="cn.spring.TempSpring">
<propertyname="msg" value="spring第一节"/>
<propertyname="time"value="5"/>
</bean>
</beans>
配置文件的规范形式:
<?xml version="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
.....配置相关bean信息
</beans>
配置信息可以从spring的参考手册或spring的例子中得到。配置文件的取名可以任意,文件可以存放在任何目录下,但考虑到通用性,一般放在类路径下。
E:\java\开发工具\spring\spring-framework-2.5.5\docs\reference\html_single\index.xml中
3.3.2.6. Shortcuts and other convenience options forXML-based configuration metadata获取配置信信息
IOC容器原理及依赖注入
容器的发展:
旧EJB容器时代(EJB容器)
轻容器时代(PicoContainer、Spring、Apache HiveMind、Guice)
新EJB时代EJB3.0
容器展望(中庸理论)
1、Bean概述
一切都是Bean
每一个Bean都有一个唯一的名字
2、spring容器工作的过程--Bean的生命周期(singleton类型)
A、解析资源文件(xml/properties...),把文件中相关bean的定义信息BeanDefinition。===提供合法的xml文件
B、根据BeanDefinition创建Bean的实例.
C、装配并注入Bean所依赖的对象。(规则非常多,可以是自动注入,可以按用户指定的名字,类型等来注入)---通过配置文件来注入
D、给用户提供Bean。调用容器中所提供的查询Bean的方法。
E、Spring容器停止时销毁Bean
3、BeanFactory及应用上下文ApplicationContext
A、BeanFactory,定义了Spring容器最基本的功能
ObjectgetBean(String name)--根据Bean名称(包括别名)获得Bean
<T> TgetBean(Class<T> requiredType) --根据类型获得Bean
<T> TgetBean(String name, Class<T> requiredType)--返回指定类型的Bean,注意容器中有多个该类型的确Bean时,将出现异常。
booleancontainsBean(String name);
booleanisSingleton(String name); //是单例
booleanisPrototype(String name);
String[]getAliases(String name);
使用
BeanFactoryfactory=new XmlBeanFactory(new ClassPathResource("/bean.xml"));
Person p=factory.getBean(Person.class)
System.out.println(p);
BeanFactory,当调用getBean时实例化。
B、ApplicationContext--应用上下文件--以在应用使用Spring一般用他,因为可以加载应用有关资源信息。
位于org.springframework.context-3.1.1.RELEASE.jar包
在使用ApplicationContext需要用el的jar,org.springframework.expression-3.1.1.RELEASE.jar
使用
ApplicationContextac=new ClassPathXmlApplicationContext("/bean.xml");
Person p=ac.getBean(Person.class);
System.out.println(p);
ApplicationContext在加载Bean定义信息就会实例化singleton的Bean.
4、Spring中Bean实例化的方式
A. 使用构造函数
配置文件: person.xml
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- 使用无参数的构造函数注入 -->
<beanid="person1"class="cn.itcast.spring.Person">
<propertyname="name"value="王五"></property>
<propertyname="age" value="23"></property>
</bean>
<!-- 使用构造函数注入属性 -->
<beanid="person2"class="cn.itcast.spring.Person">
<!-- <constructor-argvalue="李四"/>
<constructor-argvalue="23"/> -->
</bean>
</beans>
测试代码:
@Test//通过构造函数初始化属性
public void beanConstrouctor(){//注:ApplicationContext在加载bean时,就实例化
ApplicationContext ac = new ClassPathXmlApplicationContext("/person.xml");
Person p = (Person) ac.getBean("person2");
System.out.println(p);
}
B. 静态工厂的方法:
工厂代码:
public class PersonFactory {
public static Person createPerson()
{ //调用静态工厂方法来返回
System.out.println("静态工厂方法初始化person");
return new Person();
}
public static Person createPerson(Stringname,int age)
{ //调用静态工厂方法来返回
System.out.println("带参数静态工厂方法初始化person");
return new Person(name,age);
}
}
Person.xml文件的配置:
<!-- 使用不带参数的静态工厂方法 -->
<beanid="person3"class="cn.itcast.spring.PersonFactory"factory-method="createPerson"/>
<!-- 使用带参数的静态工厂进行属性注入 -->
<beanid="person4"class="cn.itcast.spring.PersonFactory"factory-method="createPerson">
<constructor-argvalue="张三"/>
<constructor-argvalue="22"/>
</bean>
测试代码: @Test//通过静态工厂初始化属性
public void personFactory(){//注:ApplicationContext在加载bean时,就进行bean的实例化
ApplicationContext ac = new ClassPathXmlApplicationContext("/person.xml");
Person p3 = (Person) ac.getBean("person3");
Person p4 = (Person) ac.getBean("person4");
System.out.println(p3);
System.out.println(p4);
}
C.动态工厂方法:
Person.xml文件的配置:
<!-- 使用动态工厂方法 -->
<beanid="personFactory"class="cn.itcast.spring.PersonFactoryFamily">
<propertyname="family"value="李"/>
</bean>
<beanid="person5"factory-method="createPerson"factory-bean="personFactory">
<constructor-argvalue="四"/>
<constructor-argvalue="23"/>
</bean>
动态工厂类代码:
public class PersonFactoryFamily {
private String family;
public void setFamily(String family) {
this.family = family;
}
public Person createPerson()
{ //调用静态工厂方法来返回
System.out.println("动态工厂方法初始化person");
Person p = new Person();
p.setName(family);
return p;
}
public Person createPerson(String name,int age)
{ //调用静态工厂方法来返回
System.out.println("带参数动态工厂方法初始化person");
System.out.println(name+":"+age);
return new Person(family+name,age);
}
}
测试代码: @Test//通过动态工厂初始化属性
public void personFactory(){//注:ApplicationContext在加载bean时,就实例化
ApplicationContext ac = new ClassPathXmlApplicationContext("/person.xml");
Person p5= (Person) ac.getBean("person5");
System.out.println(p5);
}
Bean的作用域
singleton(默认值)
singleton--整个容器中只创建一次这个Bean实例.
默认情况下会在容器启动时初始化bean,但我们可以指定Bean节点的lazy-init=“true”来延迟初始化bean,这时候,只有第一次获取bean会才初始化bean。如:
<bean id="xxx"class="cn.itcast.OrderServiceBean" lazy-init="true"/>
如果想对所有bean都应用延迟初始化,可以在根节点beans设置default-lazy-init=“true“,如下:
<beansdefault-lazy-init="true“ ...>
Person.xml文件的配置:
<beanid="person1" class=" cn.itcast.spring.Person">
<property name="name"value="哈哈"/>
</bean>
B、prototpye--每次找容器的时候都会创建这个Bean。
Person.xml文件的配置:
<!-- scope为prototype的Bean -->
<bean id="person2"class=" cn.itcast.spring.Person" scope="prototype">
<property name="name"value="莉莉"></property>
</bean>
C、request--在Web环境中,针对每次请求只产生一次。
Person.xml文件的配置:
<bean id="person2" class="cn.itcast.spring.Person" scope="request">
<property name="name"value="莉莉"></property>
</bean>
D. session--在Web环境中,针对一个会话(用户)产生一次。当重新打开新的浏览器则会产生一个新会话,关闭会话session销毁.
Bean的初始化及销毁方法
A、业务类
public class PersonService {
public void init() {
System.out.println("初始化bean........");
}
public void shutdown() {
System.out.println("bean的销毁.......");
}
public void addPerson() {
// 业务操作
}
}
B、配置person.xml
<!-- 指定初始化方法,也就是在Bean实例的,装配好以后,执行的方法 ,使用init-method属性-->
<!-- bean的初始化和销毁方式 ,在标签中配置使用属性:init-method, destroy-method -->
<bean id="personService"class="cn.itcast.spring.PersonService"
init-method="init"destroy-method="shutdown"/>
Bean的名称:
配置文件: person.xml
<!-- 使用id指定bean的名称 -->
<beanid="person6"class="cn.itcast.spring.Person">
<!-- 使用setter方法注入属性 -->
<propertyname="name"value="小龙女"/>
<propertyname="age" value="23"/>
</bean>
<!--给bean指定别名 -->
<aliasname="person6"alias="p6"/>
<!--使用name指定bean的名称 -->
<beanid="ps" name="/persons" class="cn.itcast.spring.Person">
<propertyname="name"value="杨过"/>
<propertyname="age" value="23"/>
</bean>
注: 在同时使用属性id和name时,需要在name属性的前面加上反斜杠” / ”, 测试类中如果通过name属性访问时,
也需要前面加上反斜杠” / ”,如果只是用单个属性,则不需要增加反斜杠” / ”
测试代码: @Test
public void beanName(){
ApplicationContext ac=new ClassPathXmlApplicationContext("/person.xml");
System.out.println(ac.getBean("person6"));
System.out.println(ac.getBean("/persons"));
System.out.println(ac.getBean("p6"));
System.out.println(ac.getBean("ps"));
}
Spring IoC容器详解
依赖注入:
支持注入类型:设值方法setter方法注入(属性注入)/构造函数(方法)注入/字段Field注入(注解)
1.setXxx()方法注入:
通过setter方法注入依赖<bean>元素的< property >子元素指明了使用它们的set方法来注入。可以注入任何东西,从基本类型到集合类,甚至是应用系统的bean。
<!--通过标签<property>注入,即setXxx方法注入属性-->
<beanid="personServices"class="cn.itcast.dao.PersonServiceImpl">
<propertyname="dao" value="dao"/>
</bean>
业务类代码:
public class PersonServiceImpl implements IpersonService{
private IpersonDao dao;
public void setDao(IpersonDao dao) {
this.dao= dao;
}
public void addPerson() {
dao.addPerson();
}
}
2.构造函数注入
配置:beans.xml
<!-- 通过标签<property>注入,即setXxx方法注入属性-->
<beanid="personDao"class="cn.itcast.dao.PersonDaoImpl">
<propertyname="tag" value="dao1"/>
</bean>
<beanid="personService"class="cn.itcast.dao.PersonServiceImpl">
<constructor-argvalue="dao"ref="personDao"/>
</bean>
业务类代码:
public class PersonServiceImpl implements IpersonService{
private IpersonDao dao;
public PersonServiceImpl(IpersonDao dao){
this.dao = dao;
}
@Override
public void addPerson() {
dao.addPerson();
}
}
3.字段的注入,即使用注解注入属性
Dao层代码:
public class PersonDaoImpl implements IpersonDao{
private String tag;
public void setTag(String tag) {
this.tag = tag;
}
@Override
public void addPerson() {
System.out.println("增加person......"+tag);
}
}
业务层代码:
public class PersonServiceImpl implements IpersonService{
@Autowired
private IpersonDao dao;
public void setDao(IpersonDao dao){
this.dao = dao;
}
public PersonServiceImpl(IpersonDao dao){
this.dao = dao;
}
@Override
public void addPerson() {
dao.addPerson();
}
}
配置文件:
<!-- 通过标签<property>注入,即setXxx方法注入属性-->
<beanid="personDao" class="cn.itcast.dao.PersonDaoImpl">
<property name="tag"value="dao1"/>
</bean>
<!-- 构造函数注入属性 -->
<beanid="personService"class="cn.itcast.dao.PersonServiceImpl">
<constructor-arg value="dao"ref="personDao"/>
</bean>
<bean id="personServices"class="cn.itcast.dao.PersonServiceImpl">
<property name="dao"value="dao"/>
</bean>
</beans>
测试代码: @Test
public void annotationAndXml(){
ApplicationContext ac = new ClassPathXmlApplicationContext("/beans.xml");
PersonServiceImpl p =(PersonServiceImpl)ac.getBean("personService");
p.addPerson();
}
装配的方式:手动装配/自动装配
自动装配中autowire属性取值如下
* byType:按类型装配,可以根据属性的类型,在容器中寻找跟该类型匹配的bean。如果发现多个,那么将会抛出异常。如果没有找到,即属性值为null。
* byName:按名称装配,可以根据属性的名称,在容器中寻找跟该属性名相同的bean,如果没有找到,即属性值为null。
* constructor与byType的方式类似,不同之处在于它应用于构造器参数。如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常。
* autodetect :首先尝试使用constructor来自动装配,然后使用byType方式。不确定性的处理与constructor方式和byType方式一致。
(1)、按名称自动装配
业务类代码:
public class PersonServiceImpl implements IpersonService{
private IpersonDao dao;
public void setDao(IpersonDao dao) {
this.dao= dao;
}
public void addPerson() {
dao.addPerson();
}
}
配置文件:
<bean id="dao"class="cn.itcast.gz.springioc.PersonDaoImpl"></bean>
<!--定义personService -->
<!--autowire属性用来指定自动装配,
EnumeratedValues :
- default
- no
- byName-按名称,按属性名称
- byType-按类型,按属性类型
- constructor-构造方法(类型)
-->
<bean id="personService"class="cn.itcast.gz.springioc.PersonServiceImpl2" autowire="byName" />
(2)、按类型自动装配---要求只能有一个要装配类型的Bean.在系统中找到依赖对象相同类型的Bean,如果找到则装配。参考(1)
(3)、按构造函数--按构造函数参数的类型进行装配。
public class PersonServiceImpl implements IpersonService{
private IpersonDao dao;
public PersonServiceImpl(IpersonDao dao){
this.dao= dao;
}
@Override
public void addPerson() {
dao.addPerson();
}
}
配置文件:
<bean id="personService"class="cn.itcast.gz.springioc.PersonServiceImpl2" autowire="constructor"/>
一.Spring IOC容器: 在属性注入时一定要提供相应的setter方法.
1.装配各种类型的属性需要在 *.xml文件中的配置
private String name;
private Integer age;
private booleanmarried;
A、简单bean配置-使用value属性或者标签来指定属性值,Spring进行自动类型转换;
配置: <beanid="person"class="cn.itcast.springioc.Person">
<!-- setter方式注入属性值 -->
<propertyname="name"value="李四"/>
<propertyname="age" value="20"/>
<propertyname="married"value="true"/>
<bean >
B、外部引用其它Bean,使用ref属性或者ref标签。
使用ref标签首找bean(直接找一个Bean,先在当前容器中找,找不到则到父容器中找),local(引用一个在当前容器中的bean),parent(引应父级容器中的
private Person parent;
配置: <property name="parent" ref="person2"/>或者将ref直接作为标签使用
<property name="parent"><refbean="person2"/></property>
<beanid="person2"class="cn.itcast.springioc.Person">
<propertyname="name"value="张三"/>
<propertyname="age" value="23"/>
</bean>
测试代码:: @Test
public void simpleType(){//通过ApplicationContext加载资源文件
ApplicationContext ac =newClassPathXmlApplicationContext("/parent.xml");
Person p = (Person) ac.getBean("person");
System.out.println(p);
}
2.内部引用其他bean
配置: <beanid="person2"class="cn.itcast.springioc.Person">
<propertyname="parent">
<beanid="person3" class="cn.itcast.springioc.Person">
<propertyname="name" value="王五"></property>
</bean>
</property>
</bean>
</bean>
测试代码::
@Test
public void mulitiConfigFiles(){
ApplicationContext ac = new ClassPathXmlApplicationContext("/parent.xml");
Person p = (Person) ac.getBean("person");
System.out.println(p);
}
C. 装配set类型的集合--使用set标签
private Set<String> edus=new HashSet<String>();
配置: <propertyname="edus">
<!-- 使用set标签来装配set类型的集合-->
<set><!-- setter注入,使用value属性-->
<value>本科学历</value>
<value>专科学历</value>
</set>
</property>
测试代码: @Test
public void collection(){
//加载资源文件并注入属性值
ApplicationContext ac = new ClassPathXmlApplicationContext("/collection.xml");
Person p = (Person) ac.getBean(Person.class);
System.out.println(p);
}
D.使用list标签配置list类型的属性
private List<Person> friends;
配置: <propertyname="friends">
<list><!--使用list标签来装配list类型的集合 -->
<beanid="person4"class="cn.itcast.springioc.Person">
<propertyname="name"value="小娟"></property>
</bean>
</list>
</property>
D.使用map标签配置map类型的属性
private Map<String,Double> scores=new HashMap<String,Double>();
配置:
<propertyname="scores">
<map><!--使用map标签来装配map类型的集合 -->
<entrykey="英语"value="85"/>
<entrykey="数学"value="90"/>
</map>
</property>
测试代码:
public void collection(){
//加载资源文件并注入属性值
ApplicationContext ac = new ClassPathXmlApplicationContext("/collection.xml");
Person p = (Person) ac.getBean(Person.class);
System.out.println(p);
}
E.使用属性文件进行配置
private Properties setting;
配置: <propertyname="setting">
<props><!--使用属性文件标签来装配 -->
<propkey="username">李四</prop>
<propkey="password">123456</prop>
</props>
</property>
2、构造函数注入, 装配的元素的类型与setter方法注入一致. 构造参数的个数必须与Bean构造函数一致。
当构造函数的个数比较复杂,参数类型及顺序有岐义,用户想要指定一个具体的构造函数,可以使用index/type/name来指定参数构造参数类型,及顺序等。
代码:
publicPerson(Integer age, String name) {}
配置:
<beanid="person"class="cn.itcast.springioc.Person">
<constructor-ragvalue="李四"index="0" type="java.lang.Interger"/>
<constructor-ragvalue="23" index="1" type="java.lang.String"/>
</bean>
构造函数代码:
publicPerson(String name, Person parent) {}
配置:
<beanid="person2"class="cn.itcast.gz.springioc.Person">
<!-- constructor-arg表示构造参数 -->
<constructor-argvalue="abc"></constructor-arg>
<!--
<constructor-arg ref="person1"></constructor-arg>
-->
<!-- 构造参数的值可以写在constructor-arg的子元素中 -->
<constructor-arg>
<refbean="person1"/>
</constructor-arg>
<propertyname="age" value="67"></property>
</bean>
测试代码: @Test
public void constroctuor(){
//加载资源文件并注入属性值
ApplicationContext ac = new ClassPathXmlApplicationContext("/constructor.xml");
Person p = (Person) ac.getBean("person");
System.out.println(p);
}
3.注解注入:
使用注解装配
在java代码中使用@Autowired或@Resource注解方式进行装配,这两个注解的区别是:@Autowired默认按类型装配,@Resource默认按名称装配,当找不到与名称匹配的bean才会按类型装配。
@Resource注解和@Autowired一样,也可以标注在字段或属性的setter方法上.
@Resource注解默认按名称装配。
名称可以通过@Resource的name属性指定,如果没有指定name属性,
当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象
当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。
注意:如果没有指定name属性,并且按照默认的名称找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。
Dao层代码:
@Repository("personDao")//给bean指定名称
@Scope("prototype")
public class PersonDaoImpl2 implements IpersonDao{
private String tag;
public void setTag(String tag) {
this.tag = tag;
}
@Override
public void addPerson() {
System.out.println("增加person......"+tag);
}
}
业务类代码:(要注入的依赖对象需要在Field上加@Autowired等标签)
@Autowired注解是按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。@Autowired(required=false)
@Service("personService")//给bean指定名称
public class PersonServiceImpl implements IpersonService{
@Autowired
private IpersonDao dao;//用于字段上
@Autowired //用在属性的set方法上
public void setDao(IpersonDao dao) {
this.dao = dao;
}
@Override
public void addPerson() {
dao.addPerson();
}
}
配置文件 beans.xml
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
<!—此处引入context命名空间主要是在操作时标签能显示提示信息-->
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- 表示当前容器需要使用注解来进行装配 -->
<context:annotation-config/>
<beanid="personDao"class="cn.itcast.dao.PersonDaoImpl"></bean>
<!-- 通过property元素来指定设值方法注入 -->
<beanid="personService"class="cn.itcast.dao.PersonServiceImpl"></bean>
或者使用以下方式:,让Spring容器去扫描一些包
<beanid="personDao"class="cn.itcast.dao.PersonDaoImpl"></bean>
<!-- 通过property元素来指定设值方法注入 -->
<beanid="personService"class="cn.itcast.dao.PersonServiceImpl"></bean>
<!--指定让Spring容器去扫描一些包,多个包 -->
<context:component-scanbase-package="cn.itcast"/>
通过在classpath自动扫描方式把组件纳入spring容器中管理
1. 如果要用注解装配,则需要引入context命名空间),并且在配置文件中加上<context:annotation-config/>。
2. 在配置文件中添加context:component-scan标签<context:component-scanbase-package="cn.itcast"/>
其中base-package为需要扫描的包(含子包)。
3.如果需要标签提示信息: 可在编辑器中(eclipse) 增加spring-context-3.0.xsd进行规范,
打开window-àpreferences(参数配置)-à搜索xml-à选择xmlcatalog-àuser specified entries-à
AddàfileSystem-à找到spring-context-3.0.xsd-àkey type:选择urlàkey:填写http://www.springframework.org/schema/context/spring-context-3.0.xsd(spring参考手册中查找)--->ok,此时就可以使用标签提示信息了.
注:
1、在使用组件扫描元素时,AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor会隐式地被包括进来。 也就是说,连个组件都会被自动检测并织入 - 所有这一切都不需要在XML中提供任何bean配置元数据。
2、功能介绍
@Service用于标注业务层组件、
@Controller用于标注控制层组件(如struts中的action)、
@Repository用于标注数据访问组件,即DAO组件。
而@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
二、Spring IOC扩充
A、spring除了简单类型int...Spring还能帮助把value里面字符串直接转换成特定的对象。比如URL类的属性,可以使用value="http://www.itca..."来注入。
B、如果是通过value来注入自定义类型的对象,则要求bean中必须提供一个空参数的构造函数。
Bean代码:
public class Person {
private String name;
private Integer age;
private Person parent;
private Date bornDate;
private URL url;
public Person() {
super();
System.out.println("构造Person()..");
}
//必须提供setXxx(), getXxx()方法
}
配置文件: bean.xml
<beanid="person1"class="cn.itcast.gz.springioc.Person">
<propertyname="name"value="李四"></property>
<propertyname="age" value="100"></property>
<property name="url"value="http://www.itcast.cn">
</property>
<propertyname="parent"value="李四"></property>
</bean>
测试代码: @Test
public void simpleType() {
ApplicationContext ac = new ClassPathXmlApplicationContext("/bean.xml");
System.out.println(ac.getBean("person1"));
}
C. 复杂的类型转换
Spring的类型转换均是通过javaBean中的PropertyEditorSupport类来实现,我们可以自己扩展这个类。并注使用CustomEditorConfigurer类注册到Spring容器容器
自定义转换器步骤:
A. 写一个类继承PropertyEditorSupport自定义的编辑器,重写setAsText()等方法,在该该方法中实现需要的操作.
B.使用org.springframework.beans.factory.config.CustomEditorConfigurer类型的Bean来加载我们自定义编辑器
3.0中只要有一个带字符串的构造函数,他会自动调用Bean带字符串的构造函数自动转换。
1.自定义一个类继承PropertyEditorSupport类,.
//自定义一个属性转换器
public classMyDatePropertyEditorSupport extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
Date d;
try {
d = sdf.parse(text);
super.setValue(d);
} catch (ParseException e) {
e.printStackTrace();
};
}
}
2.在配置文件中配置,通过CustomEditorConfigurer类注入到spring容器中.
配置文件:bean.xml
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<beanid="person1"class="cn.itcast.gz.springioc.Person">
<beanid="person1"class="cn.itcast.gz.springioc.Person">
<propertyname="name"value="李四"></property>
<propertyname="age" value="100"></property>
<property name="url"value="http://www.itcast.cn">
</property>
<propertyname="parent"value="李四"></property>
<!—日期格式的字符串转换-->
<property name="bornDate"value="2012-01-03"/>
</bean>
<beanclass="org.springframework.beans.factory.config.CustomEditorConfigurer">
<propertyname="customEditors">
<map>
<entrykey="java.util.Date"value="cn.itcast.gz.springioc.MyDatePropertyEditorSupport"></entry>
</map>
</property>
</bean>
</beans>
测试代码:
@Test
public void simpleType() {
ApplicationContext ac = new ClassPathXmlApplicationContext("/bean.xml");
System.out.println(ac.getBean("person1"));
// PropertyEditorSupport pe;
// CustomEditorConfigurer b;
// b.setCustomEditors(customEditors)//点击该类,按f4查看该类的属性和方法
}
2、工厂bean-FactoryBean接口:
是用来生产具体产品的特殊的Bean。一个Bean要是实现了FactoryBean接口。那么在容器中的Bean的实例就是由FactoryBean里面的getObject()方法所返回的对象。
public interface FactoryBean{
public Object getObject();
public Class getObjectType() ;
public booleanisSingleton();
}
1.实现FactoryBean接口:
public class PersonFactoryBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
System.out.println("创建getObject()...");
return new Person("dddd");//产生一个person实例
}
@Override
public Class getObjectType() {
return null;
}
@Override
public booleanisSingleton() {
return true;
}
}
配置文件:factorybean.xml
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!—通过factorybean产生person对象-->
<beanid="person1"class="cn.itcast.gz.springioc.PersonFactoryBean">
</bean>
</beans>
测试代码: @Test
public void factoryBean() {
ApplicationContext ac = new ClassPathXmlApplicationContext("/factorybean.xml");
System.out.println(ac.getBean("person1"));
System.out.println(ac.getBean("person1"));
}
3、容器扩展点应用-
Spring提供了很多容器扩展点供我们使用,我们通过这些扩展点来扩充Spring的功能。用法是让我们写一个Bean去实现Spring所提供的扩展点的接口,获得容器相关的一些资源,从而达到扩充容器的功能。
A、BeanPostProcessor
B、BeanFactoryPostProcessor--在容器初始化,在Bean创建之前使用,会调用扩展点中的postProcessBeanFactory方法,可以在该方法中加入对Bean定义信息的处理,比如过滤或替换等。
代码:
public class MyBeanFactoryPostProcessorimplements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("配置文件加载完成....");
//从容器中读出Bean配置信息
for(StringbeanName:beanFactory.getBeanDefinitionNames()){
BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
System.out.println(beanName+":"+bd.getClass());
if(bd instanceof GenericBeanDefinition){
GenericBeanDefinition bd0=(GenericBeanDefinition)bd;
MutablePropertyValues mvp= bd0.getPropertyValues();
for(PropertyValue mv:mvp.getPropertyValues()){
Objectov= mv.getValue();
if(ov instanceof TypedStringValue){
//替换配置文件中的value属性值
( (TypedStringValue)ov).setValue(( (TypedStringValue)ov).getValue().replaceAll("你好","XXX"));
}
}
}
}
}}
配置文件:
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<beanid="person1"class="cn.itcast.gz.springioc.Person">
<propertyname="name"value="你好"></property>
</bean>
<beanclass="cn.itcast.gz.springioc.MyBeanProcesser"></bean>
<beanclass="cn.itcast.gz.springioc.MyBeanFactoryPostProcessor"></bean>
</beans>
测试代码: @Test
public void containerextend() {
ApplicationContext ac = new ClassPathXmlApplicationContext("/containerextend.xml");
System.out.println(ac.getBean("person1"));
}
4、属性文件的配置:
属性文件db.properties
username=root
password=abc;
配置文件 : properties.xml
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<beanid="person1"class="cn.itcast.gz.springioc.Person">
<propertyname="name"value="${username}"></property>
<propertyname="name"value="${password}"></property>
</bean>
<beanclass="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<propertyname="location"value="db.properties"></property>
</bean>
</beans>
//自定义属性
@Test
public void customproperties() {
ApplicationContextac = new ClassPathXmlApplicationContext("/properties.xml");
System.out.println(ac.getBean("person1"));
//org.springframework.beans.factory.config.PropertyPlaceholderConfigurerpp;
//pp.setLocation(location)
}
5、简化属性配置:
配置文件;namespace.xml
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- 写法1使用set方法注入属性-->
<beanid="person1"class="cn.itcast.gz.springioc.Person">
<propertyname="name"value="杨过"></property>
<propertyname="age" value="100"></property>
<propertyname="parent"ref="person3"></property>
</bean>
<!-- 使用p命名空间以后 -->
<beanid="person2" class="cn.itcast.gz.springioc.Person"
p:name="小龙女" p:age="20"p:parent-ref="person3">
</bean>
<!-- 使用构造函数注入属性 -->
<beanid="person3"class="cn.itcast.gz.springioc.Person" >
<constructor-argvalue="杨过"></constructor-arg>
<constructor-argvalue="20"></constructor-arg>
</bean>
<!-- 使用c命名空间后 -->
<beanid="person4" class="cn.itcast.gz.springioc.Person" c:name="小龙女"
c:age="22"/>
</beans>
注: 在使用简化属性文件配置时,需要引入约束文件.
测试代码:
@Test
public void pAndCNamesapce() {
ApplicationContext ac = newClassPathXmlApplicationContext("/namespace.xml");
System.out.println(ac.getBean("person1"));
System.out.println(ac.getBean("person2"));
System.out.println(ac.getBean("person3"));
System.out.println(ac.getBean("person4"));
}
三、代理模式-
1、使用JDK动态代理来创建代理对象
//实现类
public class HelloImpl implements IHello {
@Override
public void sayHello() {
System.out.println("大家好!当前时间:"+new Date());
}
}
//代理对象
public class JDKProxy implements InvocationHandler {
private Object realObject;
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//加入代理自己的代码,日志
System.out.println(method.getName()+"方法调用,时间:"+new Date());
Object obj = method.invoke(realObject, args);
return obj;
}
//创建一个真实对象的代理对象
public Object createProxyObject(Object realObject){
this.realObject =realObject;
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
realObject.getClass().getInterfaces(), this);
}
public static void main(String[] args) {
//创建一个HelloImpl的代理对象
IHello hello=(IHello) new JDKProxy().createProxyObject(new HelloImpl());
//所有对代理对象上的调用,都会发给InvocationHandler对象来执行。
hello.sayHello();
}
注: Jdk中的代理只能是代理接口,而不能代理类
2、使用CGlib框架来创建针对类的代理对象
public class CglibTest {
public static void main(String[] args) {
//如果一个对象没有实现任何接口,要创建其代理对象,则必须依赖第三方框架,比如cglib
final HelloImpl2 realObject=new HelloImpl2();
/*HelloImpl2hello=(HelloImpl2)Enhancer.create(HelloImpl2.class, new InvocationHandler(){
@Override
public Object invoke(Object arg0, Methodarg1, Object[] arg2)
throwsThrowable {
return arg1.invoke(realObject, arg2);
}
});*/
Enhancer en=new Enhancer();
en.setSuperclass(HelloImpl2.class);
en.setCallback(new InvocationHandler(){
@Override
public Object invoke(Object arg0, Method arg1,Object[] arg2)
throws Throwable {
return arg1.invoke(realObject, arg2);
}
});
HelloImpl2 hello=(HelloImpl2)en.create();
hello.sayHello();
//session.load(Person.class,1L);
}
}
3、使用AOP的解决方案
接口:
public interfaceIPersonService {
void addPerson(Person p);
void deletePerson(Long id);
void updatePerson(Long id, Person p);
}
业务代码:
public class PersonServiceImpl implements IPersonService {
@Override
public void addPerson(Person p) {
System.out.println("addPerson()...业务逻辑省略...");
//...
}
@Override
public void deletePerson(Long id) {
System.out.println("deletePerson()...业务逻辑省略...");
//...
}
@Override
public void updatePerson(Long id,Person p) {
System.out.println("updatePerson()...业务逻辑省略...");
///..
}
}
//写日志的切面,专门解决写日志的问题
public class LogAspect {
public void writeBeforeLog(Method m){
//切面2:日志的功能
System.out.println(UserContext.getUser()+"调用"+m.getName()+"方法,时间:"
+new Date());
}
public void writeAfterLog(Method m){
//切面3:审计的功能
System.out.println(UserContext.getUser()+"成功调用"+m.getName()+"方法,时间:"+new Date());
}
}
//验证权限的切面
public class SecurityAspect {
public void checkPermission(Method m){
// 切面1:加入检查权限的代码
System.out.println("权限检查");
if (!"admin".equals(UserContext.getUser())) {
throw new RuntimeException("没有权限");
}
}
}
//用户
public class UserContext {
public static String getUser() {
return "admin";
}
}
容器中实现aop代码:
public classContainerImpl implements Container {
private Map<String, Object> beans =new HashMap<String, Object>();
public ContainerImpl() {
init();
aop();
}
//初始化
public voidinit() {
// 去一个指定的配置文件加载关于要放置到容器中的类的相关信息
// *.xml,properties
Properties p = new Properties();
try {
p.load(this.getClass().getResourceAsStream("/bean.properties"));
// 初始化Bean,属性名称不能包含.
for (Object o : p.keySet()) {
String beanName =o.toString();
// zwj
if(beanName.indexOf('.') < 0) {
StringclzName = p.getProperty(beanName);
// 初始化
Object bean =Class.forName(clzName).newInstance();
beans.put(beanName,bean);
}
}
} catch (Exception e) {
e.printStackTrace();
}
injectByField();
}
/**
* 依赖注入,采用setter设值方法注入
*/
public void injectByField() {
Properties p = new Properties();
try {
p.load(this.getClass().getResourceAsStream("/bean.properties"));
// 初始化Bean,属性名称不能包含.
for (Object o : p.keySet()){
String key =o.toString();
//zwj.name
if (key.indexOf('.')> 0) {
StringbeanName=key.substring(0,key.indexOf('.'));
String pname=key.substring(key.indexOf('.')+1);
//属性的值
Stringvalue=p.getProperty(key);
Objectbean=getBean(beanName);
//查找到bean的pname属性
//System.out.println(beanName+":"+pname);
Field field=bean.getClass().getDeclaredField(pname);
field.setAccessible(true);
//调用属性的setter方法来注入内容
if(field.getType()==String.class){
field.set(bean,value);
}
else {
//就是Bean
field.set(bean,getBean(value));
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void inject() {
Properties p = new Properties();
try {
p.load(this.getClass().getResourceAsStream("/bean.properties"));
// 初始化Bean,属性名称不能包含.
for (Object o : p.keySet()){
String key =o.toString();
//zwj.name
if (key.indexOf('.')> 0) {
StringbeanName=key.substring(0,key.indexOf('.'));
Stringpname=key.substring(key.indexOf('.')+1);
//属性的值
Stringvalue=p.getProperty(key);
Objectbean=getBean(beanName);
//查找到bean的pname属性
//System.out.println(beanName+":"+pname);
PropertyDescriptor[]pds=java.beans.Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();
for(PropertyDescriptorpd:pds){
if(pd.getName().equals(pname)){
//调用属性的setter方法来注入内容
if(pd.getPropertyType()==String.class){
pd.getWriteMethod().invoke(bean,value);
}
else{
//就是Bean
pd.getWriteMethod().invoke(bean,getBean(value));
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
//实现Aop
public void aop() {
Properties p = new Properties();
try {
p.load(this.getClass().getResourceAsStream("/aop.properties"));
// 初始化Bean,属性名称不能包含.
for (Object o : p.keySet()){
//取得属性文件中的键personService.before
final String key =o.toString();
//personService.before=logAspect.writeBeforeLog,securityAspect.checkPermission
StringbeanName=key.substring(0,key.indexOf('.'));
//在某一个JavaBean的前方法执行之前,执行一系列的方法
String[]ps=p.getProperty(key).split(",");
//logAspect.writeBeforeLog,securityAspect.checkPermission
for(Stringvs:ps){
finalObject bean=getBean(beanName);
StringaspectBeanName=vs.substring(0,vs.indexOf('.'));
StringaspectMethodName=vs.substring(vs.indexOf('.')+1);
finalObject aspectBean=getBean(aspectBeanName);
//找到aspectBean的aspectMethodName
finalMethod aspectMethod=aspectBean.getClass().getMethod(aspectMethodName,Method.class);
//创建一个bean的代理对象,在执行他们的方法之前,先执行aspectBean的aspectMethodName方法
if(bean.getClass().getInterfaces().length>0){
//System.out.println("接口代理...");
//实现了接口,创建代理对象就使用JDK动态代理即可
ObjectproxyBean=Proxy.newProxyInstance(this.getClass().getClassLoader(),bean.getClass().getInterfaces(), new InvocationHandler() {
@Override
publicObject invoke(Object proxy, Method method, Object[] args)
throwsThrowable {
if(key.indexOf(".before")>0){
//调用真实对象的业务方法之前,先调用aspectBean的aspectMethodName
aspectMethod.invoke(aspectBean,method);
}
//调用业务方法
Objectret=method.invoke(bean, args);
if(key.indexOf(".after")>0){
//调用真实对象的业务方法之前,先调用aspectBean的aspectMethodName
aspectMethod.invoke(aspectBean,method);
}
returnret;
}
});
//把代理对象放回容器
beans.put(beanName,proxyBean);
//System.out.println(beanName+":"+proxyBean.getClass());
}
}
}
}
catch(Exception e){
e.printStackTrace();
}
}
@Override
public Object getBean(String name) {
return beans.get(name);
}
@Override
public <T> T getBean(Class<T>type) {
for(Object o:beans.values()){
//isAssignableFrom()表示参数中的类是否是当前的类的子类
if(o.getClass().isAssignableFrom(type)){
return (T)o;
}
}
return null;
}
}
Aop属性文件配置:aop.properties
personService.before=securityAspect.checkPermission,logAspect.writeBeforeLog
personService.after=logAspect.writeAfterLog
bean属性文件配置: bean.properties
logAspect=cn.itcast.gz.ioc.serivce.aop.LogAspect
securityAspect=cn.itcast.gz.ioc.serivce.aop.SecurityAspect
personService=cn.itcast.gz.ioc.serivce.aop.PersonServiceImpl
客户端代码:
public static void main(String[] args) {
Container c=new ContainerImpl();
Boy boy=c.getBean(Boy.class);
boy.kiss();
}
三、Spring中的数据库支持
访问数据(jdbc/dbutils/ibatis/jdo/hibernate/jpa/ejb)===jdbc/ibatis/hibernate/jpa
1,Spring+JDBC组合开发 ,
A、利用JdbcTemplate的类来写Dao的实现
各类的路径:
JdbcTemplate类路径:org.springframework.jdbc-3.1.1.RELEASE.jar\core\JdbcTemplate
数据BasicDataSource类: commons-dbcp.jar\dbcp\BasicDataSource ,主要注入url.驱动连接driver,密码,用户名.
Dao层代码:
public class PersonDaoImpl implements IPersonDao {
private JdbcTemplatejdbcTemplate;//通过set方式注入属性
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate =jdbcTemplate;
}
@Override//增加用户信息
public void savePerson(Person p) {
jdbcTemplate.update("insert into person(age,name)values(?,?)",p.getAge(),p.getName());
}
@Override//删除用户
public void deletePerson(Long id) {
jdbcTemplate.update("delete from person where id=?", id);
}
@Override//获取用户信息
public Person getPerson(Long id) {
returnjdbcTemplate.queryForObject("select * from person where id="+id,new PersonRowMapper());
}
@Override//取得所有用户信息
public List<Person> listAllPerson(Long id) {
return (List<Person>)jdbcTemplate.queryForObject("select * from person ",new PersonRowMapper());
//return jdbcTemplate.query("select *from person ", new PersonRowMapper());
}
}
class PersonRowMapperimplements RowMapper<Person>{
@Override//RowMapper将行数据转换成person映射类
public Person mapRow(ResultSet rs,int rowNum) throws SQLException {
Person p = new Person();//从ResultSet中读中出数据,并设置到person对象中
p.setId(rs.getInt("id"));
p.setAge(rs.getInt("age"));
p.setName(rs.getString("name"));
return p;
}
}
配置文件: beans-jdbctemplate.xml
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd ">
<!-- 配置数据源,主要有驱动driver,url,密码,用户名,此属性通过dataSource
的子类BasicDataSource提供的set方法注入,该类在commons-dbcp.jar\dbcp\BasicDataSource-->
<beanid="dataSource"class="org.apache.commons.dbcp.BasicDataSource">
<propertyname="driverClassName"value="org.gjt.mm.mysql.Driver"/>
<propertyname="url"value="jdbc:mysql://localhost:3306/mydb2"/>
<propertyname="username"value="root"/>
<propertyname="password"value="root"/>
</bean>
<!-- 配置jdbcTemplate ,数据源的dataSource的注入,是通过JdbcTemplate这个
类的构造函数进行属性的注入,该类在org.springframework.jdbc-3.1.1.RELEASE.jar包中-->
<beanid="jdbcTemplate"class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-argref="dataSource"/>
</bean>
<!-- 配置dao层 ,属性name="jdbcTemp"的注入方式:通过提供的set方法注入-->
<beanid="personDao"class="cn.jdbc.spring.jdbctemp.dao.PersonDaoImpl">
<propertyname="jdbcTemplate"ref="jdbcTemplate"/>
</bean>
</beans>
测试类代码;
public class JdbcTemp {
private IPersonDaodao;
@Before
// 在加载配置文件前初始化perosondao
public void init() {
ApplicationContext ac = new ClassPathXmlApplicationContext("/jdbc-beans.xml");
dao = (IPersonDao) ac.getBean("personDao");
}
@Test
public void addPerson() {
Person p = dao.getPerson(1L);
System.out.println(p.getName() +":" + p.getAge());
}
}
B、使用JdbcDaoSupport类来实现Dao,该类中提供了JdbcTemplate类,在使用过程中只是需要继承该类即可.
1.写一个类继承JdbcDaoSupport
//继承JdbcDaoSupport,里面提供了getJdbcTemplate()方法
public class PersonDaoImpl2 extends JdbcDaoSupportimplements IPersonDao {
@Override
public void save(Person p) {
getJdbcTemplate().update("insert into Person(name,age)values(?,?)",p.getName(),p.getAge());
}
@Override
public void delete(Long id) {
getJdbcTemplate().update("delete from Person where id=?", id);
}
@Override
public Person get(Long id) {
return getJdbcTemplate().queryForObject("select * from Person where id="+id,new PersonRowMapper());
}
@Override
public List<Person> loadAll() {
return getJdbcTemplate().query("select* from Person",new PersonRowMapper());
}
private class PersonRowMapper implements RowMapper<Person>{
@Override
public Person mapRow(ResultSet rs,int rowNum) throws SQLException {
//从ResultSet中读中出数据,并设置到person对象中
Person p=new Person();
p.setId(rs.getLong("id"));
p.setName(rs.getString("name"));
p.setAge(rs.getInt("age"));
return p;
}
}
}
属性文件配置: db.properties
driveClass=org.gjt.mm.mysql.Driver
url=jdbc:mysql://localhost:3306/mydb2
username=root
password=admin
文件配置1. jdbc-properties.xml
<?xml version="1.0" encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- 配置数据源,注入属性 -->
<beanid="dataSource"class="org.apache.commons.dbcp.BasicDataSource">
<propertyname="driverClassName"value="${driveClass}"></property>
<propertyname="url"value="${url}"></property>
<propertyname="username"value="${username}"></property>
<propertyname="password"value="${password}"></property>
</bean>
<!-- 加载属性文件 -->
<beanclass="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<propertyname="location"value="db.properties"></property>
</bean>
<!-- 配置Dao实现,DAo实现继承了JdbcDaoSupport,通过该类加载数据源-->
<beanid="personDao"class="cn.itcast.gz.jdbc.dao.impl.PersonDaoImpl2">
<propertyname="dataSource"ref="dataSource"></property>
</bean>
</beans>
配置文件2:简化方式
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
">
<!-- 配置数据源 -->
<beanid="dataSource"class="org.apache.commons.dbcp.BasicDataSource"
p:driverClassName="${driveClass}"p:url="${url}"p:username="${username}"
p:password="${password}"></bean>
<!-- 使用 context:property-placeholder来加载属性文件 -->
<context:property-placeholderlocation="db.properties"/>
<!-- 配置Dao实现,DAo实现继承了JdbcDaoSupport,通过该类加载数据源-->
<beanid="personDao"class="cn.itcast.gz.jdbc.dao.impl.PersonDaoImpl2">
<propertyname="dataSource"ref="dataSource"></property>
</bean>
</beans>
测试代码:
public class JdbcTemp {
private IPersonDaodao;
@Before//在加载配置文件前初始化perosondao
public void init() {
ApplicationContext ac = new ClassPathXmlApplicationContext("/jdbc-properties.xml");
dao = (IPersonDao) ac.getBean("personDao");
}
@Test
public void addPerson() {
Person p = dao.getPerson(1L);
System.out.println(p.getName() +":" + p.getAge());
}
}
详解AOP开发步骤:
1. 导入相关的jar包:
aopalliance.jar
aspectjrt.jar
aspectjweaver.jar
cglib-nodep-2.1_3.jar
commons-logging.jar
org.springframework.aop-3.1.1.RELEASE.jar
org.springframework.asm-3.1.1.RELEASE.jar
org.springframework.beans-3.1.1.RELEASE.jar
org.springframework.context-3.1.1.RELEASE.jar
org.springframework.core-3.1.1.RELEASE.jar
org.springframework.expression-3.1.1.RELEASE.jar
注:以上jar包在myeclips开发环境中的javaEE5,6都支持.
2. 写业务类
1) 例如:一个类中有多个操作的大体相同的方法,此时可以抽取出来,形成相对独立的方法,方便类之间的调用,此时使用AOP切面技术进行解决.
//写日志的切面,专门解决写日志的问题
public class LogAspect {
public void writeBeforeLog(){//切面2:日志的功能
System.out.println(UserContext.getUser()+"调用方法,时间:"+new Date());
}
public void writeAfterLog(){//切面3:审计的功能
System.out.println(UserContext.getUser()+"成功调用方法,时间:"+new Date());
}
public void writeException(Exception e){//切面3:异常记录
System.out.println("出现异常:"+e.getMessage()+",时间:"+new Date());
}
public void writeFinishLog(){//切面3:审计的功能
System.out.println(UserContext.getUser()+"完成方法调用,时间:"+new Date());
}
}
//增删该查等操作
public class PersonServiceImpl implements IPersonService {
private Stringname;
public void addPerson(Person p) {
System.out.println("addPerson()......");
//...
///..
}
public void deletePerson(Long id) {
if(id<0)thrownew RuntimeException("ID小于0");
System.out.println("deletePerson()......");
//...
///..
}
public void updatePerson(Long id, Person p) {
System.out.println("updatePerson().....");
//...
///..
}
//检查用户权限
public class SecurityAspect {
public void checkPermission() {
// 切面1:加入检查权限的代码
System.out.println("权限检查");
if (!"admin".equals(UserContext.getUser())) {
throw new RuntimeException("没有权限");
}
}
public Object checkSecurity(ProceedingJoinPointjoinPoint)throws Throwable {
Object ret;
StringmethodName=joinPoint.getSignature().getName();
System.out.println("准备执行方法:"+methodName);
try {
if(!"admin".equals(UserContext.getUser()))thrownew RuntimeException("用户名不正确");
//可以获得调用参数....
Object[] args=joinPoint.getArgs();
System.out.println(Arrays.toString(args));
//proceed()方法是执行目标对象上的方法
ret = joinPoint.proceed();
//写日志
System.out.println("成功调用了"+methodName+"..方法");
return ret;
} catch (Throwable e) {
//e.printStackTrace();
System.out.println("方法调用出错"+methodName+".."+new Date());
throw e;
}
finally{
System.out.println("调了方法!"+methodName);
}
}
}
3.配置*.xml文件
例如:以上的抽取的代码: 我们需要操作在不同的方法中调用相同方法,此时可以直接在xml文件中配置.
配置: beans.xml ,该文件可以放在任何目录下,src目录下可以只在写该文件名,当放在不是src目录下时,在加载该文件时,需要指明文件的位置,负责会出现文件找不到异常
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- 配置业务组件bean切面 -->
<beanid="personService"class="cn.spring.aop.PersonServiceImpl"></bean>
<beanid="logAspect"class="cn.spring.aop.LogAspect"></bean>
<beanid="securityAspect"class="cn.spring.aop.SecurityAspect"></bean>
<!-- 配置AOP切面 -->
<aop:config>
<!-- pointcut:切入点, expression:表达式中的写执行那个业务操作,第一个星号*表示业务操作中方法的返回值,
cn.spring.aop..*(..):表示执行具体包下的业务方法,(..):表示执行那些方法,id:可以随便起名-->
<aop:pointcutexpression="execution(*cn.spring.aop..*(..))"id="personMethod"/>
<!-- 配置一个日志切面ref:表示执行cn.spring.aop.LogAspect类中的方法-->
<aop:aspectref="logAspect">
<!-- 在personMethod这个业务的切入点之前执行 writeBeforeLog()方法-->
<aop:beforemethod="writeBeforeLog"pointcut-ref="personMethod"/>
<!-- 在personMethod这个业务的切入点之后执行 writeAfterLog()方法-->
<aop:aftermethod="writeAfterLog"pointcut-ref="personMethod"/>
</aop:aspect>
<!-- 配置一个校验用户的切面 -->
<aop:aspectref="securityAspect">
<!-- 在personMethod这个业务的切入点之前执行校验用户的 checkPermission()方法
注意配置befor时,方法是按before的配置先后顺序执行,先配置的先执行.反之after同理-->
<aop:beforemethod="checkPermission"pointcut-ref="personMethod"/>
</aop:aspect>
</aop:config>
</beans
4.测试代码:
public class SpringClient {
public static void main(String[] args) {
//通过AplicationContext类加载配置文件,并初始化bean放入容器中
AplicationContext ac=newClassPathXmlApplicationContext("/beans.xml");
IPersonServiceservice=(IPersonService) ac.getBean("personService");
service.addPerson(new Person());
}
}
注意; beans.xml ,该文件可以放在任何目录下,src目录下可以只在写该文件名,当放在不是src目录下时,在加载该文件时,需要指明文件的位置,负责会出现文件找不到异常
例2 :在业务方法操作之前执行其他的方法
配置文件 : beforeadvice.xml
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- 配置核心业务组件及切面的Bean -->
<beanid="personService"class="cn.itcast.gz.aop.service.PersonServiceImpl2"></bean>
<beanid="logAspect"class="cn.itcast.gz.aop.service.LogAspect"></bean>
<beanid="securityAspect"class="cn.itcast.gz.aop.service.SecurityAspect"></bean>
<aop:config>
<!-- ref指向该切面所对应的bean -->
<aop:aspectref="securityAspect">
<!-- 描述切入点,使用的是AspectJ的切入点表达式语法 -->
<aop:beforemethod="checkPermission"pointcut="execution(*cn.itcast..*(..) )"/>
</aop:aspect>
</aop:config>
</beans>
测试代码 :
@Test
public void beforeAdvice(){
ApplicationContext ac=new ClassPathXmlApplicationContext("/beforeadvice.xml");
//对类的代理
PersonServiceImpl2 service=(PersonServiceImpl2) ac.getBean("personService");
System.out.println(service.getClass());
service.addPerson(new Person());
}
例3 : 在业务方法操作之后执行其他的方法
配置文件 : afteradvice.xml
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- 配置核心业务组件及切面的Bean -->
<beanid="personService"class="cn.itcast.gz.aop.service.PersonServiceImpl"></bean>
<beanid="logAspect"class="cn.itcast.gz.aop.service.LogAspect"></bean>
<beanid="securityAspect"class="cn.itcast.gz.aop.service.SecurityAspect"></bean>
<aop:config>
<!-- ref指向该切面所对应的bean -->
<aop:aspectref="logAspect">
<aop:pointcutexpression="execution(*cn.itcast..*(..) )"
id="allMethods"/>
<!-- 后置通知after-returning,方法正常调用完成以后执行 -->
<aop:after-returningmethod="writeAfterLog"
pointcut="execution(* cn.itcast..*(..) )"/>
<!-- 异常通知,当方法出现异常时执行,throwing用来指定异常的参数的名称,对应通知中的方法的参数名-->
<aop:after-throwingmethod="writeException"
pointcut-ref="allMethods"throwing="e"/>
</aop:aspect>
</aop:config>
</beans>
测试代码:
@Test
public void afterAdvice(){
ApplicationContext ac=new ClassPathXmlApplicationContext("/afteradvice.xml");
IPersonService service=(IPersonService) ac.getBean("personService");
System.out.println(service.getClass());
service.deletePerson(1L);
}
例3: 环绕通知:
配置文件: aroundadvice.xml
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- 配置核心业务组件及切面的Bean -->
<beanid="personService"class="cn.itcast.gz.aop.service.PersonServiceImpl"></bean>
<beanid="securityAspect"class="cn.itcast.gz.aop.service.SecurityAspect"></bean>
<aop:config>
<!-- ref指向该切面所对应的bean -->
<aop:aspectref="securityAspect">
<aop:pointcutexpression="execution(*cn.itcast..*(..) )"
id="allMethods"/>
<!-- 环绕通知,在方法的前面后面均可以加入自己的切入代码 -->
<aop:aroundmethod="checkSecurity"pointcut-ref="allMethods"/>
</aop:aspect>
</aop:config>
</beans>
测试代码:
@Test
public void aroundAdvice(){
ApplicationContext ac=new ClassPathXmlApplicationContext("/aroundadvice.xml");
IPersonService service=(IPersonService) ac.getBean("personService");
System.out.println(service.getClass());
// service.addPerson(new Person());
service.deletePerson(-1L);
}
二:AOP中使用注解方式
//定义一个切面,写日志的切向,专门解决写日志的问题
@Aspect//切面特有的注解
public class LogAspect {
//定义切入点
@Pointcut("execution(* cn.itcast.gz..IPersonService.*(..))")
private void personMethods(){}
//定义通知
@Before("personMethods()")
public void writeBeforeLog(){//切面2:日志的功能
System.out.println(UserContext.getUser()+"调用方法,时间:"+new Date());
}
@AfterReturning("personMethods()")
public void writeAfterLog(){//切面3:审计的功能
System.out.println(UserContext.getUser()+"成功调用方法,时间:"+new Date());
}
@AfterThrowing(pointcut="personMethods()",throwing="e")
public void writeException(Exception e){//切面3:异常记录
System.out.println("出现异常:"+e.getMessage()+",时间:"+new Date());
}
@After("personMethods()")
public void writeFinishLog(){//切面3:审计的功能
System.out.println(UserContext.getUser()+"完成方法调用,时间:"+new Date());
}
}
业务类:
public class PersonServiceImpl implements IPersonService {
private Stringname;
@Override
public void addPerson(Person p) {
System.out.println("addPerson()...业务逻辑省略...");
//...
///..
}
@Override
public void deletePerson(Long id) {
if(id<0)thrownew RuntimeException("ID不能小于0");
System.out.println("deletePerson()...业务逻辑省略...");
//...
///..
}
@Override
public void updatePerson(Long id, Person p) {
System.out.println("updatePerson()...业务逻辑省略...");
//...
///..
}
}
配置文件: aop-annotation.xml
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
">
<!-- 配置核心业务组件及切面的Bean -->
<beanid="logAspect"class="cn.itcast.gz.aop.service.LogAspect"></bean>
<beanid="personService"class="cn.itcast.gz.aop.service.PersonServiceImpl"></bean>
<!-- 启用Spring对@AspectJ的支持
让spring去扫描Bean中定义aspectj的相关注解,并转换成相关aop配置。-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
常用的代码简化方式:
递归结构:
1. 例如通常中的层次结构,通过不同的顶级查找下级,此时使用递归的循环查找每个下级.
public class DepartmentUtil {
public static List<Department>getAllDepartment(List<Department> topList) {
//创建一个集合,用于存储层次结构的部门关系
List<Department> list = new ArrayList<Department>();
walkDepartmentTrees(topList, "┡", list);
return list;
}
//递归调用遍历出下级部门
private static voidwalkDepartmentTrees(Collection<Department> topList,
String prefix, List<Department> list) {
for (Department d : topList) {
//复制一个新的department,
Department department = new Department();
department.setId(d.getId());
department.setName(prefix+d.getName());
list.add(department);//添加到新的集合中
walkDepartmentTrees(d.getChildren(),""+prefix,list);
}
}
}
2 jdbc中查询是代码的简化:.
public class PersonDaoImplJdbc2 implements IPersonDao {
public interface Callback{
//表示一个操作
Object execute(Statement stmt) throws Exception;
}
public void executeUpdate(Callback callback) {
//获得一个连接Connection
Connection conn=null;
Statement stmt=null;
try{
conn=DbUtil.getConn();
//开取事务
conn.setAutoCommit(false);
//初始化相关的Statment对象
stmt=conn.createStatement();
/* Stringsql="insert into person(name,age)values('"+p.getName()+"',"+p.getAge()+")";
stmt.executeUpdate(sql);*/
//加入不一样的操作代码
callback.execute(stmt);
//提交事务
conn.commit();
}
catch (Exception e) {
try {
if(conn!=null)
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
finally{
DbUtil.close(null, stmt, conn);
}
}
@Override
public void save(final Person p) {
executeUpdate(new Callback() {
@Override
public Object execute(Statement stmt)throws Exception {
String sql="insert into person(name,age)values('"+p.getName()+"',"+p.getAge()+")";
stmt.executeUpdate(sql);
return null;
}
});
}
public void delete(final Long id) {
executeUpdate(new Callback() {
@Override
public Object execute(Statement stmt)throws Exception {
String sql="delete from person where id="+id;
stmt.executeUpdate(sql);
return null;
}
});
}
}
3.例如hibernate中dao,主要负责增删改查,该方法会在每个类中都用到,此时需要简化代码, 通常我们写一个接口,该接口中定义我们需要的常用方法, 让每个类去实现该接口即可,但是在实现过程中又会出现同样的实现方法, 而且每个类都不同,对于这样的情况, 还需要在写一个类,实现我们的定义的公用接口,实现其中的方法,并通过hibernate中提供的特有增删该方法,简化sql语句,即beanUtils, 此时对于每个类中的实现情况不同,需要使用泛型, 通过反射的方式取得每个类的字节码,创建每个对象的实例.
具体如下:
@Transactional//该注解可以被继承,并在本类和子类中有效
@SuppressWarnings("unchecked")
public abstract class BaseDaoImpl<T> implements BaseDao<T>{
@Resource//注入sessionFactory工厂
private SessionFactorysessionFactory;
private Class<T>clazz;
public Session getSession(){
returnsessionFactory.getCurrentSession();
}
/**初始化对象*/
public BaseDaoImpl(){
//getGenericSuperclass():取得泛型类型的父类的参数类型
ParameterizedType type = (ParameterizedType)this.getClass().getGenericSuperclass();
//取得父类参数类型的实际参数
this.clazz = (Class<T>) type.getActualTypeArguments()[0];
}
@Override/**保存*/
public void save(T entity) {
getSession().save(entity);
}
/**根据id更新*/
@Override
public void update(Long id) {
getSession().update(id);
}
/**更新*/
public void update(T entity) {
getSession().update(entity);
}
@Override/**根据id查询*/
public T findById(Long id) {
if(id==null){
return null;
}
return (T) getSession().get(clazz, id);
}
@Override/**删除*/
public void delete(Long id) {
Object obj = findById(id);
if(obj !=null){
getSession().delete(obj);
}
}
@Override/**根据id查询*/
public List<T> getByIds(Long[] ids) {
System.out.println("岗位编号------->id="+ids[0]);
if(ids==null || ids.length==0){
return Collections.emptyList();
}//通过别名查询,并通过setParameterList()设置一个集合或数组类型的参数设置参数的类型
return getSession().createQuery("from "+clazz.getSimpleName()+" where id in(:ids)").setParameterList("ids",ids).list();
}
@Override/**查询所有*/
public List<T> findAll() {
//getSimpleName():取得泛型类型的实际参数
return getSession().createQuery("from "+clazz.getSimpleName()).list();
}
}
SSH框架整合:
1. 数据库(mysql)的创建: create databases myoa defaultcharacter set utf8;
2. 查看创建的数据 show create database myoa;
Contr+shift+t: 查找方言
Alt+shit+a快速去除重复的字段
Spring :
SHH框架整合:
spring与struts整合:
1.创建数据库
C:\Documents andSettings\Administrator>mysql -uroot -proot
mysql> create database myoa defaultcharacter set utf8;
Query OK, 1 row affected (0.03 sec)
mysql> show create database myoa;
+----------+---------------------------------------------------------------+
| Database | Create Database |
+----------+---------------------------------------------------------------+
| myoa | CREATE DATABASE `myoa` /*!40100 DEFAULT CHARACTER SET utf8 */ |
+----------+---------------------------------------------------------------+
1 row in set (0.00 sec)
2.创建web项目
创建Web项目名为: MyOA
设置项目的编码方式为utf-8 . 设置后默认项目下所有文件与utf-8编码方式
项目名右击-->properties->Resource-->Text fileencoding->Other-->UTF-8
3.配置环境
Struts 与spring整合
一: struts环境测试:
1. 导入相关的jar包
commons-fileupload-1.2.2.jar//文件上传和下载使用
commons-io-2.0.1.jar
commons-lang-2.5.jar//java.lang增强包
freemarker-2.3.18.jar
javassist-3.11.0.GA.jar
mysql-connector-java-5.1.7-bin.jar
ognl-3.0.3.jar//sruts标签库
struts2-core-2.3.1.1.jar//核心包
xwork-core-2.3.1.1.jar
2.配置web.xml文件
例如:
<?xmlversion="1.0"encoding="UTF-8"?>
<web-appversion="2.5"xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name></display-name>
<filter><!--配置struts过滤器-->
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
3.写一个action类实现actionSupport
例如:
public class TestActionextendsActionSupport{
@Override
public Stringexecute()throwsException {
System.out.println("struts整合....");
return "success";
}
}
4.配置struts.xml文件
例如:
<struts>
<!-- 配置struts模式-->
<constantname="struts.devMode"value="true"></constant>
<!-- 配置struts扩展名-->
<constantname="struts.action.extension"value="action"></constant>
<packagename="mydefault"namespace="/"extends="struts-default">
<actionname="test"class="test.TestAction">
<result>/WEB-INF/success.jsp</result>
</action>
</package>
</struts>
5.写一个返回页面success.jsp
<%@ page language="java"import="java.util.*"pageEncoding="UTF-8"%>
<!DOCTYPEHTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<body>
<fontcolor="red"> 测试struts环境搭建</font>
</body>
</html>
6.进行struts环境搭建测试:
a.启动服务器
b.在浏览器中输入url访问: http://localhost:8080/myday62/test.action
c.浏览器中出现jsp页面中写的内容,( 测试struts环境搭建),表示配置成功.
二; Struts 与spring整合环境测试
1. strutsjar如上,导入spring jar包
aspectjrt.jar// Spring的AOP切面编程技
aspectjweaver.jar
spring.jar//spring核心包
log4j-1.2.15.jar
commons-logging.jar
cglib-nodep-2.1_3.ja// AOP切面编程所依赖的技术 动态代理和cglib 包
2. 配置文件; applicationContext.xml
例如:
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- 配置spring自动扫描与装配bean-->
<context:component-scanbase-package="cn.web.myoa.ssh.test"/>
</beans>
2. 配置web.xml文件:
如:
<!--配置spring用于创建容器的监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
3.将测试struts 类使用spring提供的注解注入bean
@Controller("testAction")
public class TestActionextendsActionSupport{
@Override
publicString execute()throwsException {
System.out.println("struts整合....");
return "success";
}
}
4.写一个测试类: 使用junit提供的测试方法进行测试:
如下: 使用@Test即可直接运行代码,无需写main函数
public class SpringStrutsextendsActionSupport{
privateApplicationContextac = newClassPathXmlApplicationContext("applicationContext.xml");
@Test
public voidtestSpring()throwsException {
TestAction ta =(TestAction) ac.getBean("testAction");
System.out.println("testAction----->"+ta);
}
}
5.测试在控制打印结果:
testAction----->cn.web.myoa.ssh.test.action.TestAction@55e55f
以上表示整合成功.
三: hibernate与spring测试:
1. 导入hibernate包
antlr-2.7.6.jar
c3p0-0.9.1.jar
commons-codec.jar
commons-collections-3.1.jar
commons-logging.jar
dom4j-1.6.1.jar
hibernate-jpa-2.0-api-1.0.0.Final.jar
hibernate3.jar
jta-1.1.jar
log4j-1.2.15.jar
slf4j-api-1.5.0.jar
slf4j-log4j12-1.5.0.jar
2. 配置hibernate.cfg.xml文件:
<?xmlversion='1.0'encoding='utf-8'?>
<!DOCTYPEhibernate-configuration PUBLIC
"-//Hibernate/HibernateConfiguration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<!--该配置文件必须放在src目录下 -->
<hibernate-configuration>
<session-factory>
<!-- 数据库方言 -->
<propertyname="dialect">
org.hibernate.dialect.MySQL5Dialect
</property>
<!-- 显示SQL语句-->
<propertyname="show_sql">true</property>
<!-- 通过映射文件自动产生sql语句-->
<propertyname="hbm2ddl.auto">update</property>
<!-- 加载映射文件 -->
<mappingresource="cn/web/myoa/ssh/test/domain/User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
3. 配置applicationContext.xml文件:
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- 配置spring自动扫描与装配bean-->
<context:component-scanbase-package="cn.web.myoa.ssh.test"/>
<!-- 加载外部的properties配置文件 -->
<context:property-placeholderlocation="classpath:db.properties"/>
<!-- 配置sessionFactory -->
<beanid="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<!-- 指定hibernate配置文件-->
<propertyname="configLocation"value="classpath:hibernate.cfg.xml"></property>
<propertyname="dataSource"ref="dataSource">
</property>
</bean>
<!-- 配置数据源 -->
<beanid="dataSource"class="com.mchange.v2.c3p0.ComboPooledDataSource">
<propertyname="driverClass"value="${driverClass}"/>
<propertyname="jdbcUrl"value="${url}"/>
<propertyname="user"value="${username}"/>
<propertyname="password"value="${password}"/>
<!-- 初始化链接数 Default: 3-->
<propertyname="initialPoolSize"value="3"></property>
<!--连接池中保留的最小连接数。Default: 3 -->
<propertyname="minPoolSize"value="3"></property>
<!--连接池中最大连接数。Default: 15 -->
<propertyname="maxPoolSize"value="15"></property>
<!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default:3 -->
<propertyname="acquireIncrement"value="3"></property>
<!-- 控制数据源内加载的PreparedStatements数量。如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default:0 -->
<propertyname="maxStatements"value="8"></property>
<!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。Default:0 -->
<propertyname="maxStatementsPerConnection"value="5"></property>
<!--最大空闲时间,1800秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
<propertyname="maxIdleTime"value="1800"></property>
</bean>
<!-- 配置spring事物管理 -->
<beanid="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<propertyname="sessionFactory"ref="sessionFactory"/>
</bean>
<tx:annotation-driventransaction-manager="transactionManager"/>
</beans>
4. 写一个测试类进行测试sessionFactory:
@Test
public voidtestTransaction()throwsException {
SessionFactorysessionFactory = (SessionFactory) ac.getBean("sessionFactory");
System.out.println("sessionFactory------->"+sessionFactory);
}
5. 运行代码结果:
sessionFactory------->org.hibernate.impl.SessionFactoryImpl@162522
以上表示spring和hibernate整合成功
四: 事物测试(将事物交与spring进行管理)
//视图层
@Controller("testAction")
public class TestAction extendsActionSupport{
@Resource
privateTestServicetestService;
@Override
publicString execute()throwsException {
System.out.println("struts整合....");
testService.save();
return "success";
}
}
//业务层
@Service
public classTestService {
@Resource
privateSessionFactorysessionFactory;
@Transactional
public voidsave()throwsException{
Session session = sessionFactory.getCurrentSession();
session.save(newUser());
//int result =9/0;//测试事物回滚
session.save(newUser());
}
}
测试代码:
@Test
public void testTransaction()throwsException {
TestService testService =(TestService) ac.getBean("testService");
testService.save();
System.out.println("testService------->"+testService);
}
整体测试,启动服务器,访问url: http://localhost:8080/myday62/test.action
Jsp页面显示出内容即可. 至此ssh框架整合完毕.
注:以上版本适用于javaEE5,如果使用javaEE6的则会出现sessionFactory不能注入错误信息
shh所需要的jar包,即配置文件如下:javaEE5环境,总共有24个jar包
antlr-2.7.6.jar
aspectjrt.jar
aspectjweaver.jar
c3p0-0.9.1.jar
cglib-nodep-2.1_3.jar
commons-collections-3.1.jar
commons-fileupload-1.2.1.jar
commons-io-1.3.2.jar
commons-logging.jar
dom4j-1.6.1.jar
freemarker-2.3.15.jar
hibernate-jpa-2.0-api-1.0.0.Final.jar
hibernate3.jar
javassist-3.12.0.GA.jar
jta-1.1.jar
log4j-1.2.15.jar
mysql-connector-java-5.1.5-bin.jar
ognl-2.7.3.jar
slf4j-api-1.5.0.jar
slf4j-log4j12-1.5.0.jar
spring.jar
struts2-core-2.1.8.1.jar
struts2-spring-plugin-2.1.8.1.jar
xwork-core-2.1.6.jar
需要配置的文件:
applicationContext.xml //主要用于加包下面的实体bean,直接在压缩包搜索即可,文件名不能修改
db.properties //属相文件,后缀必须为properties,主要存一些经常变换的属性,键值对存储形式,key=value
User.hbm.xml //实体/关系映射文件,主要是实体对象的属性和表之间的对应,文件名和类名一直,类名.hbm.xml,该文件与实体bean放在一起
hibernate.cfg.xml //固定文件,不可修改文件名,主要配置数据连接信息
log4j.properties//日志记录文件,该文件需要设置,为error级别,减少不必要显示的信息,配置log4j.rootLogger=error,stdout,其他带有日志级别(info,warn,debug)的全部使用#注释
struts.xml //用于配置action,文件名不可改变,只能放在src目录下.其他文件任意
2012-09-20Day58:
Lucene: 搜索:
开发步骤:
1.导入jar包:
lucene-core-3.0.1.jar(核心包)
lucene-analyzers-3.0.1.jar(分词器)
lucene-highlighter-3.0.1.jar(高亮)
lucene-memory-3.0.1.jar(高亮)
模拟示例:
public classLuceneTest {
private staticDirectory directory;
private staticAnalyzer analyzer;
static{
try {//创建索引库目录
directory = FSDirectory.open(new File("e:/lucene1/"));
//创建分词器
analyzer=newStandardAnalyzer(Version.LUCENE_30);
} catch(IOException e) {
throw newRuntimeException(e);
}
}
//将article转换为document对象
publicDocument ArticleToDocument(Article article) {
//建立Document,并将article转换为doc
Documentdoc = newDocument();
//将字符与整形的转换
StringidStr = NumericUtils.intToPrefixCoded(article.getId());
doc.add(newField("id",idStr,Store.YES,Index.ANALYZED_NO_NORMS));
//Index.ANALYZED:表示需要分词
doc.add(newField("title",article.getTitle(), Store.YES,Index.ANALYZED));
doc.add(newField("content",article.getContent(),Store.YES,Index.ANALYZED));
return doc;
}
@Test//保存数据
public voidsave() throwsException {
Articlearticle = newArticle();
article.setId(1);
article.setTitle("Lucene练习");
article.setContent("开始学习lucene了,该软件主要做搜索功能,有全面搜索,垂直搜索等");
//将article转换为document对象
Documentdoc = ArticleToDocument(article);
//建立索引,并添加到索引库中
IndexWriter in = newIndexWriter(directory,analyzer,MaxFieldLength.LIMITED);
in.addDocument(doc);
//关闭资源
in.close();
}
//将document转换为Article
public voiddocumentToArticle(List<Article> list, Document doc) {
Articlearticle = newArticle();
//将字符串转换为整形
Integerid = NumericUtils.prefixCodedToInt(doc.get("id"));
article.setId(id);
article.setTitle(doc.get("title"));
article.setContent(doc.get("content"));
list.add(article);
}
//查询
@Test
public voidfind() throwsException {
try {
//查询字符串
Stringstr = "Lucene";
//取得执行查询的query,从title和content中查询
QueryParserparser = newMultiFieldQueryParser(Version.LUCENE_30,newString[]{"title","content"},analyzer);
//将字符串转换为query对象
Queryquery = parser.parse(str);
//将搜索的结果放在list集合
List<Article>list = newArrayList<Article>();
//从指定的索引库中查询
IndexSearcherindexSearcher = newIndexSearcher(directory);
//查询返回匹配的最多n条数据
TopDocstopDocs = indexSearcher.search(query, 100);
//匹配的最多n条数据
ScoreDoc[]scoreDocs = topDocs.scoreDocs;
//匹配到结果的总数
intcount = topDocs.totalHits;
//取出结果数据
for (inti=0;i<scoreDocs.length;i++){
//取得得分结果
floatscore = scoreDocs[i].score;
//取得doc的id
int docId = scoreDocs[i].doc;
//根据docId取得document对象
Documentdoc = indexSearcher.doc(docId);
//将document转换为Article
documentToArticle(list,doc);
}
//取出结果
for (Article article : list) {
System.out.println("搜多结果:");
System.out.println("id="+article.getId()+",title="+article.getTitle()+",content="+article.getContent());
}
} catch(Exception e) {
throw newRuntimeException(e);
}
}
}
生成高亮字段Highlighter:
//设置生成高亮
//格式字符串
Formatterformatter =
newSimpleHTMLFormatter("<font color='red'>","</font>");
//得分记录
Scorerscorer = newQueryScorer(query);
Highlighterhg = newHighlighter(formatter, scorer);
//设置高亮字段
hg.setTextFragmenter(newSimpleFragmenter(10));
取出高亮字段并将原有的字段替换为现有的高亮字段
//根据docId取得document对象
Document doc =indexSearcher.doc(scoreDocs[i].doc);
//取出高亮字段,当内容中没有高亮字段时,返回null
Stringtext = hg.getBestFragment(LuceneUtils.getAnalyzer(),"content",doc.get("content"));
//使用高亮替换原有字段
if(text!=null){
doc.getField("content").setValue(text);
}
//将document转换为Article
ArticleDocumentUtil.documentToArticle(list, doc);
Lucene中的查询语句:
/**关键词查询*/
@Test
public voidtest1(){
//Queryquery = new TermQuery(new Term("title","lucene"));
Queryquery = newTermQuery(new Term("content","lucene"));
//Queryquery = new MatchAllDocsQuery();
search(query);
}
/**范围查询*/
@Test
public void testRange(){
Queryquery = NumericRangeQuery.newIntRange("id",1, 4,true,true);
search(query);
}
// 通配符查询,? 表示1个任意字符, * 表示0或多个任意字符
@Test
public voidtest2(){
// 对应的查询字符串:title:luce??
//Queryquery = new WildcardQuery(new Term("title","luce??"));
// 对应的查询字符串:title:luce*
Queryquery = newWildcardQuery(new Term("content","luce*"));
search(query);
}
//查询所有
@Test
public voidtestQueryAll(){
Queryquery = newMatchAllDocsQuery();
search(query);
}
//模糊查询
@Test
public voidtestFuzzyQuery(){
//第二个参数表示最小相似度,0-1的浮点型数字,相似度=字符的总长度*相似度
Queryquery = newFuzzyQuery(new Term("content","luceneXX"),0.6f);
search(query);
}
//短语查询
@Test
public voidtestPhraseQuery(){
PhraseQueryp = newPhraseQuery();
// 对应的查询字符串:title:"? ? ?lucene",3:表示从第几个位置开始查询
//p.add(newTerm("title","lucene"), 3);
//search(p);
//单词之间的间隔查询,最多间隔3个单词
p.add(new Term("content","lucene"));
p.add(new Term("content","功能"));
p.setSlop(2);
search(p);
}
//查询方法略: search(p);如上:
排序:
//排序方式:
Sort sort = new Sort(new SortField("id",SortField.INT));//升序
//Sort sort = newSort(new SortField("id",SortField.INT,true));//将序
Filter filter =NumericRangeFilter.newIntRange("id", 1, 5,true, true);
TopDocs topDocs =indexSearcher.search(query, filter, 10);
//高亮字段设置
Formatter formatter = new SimpleHTMLFormatter("<font color='red'>","</font>");
Scorer scort = new QueryScorer(query);
Highlighter highLighter = new Highlighter(formatter, scort);
highLighter.setTextFragmenter(new SimpleFragmenter(20));
//匹配的最多n条数据
ScoreDoc[] scoreDocs =topDocs.scoreDocs;
//匹配到结果的总数
int count = topDocs.totalHits;
循环遍历取出结果
2012-09-23 day60:
WebService:
操作步骤:
1, 发布一个WebService
a. 写一个实现类
例如:
//使用注解注入serviceName:表示类名, portName:端口名称,name实现类名
@WebService(serviceName="TestWebService",portName="TestWebServiceInstance",name="TestWebServiceImpl")
public class TestWebServiceImpl{
@WebMethod(operationName="sayHello")//操作的方法名
//@WebParam(name="content")参数名称
public String sayHi(@WebParam(name="content") String content) {
System.out.println("TestWebServiceImpl"+content);
return"访问成功...";
}
}
//发布类
public class WSClient {
public static void main(String[] args) {
Endpoint.publish("http://127.0.0.1:9001/webservice",new TestWebServiceImpl() );
System.out.println("发布成功........");
}
}
b. 调用该主函数进行发布
2, 使用WS Explorer调用这个WebService
a. 打开myeclips菜单栏中的window-à参数(preferenece)---à搜索(tcp)--à点击tcp/ipmonitor---à编辑(edit)
-----à填写 本地监听端口号(location monitoring port:),即在访问时使用的端口号,如9005, 主机号(localhost: 填写本地主机即可), 端口号: 填写需要代理的端口号:如:9000, 协议,选择TCP/IP -à启动(start)-àok
b.点击菜单栏中soapWeb service Explorer --à输入url地址(及访问地址, 如: http://127.0.0.1:9004/webservice?wsdl) 点击方法名---à点击add添加参数 ---à点击go
3,配置TCP/IP Monitor监控发送的数据。
a 点击window中的显示视图项(show view)--àother--搜索添加的监视器(TCP/IP MONITOR).
4,使用wsimport+生成的类调用这个WebService.
a. 使用命令wsimport进行生成java类中的代码, 首先查看环境是否正确(即该命令是否能直接访问) à如果不可以在环境中配置----à 在dos控制台输入: wsimport, 查看输入方式:如:
wsimport -s . -p cn.web.clientTest http://127.0.0.1:9000/webservice?wsdl ---à此时会出现,解析wsdl,
生成代码,等英文-à完成
-s:表示目录 .点表示当前目录, -p : 表示包名, 最后为访问地址
b. 将生成的代码放入工程中, 写一个类 ,通过主函数调用过程:
例如:
public classLocalHostTest {
public static void main(String[] args) {
Stringstr = newTestWebService().getTestWebServiceInstance().sayHello("本地主机号实验!");
System.out.println(str);
}
}
D. 调用主函数执行,或者在浏览器中直接访问地址也可以.在浏览器中会出现xml格式的文档,该文档中主要描述的是测试类的信息,如: 类名,方法,参数,返回值等.
操作过程中出现的异常: 常见的有绑定异常: 主要是端口被占用, 关闭服务重新部署即可
webService 中常用的注解:
@WebService
标注要暴露为Web Services的类或接口 ,用于修饰类或接口,包含的属性有:
targetNamespace属性:
定义命名空间,默认为”http://”+”包名倒排”
name属性
WebService 的名称,默认为类名。
wsdlLocation属性
描述服务的预定义 WSDL 的位置
endpointInterface属性
定义服务抽象 Web Service 协定的服务端点接口的完整名称
@webmethod:
action属性
操作的活动
operationName属性
与此方法匹配的 wsdl:operation 的名称
exclude属性
标注此方法是否被暴露,默认为false
如果所有方法上都没有指定@WebMethod,则默认是所有的方法都是对外暴露的方法。如果有任一方法指定了@WebMethod,则只有指定这个注解的才是对外暴露的方法。
@webresult:
@WebResult定义返回值,返回值类型不能为接口类或抽象类,而且必须有个不带参的构造函数,包含属性
name属性
返回值的名称
partName属性
表示此返回值的wsdl:part 的名称
targetNamespace属性
返回值的XML 名称空间
header属性
如果为true,则结果是从消息头而不是消息正文获取的
@webparm
@WebParam定义方法的参数,参数类型不能为接口类或抽象类,而且必须有个不带参的构造函数,包含属性
name属性
参数名称
partName属性
表示此参数的wsdl:part 的名称
targetNamespace属性
参数的XML 名称空间
header属性
如果为true,则结果是从消息头而不是消息正文获取的
mode属性
参数的流向(IN、OUT 或INOUT 之一)
@SoapBinding:
@SOAPBinding定义WebService 在SOAP中的消息协议,用于申修饰类或接口,包含的属性有:
style属性:
定义消息的编码类型,有两种类型的soap通信风格:Document和RPC。
Document:
坚持定义良好的契约,典型通过xmlschema创建,schema指定了消息的格式对于获得更好的可交互性的ws来说Document literal是首选。
RPC(RemotePrcedure call):
表示SOAP消息的body包含方法的xml表示,为了(反)序列化方法参数,SOAP规范定义了一系列编码规则。RPC主要用于传统的同SOAPencoding规则的协同,组合是指RPC/encoded。不过也可以有RPC/literal通信风格(不需要有任何编码格式),但依然限制RPC基于方法的通信,此种情况下文档无法进行检验因为没有schema定义。RPC风格的ws有很多交互问题。
use属性
定义消息的格式化类型。(通信风格)作为SOAP绑定定义在wsdl文档中。SOAP绑定可以是encoded用法和literal用法:encoding指消息会以某些格式进行编码。literal指纯文本,没有任何逻辑编码。
WSDL:全称是Web ServicesDescription Language(Web服务描述语言),是一种基于XML格式的、关于Web服务的描述语言。
主要描述的时web服务与通信之间的xml语言,提供了web服务的相关内容, 如: 提供的接口, 方法名,参数类型,返回值等.
SOAP:
全称是Simple Object Application Propotol(简单对象访问协议),是一种轻量的、简单的、基于 XML 的协议。他是一种标准化的传输消息的XML消息格式,是WebService的标准通信协议。
UDDI:
全称是UniversalDescription, Discovery and Integration(可译为“统一描述、发现和集成协议”或“通用描述、发现与集成服务”)。UDDI是一种目录服务,企业可以使用它对Web Services进行注册和搜索。 UDDI是一个分布式的互联网服务注册机制,它集描述(Universal Description)、检索(Discovery)与集成(Integration)为一体,其核心是注册机制。
使用CXF发布webservice
1.导入jar包
2.写一个实现类
3.发布信息
例如:
1.实现类
@WebService(serviceName="orderservice",portName="orderServiceImpl")
public class OrderServiceImpl implements OrderService{
@WebMethod
public String findAll(@WebParam(name="name")String name) {
System.out.println("发布成功.......");
return"调用成功"+name;
}
}
2/发布信息
public class CXFServiceTest {
public static void main(String[] args) {
JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
factory.setAddress("http://127.0.0.1:9006/Service");//发布地址
factory.setServiceBean(new OrderServiceImpl());//发布的实现类
factory.create();//发布信息
System.out.println("发布成功.....");
}
}
使用CXF的wsdl2java生成客户端代码并调用
wsdl2java -d . -p cn.itcast.ws http://127.0.0.1/ws2?wsdl
使用普通类发布webservice
1.实现类
public class OrderServiceImpl2 implements OrderService{
public String findAll(String name) {
System.out.println("发布成功.......");
return"调用成功"+name;
}
}
2.发布信息
public class CXFServiceTest {
public static void main(String[] args) {
ServerFactoryBean factory = new ServerFactoryBean();
factory.setAddress("http://127.0.0.1:9006/OrderService");//发布地址
factory.setServiceBean(new OrderServiceImpl2());//发布的实现类
System.out.println("不使用注解方式发布webservice");
}
}
HTTPClinet:
public class CxfRSClient{
@Test
public void testGet() throws Exception{
HttpUriRequest request = new HttpGet("http://127.0.0.1/product/32sdfj");
HttpClient client = new DefaultHttpClient();
HttpResponse response = client.execute(request);
InputStream in =response.getEntity().getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(in,"UTF-8"));
if(response.getEntity()!=null){
String line;
while((line=reader.readLine())!=null){
System.out.println(line);
}
}
}
}
//发布信息
public class CxfRSTest{
public static voidmain(String[] args) {
JAXRSServerFactoryBeanfactory = newJAXRSServerFactoryBean();
factory.setAddress("http://127.0.0.1");
factory.setServiceBean(newProductServiceImpl());
factory.create();// 发布服务
System.out.println("RSservice--发布成功........");
}
}
Linux:
经典著作:
<现在操系统,计算机网络>.
Linux:该系统中区分大小写.
Crtl+alt+ f1.2.3.4.5.6.7切换图形界面
Linux基本命令
ls: 查看当前目录和文件
-1 显示文件的详细信息
-a 显示当前目录所有文件,包含隐藏文件.
mkdir:创建目录
-p 父目录不存在的情况下生成父目录, 格式:mkdir 目录名.如:mkdir family
rmdir: 删除目录,如:rmdir family
clrar:清空当前的显示内容
cd:进入到指定目录格式:cd 目录名,如: cdfamily
cd.. :回到父目录
touch:创建一个空的文件, 格式: touch 文件名. 如: touch abc.txt
echo:创建一个带内容的文件格式:echo 内容>文件. 如;echo“hello world”> abc.txt
cat, tac : 显示文本文件内容,格式:cat 文件.如: catabc.txt
cp:复制文件或目,格式:cp文件 目录或者文件全路径
cpdoll.txt /home/itcast/familyA/house/roomB
rm:删除文件,格式:rm 文件名.如: rmabc.txt
mv:移动文件,格式:mv 文件名 目录的全路径
mvbook.txt /home/wang/familyA/house/roomB
moreless 分页显示内容, 格式: more 文件名. 如: more book.txt
head,tail分别显示文件开头和结尾内容,如: headbook.txt , tail book.txt
wc:显示文件内容的行数,字数,字符数. 如: wc book.txt
find: 查找指定的文件,格式:find –name 文件名. 如:find –name book.txt
grep:查找指定的文本中的字符串,格式: grep 字符串文件名.如:grepbest book.txt
pwd: 显示当前目录
tree: 显示目录树
rmdir:删除空目录,格式:rmdir 目录.如:rmdir bathroom
ln–s :建立软连接,格式: ln –s 目录 目录
ln -s/home/ubnntu/familyA/house/roomB /home/roomB
stat: 显示指定文件的相关信息如: cd/home/local , 显示目录结构信息:stat目录名
who,w : 显示在线登录用户 ,如: who
whoami:显示用户自己的身份
hostname: 显示主机名称
hostname -i 显示主机IP
uname: 显示系统信息
uname–a : 显示全部信息
top:显示当前系统中耗费资源最多的进程,动态实时监控.退出ctrl+c
ps: 显示瞬间进程状态.
ps–aux : 显示所有瞬间进程
du:显示指定文件已经使用的磁盘空间的总量.
du ,或者du 目录或者文件, 或者 du –h目录或者文件
df: 显示系统磁盘空间的使用状况.
df ,或者df–h(详细信息)
free: 显示当前内存和交换空间的使用状况.
ifconfig:显示网络接口信息
ping: 测试网络的连通性能
netstat: 显示网络状态.
man: 命令帮助信息查询, man ls
clear:清屏
kill杀死一个进程
关机/重启命令
1.shutdown命令可以安全的关闭Linux系统,shutdown命令必须有超级用户才能执行。shutdown命令执行后会以广播的形式通知正在系统中工作的所有用户,
1:shutdown -h now (关机不重启)
2:shutdown -r now (关机重启)
3:shutdown now (关机)
4:shutdown 15:22
2:halt 关机后关闭电源
3:reboot重新启动
gzip备份和压缩命令:
安装vim编辑器
sudo dpkg -i tree_1.5.3-1_i386.deb
取消: sudo dpkg –r tree_1.5.3-1_i386.deb