/* Navicat Premium Data Transfer Source Server : mysql-localhost Source Server Type : MySQL Source Server Version : 50719 Source Host : localhost Source Database : forest_blog Target Server Type : MySQL Target Server Version : 50719 File Encoding : utf-8 Date: 10/10/2017 14:59:18 PM */ SET NAMES utf8; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for `article` -- ---------------------------- DROP TABLE IF EXISTS `article`; CREATE TABLE `article` ( `article_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `article_user_id` int(10) unsigned DEFAULT '1', `article_title` varchar(255) DEFAULT NULL, `article_content` mediumtext, `article_parent_category_id` int(10) DEFAULT NULL, `article_child_category_id` int(10) DEFAULT NULL, `article_tag_ids` varchar(50) DEFAULT NULL, `article_view_count` int(10) DEFAULT '0', `article_comment_count` int(5) DEFAULT '0', `article_like_count` int(5) DEFAULT '0', `article_post_time` datetime DEFAULT NULL, `article_update_time` datetime DEFAULT NULL, `article_is_comment` int(2) unsigned DEFAULT NULL, `article_status` int(2) unsigned DEFAULT '1', `article_order` int(2) unsigned DEFAULT NULL, PRIMARY KEY (`article_id`) ) ENGINE=MyISAM AUTO_INCREMENT=19 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of `article` -- ---------------------------- BEGIN; INSERT INTO `article` VALUES ('1', '1', '[转载]SpringMVC中使用Interceptor拦截器', '

SpringMVC 中的Interceptor 拦截器也是相当重要和相当有用的,它的主要作用是拦截用户的请求并进行相应的处理。比如通过它来进行权限验证,或者是来判断用户是否登陆,或者是像12306 那样子判断当前时间是否是购票时间。


一、定义Interceptor实现类

SpringMVC 中的Interceptor 拦截请求是通过HandlerInterceptor 来实现的。在SpringMVC 中定义一个Interceptor 非常简单,主要有两种方式,第一种方式是要定义的Interceptor类要实现了Spring 的HandlerInterceptor 接口,或者是这个类继承实现了HandlerInterceptor 接口的类,比如Spring 已经提供的实现了HandlerInterceptor 接口的抽象类HandlerInterceptorAdapter ;第二种方式是实现Spring的WebRequestInterceptor接口,或者是继承实现了WebRequestInterceptor的类。


(一)实现HandlerInterceptor接口

HandlerInterceptor 接口中定义了三个方法,我们就是通过这三个方法来对用户的请求进行拦截处理的。


(1 )preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法,顾名思义,该方法将在请求处理之前进行调用。SpringMVC 中的Interceptor 是链式的调用的,在一个应用中或者说是在一个请求中可以同时存在多个Interceptor 。每个Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor 中的preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值Boolean 类型的,当它返回为false 时,表示请求结束,后续的Interceptor 和Controller 都不会再执行;当返回值为true 时就会继续调用下一个Interceptor 的preHandle 方法,如果已经是最后一个Interceptor 的时候就会是调用当前请求的Controller 方法。


(2 )postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法,由preHandle 方法的解释我们知道这个方法包括后面要说到的afterCompletion 方法都只能是在当前所属的Interceptor 的preHandle 方法的返回值为true 时才能被调用。postHandle 方法,顾名思义就是在当前请求进行处理之后,也就是Controller 方法调用之后执行,但是它会在DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller 处理之后的ModelAndView 对象进行操作。postHandle 方法被调用的方向跟preHandle 是相反的,也就是说先声明的Interceptor 的postHandle 方法反而会后执行,这和Struts2里面的Interceptor 的执行过程有点类型。Struts2 里面的Interceptor 的执行过程也是链式的,只是在Struts2 里面需要手动调用ActionInvocation 的invoke 方法来触发对下一个Interceptor 或者是Action 的调用,然后每一个Interceptor 中在invoke 方法调用之前的内容都是按照声明顺序执行的,而invoke 方法之后的内容就是反向的。


(3 )afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法,该方法也是需要当前对应的Interceptor 的preHandle 方法的返回值为true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。


下面是一个简单的代码说明:


import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;

import org.springframework.web.servlet.ModelAndView;

public class SpringMVCInterceptor implements HandlerInterceptor {

    /** 

     * preHandle方法是进行处理器拦截用的,顾名思义,该方法将在Controller处理之前进行调用,SpringMVC中的Interceptor拦截器是链式的,可以同时存在 

     * 多个Interceptor,然后SpringMVC会根据声明的前后顺序一个接一个的执行,而且所有的Interceptor中的preHandle方法都会在 

     * Controller方法调用之前调用。SpringMVC的这种Interceptor链式结构也是可以进行中断的,这种中断方式是令preHandle的返 

     * 回值为false,当preHandle的返回值为false的时候整个请求就结束了。 

     */

    @Override

    public boolean preHandle(HttpServletRequest request,

            HttpServletResponse response, Object handler) throws Exception {

        // TODO Auto-generated method stub  

        return false;

    }

    /** 

     * 这个方法只会在当前这个Interceptor的preHandle方法返回值为true的时候才会执行。postHandle是进行处理器拦截用的,它的执行时间是在处理器进行处理之 

     * 后,也就是在Controller的方法调用之后执行,但是它会在DispatcherServlet进行视图的渲染之前执行,也就是说在这个方法中你可以对ModelAndView进行操 

     * 作。这个方法的链式结构跟正常访问的方向是相反的,也就是说先声明的Interceptor拦截器该方法反而会后调用,这跟Struts2里面的拦截器的执行过程有点像, 

     * 只是Struts2里面的intercept方法中要手动的调用ActionInvocation的invoke方法,Struts2中调用ActionInvocation的invoke方法就是调用下一个Interceptor 

     * 或者是调用action,然后要在Interceptor之前调用的内容都写在调用invoke之前,要在Interceptor之后调用的内容都写在调用invoke方法之后。 

     */

    @Override

    public void postHandle(HttpServletRequest request,

            HttpServletResponse response, Object handler,

            ModelAndView modelAndView) throws Exception {

        // TODO Auto-generated method stub  

    }

    /** 

     * 该方法也是需要当前对应的Interceptor的preHandle方法的返回值为true时才会执行。该方法将在整个请求完成之后,也就是DispatcherServlet渲染了视图执行, 

     * 这个方法的主要作用是用于清理资源的,当然这个方法也只能在当前这个Interceptor的preHandle方法的返回值为true时才会执行。 

     */

    @Override

    public void afterCompletion(HttpServletRequest request,

            HttpServletResponse response, Object handler, Exception ex)

    throws Exception {

        // TODO Auto-generated method stub  

    }

}

(二)实现WebRequestInterceptor 接口

WebRequestInterceptor 中也定义了三个方法,我们也是通过这三个方法来实现拦截的。这三个方法都传递了同一个参数WebRequest ,那么这个WebRequest 是什么呢?这个WebRequest 是Spring 定义的一个接口,它里面的方法定义都基本跟HttpServletRequest 一样,在WebRequestInterceptor 中对WebRequest 进行的所有操作都将同步到HttpServletRequest 中,然后在当前请求中一直传递。


(1 )preHandle(WebRequest request) 方法。该方法将在请求处理之前进行调用,也就是说会在Controller 方法调用之前被调用。这个方法跟HandlerInterceptor 中的preHandle 是不同的,主要区别在于该方法的返回值是void ,也就是没有返回值,所以我们一般主要用它来进行资源的准备工作,比如我们在使用Hibernate 的时候可以在这个方法中准备一个Hibernate 的Session 对象,然后利用WebRequest 的setAttribute(name, value, scope) 把它放到WebRequest 的属性中。这里可以说说这个setAttribute 方法的第三个参数scope ,该参数是一个Integer 类型的。在WebRequest 的父层接口RequestAttributes 中对它定义了三个常量:


SCOPE_REQUEST :它的值是0 ,代表只有在request 中可以访问。


SCOPE_SESSION :它的值是1 ,如果环境允许的话它代表的是一个局部的隔离的session,否则就代表普通的session,并且在该session范围内可以访问。


SCOPE_GLOBAL_SESSION :它的值是2 ,如果环境允许的话,它代表的是一个全局共享的session,否则就代表普通的session,并且在该session 范围内可以访问。


(2 )postHandle(WebRequest request, ModelMap model) 方法。该方法将在请求处理之后,也就是在Controller 方法调用之后被调用,但是会在视图返回被渲染之前被调用,所以可以在这个方法里面通过改变数据模型ModelMap 来改变数据的展示。该方法有两个参数,WebRequest 对象是用于传递整个请求数据的,比如在preHandle 中准备的数据都可以通过WebRequest 来传递和访问;ModelMap 就是Controller 处理之后返回的Model 对象,我们可以通过改变它的属性来改变返回的Model 模型。


(3 )afterCompletion(WebRequest request, Exception ex) 方法。该方法会在整个请求处理完成,也就是在视图返回并被渲染之后执行。所以在该方法中可以进行资源的释放操作。而WebRequest 参数就可以把我们在preHandle 中准备的资源传递到这里进行释放。Exception 参数表示的是当前请求的异常对象,如果在Controller中抛出的异常已经被Spring 的异常处理器给处理了的话,那么这个异常对象就是是null 。


 


下面是一个简单的代码说明:


import org.springframework.ui.ModelMap;

import org.springframework.web.context.request.WebRequest;

import org.springframework.web.context.request.WebRequestInterceptor;

public class AllInterceptor implements WebRequestInterceptor {

    /** 

     * 在请求处理之前执行,该方法主要是用于准备资源数据的,然后可以把它们当做请求属性放到WebRequest中 

     */

    @Override

    public void preHandle(WebRequest request) throws Exception {

        // TODO Auto-generated method stub  

        System.out.println(\"AllInterceptor...............................\");

        request.setAttribute(\"request\", \"request\", WebRequest.SCOPE_REQUEST);//这个是放到request范围内的,所以只能在当前请求中的request中获取到  

        request.setAttribute(\"session\", \"session\", WebRequest.SCOPE_SESSION);//这个是放到session范围内的,如果环境允许的话它只能在局部的隔离的会话中访问,否则就是在普通的当前会话中可以访问  

        request.setAttribute(\"globalSession\", \"globalSession\", WebRequest.SCOPE_GLOBAL_SESSION);//如果环境允许的话,它能在全局共享的会话中访问,否则就是在普通的当前会话中访问  

    }

    /** 

     * 该方法将在Controller执行之后,返回视图之前执行,ModelMap表示请求Controller处理之后返回的Model对象,所以可以在 

     * 这个方法中修改ModelMap的属性,从而达到改变返回的模型的效果。 

     */

    @Override

    public void postHandle(WebRequest request, ModelMap map) throws Exception {

        // TODO Auto-generated method stub  

        for (String key:map.keySet())

            System.out.println(key + \"-------------------------\");;

        map.put(\"name3\", \"value3\");

        map.put(\"name1\", \"name1\");

    }

    /** 

     * 该方法将在整个请求完成之后,也就是说在视图渲染之后进行调用,主要用于进行一些资源的释放 

     */

    @Override

    public void afterCompletion(WebRequest request, Exception exception)

    throws Exception {

        // TODO Auto-generated method stub  

        System.out.println(exception + \"-=-=--=--=-=-=-=-=-=-=-=-==-=--=-=-=-=\");

    }

}


 二、把定义的拦截器类加到SpringMVC的拦截体系中

1.在SpringMVC的配置文件中加上支持MVC的schema


xmlns:mvc=\"http://www.springframework.org/schema/mvc\"

xsi:schemaLocation=\" http://www.springframework.org/schema/mvc

    http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd\"

下面是我的声明示例:




这样在SpringMVC的配置文件中就可以使用mvc标签了,mvc标签中有一个mvc:interceptors是用于声明SpringMVC的拦截器的。


 


(二)使用mvc:interceptors标签来声明需要加入到SpringMVC拦截器链中的拦截器



    

    

    

        

        

        

    



由上面的示例可以看出可以利用mvc:interceptors标签声明一系列的拦截器,然后它们就可以形成一个拦截器链,拦截器的执行顺序是按声明的先后顺序执行的,先声明的拦截器中的preHandle方法会先执行,然而它的postHandle方法和afterCompletion方法却会后执行。


在mvc:interceptors标签下声明interceptor主要有两种方式:


(1)直接定义一个Interceptor实现类的bean对象。使用这种方式声明的Interceptor拦截器将会对所有的请求进行拦截。


(2)使用mvc:interceptor标签进行声明。使用这种方式进行声明的Interceptor可以通过mvc:mapping子标签来定义需要进行拦截的请求路径。


经过上述两步之后,定义的拦截器就会发生作用对特定的请求进行拦截了。




', '1', '9', '12', '2', '0', '0', '2017-10-06 23:54:18', '2017-10-07 15:31:36', '1', '1', '1'), ('2', '1', 'springmvc 表单中文乱码解决方案', '

基本上通过在 web.xml 了配置拦截器就可以解决。

这里需要注意的是,最好把这段代码放在web.xml中开头的位置,因为拦截有顺序,如果放在后面的话容易拦截不到。

拦截器代码如下

  1. <!--post乱码过滤器-->  
  2. <!-- 配置springMVC编码过滤器 -->  
  3. <filter>  
  4.     <filter-name>CharacterEncodingFilter</filter-name>  
  5.     <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  
  6.     <!-- 设置过滤器中的属性值 -->  
  7.     <init-param>  
  8.       <param-name>encoding</param-name>  
  9.       <param-value>UTF-8</param-value>  
  10.     </init-param>  
  11.     <!-- 启动过滤器 -->  
  12.     <init-param>  
  13.       <param-name>forceEncoding</param-name>  
  14.       <param-value>true</param-value>  
  15.     </init-param>  
  16. </filter>  
  17. <!-- 过滤所有请求 -->  
  18. <filter-mapping>  
  19.     <filter-name>CharacterEncodingFilter</filter-name>  
  20.     <url-pattern>/*</url-pattern>  
  21. </filter-mapping>  


顺便再补充其他的几个可能原因。

1、数据库和数据表不是 utf-8 编码

就在前几天,我遇到的问题正是这个。当时是刚从 windows 搬到 mac。也是在提交 post 表单的时候,中文一直是乱码,后台百度发现,原来是 navicat 的原因。就是在新建 数据库连接(注意是连接),不能选择 utf-8,应该选择默认的自动。这个地方很坑人。

数据库和数据表当然是 utf-8,一般这种情况很少出错。

 

2、修改 Tomcat 的 server.xml 文件,添加 utf-8 编码

  1. <Connector port=\"8080\" protocol=\"HTTP/1.1\"  
  2.                connectionTimeout=\"20000\"  
  3.                redirectPort=\"8443\" URIEncoding=\"UTF-8\"  
  4.                useBodyEncodingForURI=\"true\" />  

3、注意你的 IDE (如Eclipse,IntelliJ IDEA)的 jsp 文件编码,一般在右下角可以看出来,通常我们也要把默认的编码设置为 utf-8。

 

4、还有一种方法就是 直接对接收到的数据编码转换,我感觉有点麻烦,所以不是很喜欢

  1. String name=new String((user.getUname()).getBytes(\"ISO-8859-1\"),\"utf8\");  

总结:一般来说,第一种拦截器方法是可用的,要注意的是要把拦截器代码放到 web.xml 头部,防止拦截不到,还有表单一定要 post 方式提交。

', '1', '9', '12', '2', '0', '0', '2017-10-07 12:12:42', '2017-10-07 15:34:10', '1', '1', '1'), ('4', '1', '[转载]HTTP的幂等性', '

理解HTTP幂等性

基于HTTP协议的Web API是时下最为流行的一种分布式服务提供方式。无论是在大型互联网应用还是企业级架构中,我们都见到了越来越多的SOA或RESTful的Web API。为什么Web API如此流行呢?我认为很大程度上应归功于简单有效的HTTP协议。HTTP协议是一种分布式的面向资源的网络应用层协议,无论是服务器端提供Web服务,还是客户端消费Web服务都非常简单。再加上浏览器、Javascript、AJAX、JSON以及HTML5等技术和工具的发展,互联网应用架构设计表现出了从传统的PHP、JSP、ASP.NET等服务器端动态网页向Web API + RIA(富互联网应用)过渡的趋势。Web API专注于提供业务服务,RIA专注于用户界面和交互设计,从此两个领域的分工更加明晰。在这种趋势下,Web API设计将成为服务器端程序员的必修课。然而,正如简单的Java语言并不意味着高质量的Java程序,简单的HTTP协议也不意味着高质量的Web API。要想设计出高质量的Web API,还需要深入理解分布式系统及HTTP协议的特性。

幂等性定义

本文所要探讨的正是HTTP协议涉及到的一种重要性质:幂等性(Idempotence)。在HTTP/1.1规范中幂等性的定义是:

Methods can also have the property of \"idempotence\" in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.

从定义上看,HTTP方法的幂等性是指一次和多次请求某一个资源应该具有同样的副作用。幂等性属于语义范畴,正如编译器只能帮助检查语法错误一样,HTTP规范也没有办法通过消息格式等语法手段来定义它,这可能是它不太受到重视的原因之一。但实际上,幂等性是分布式系统设计中十分重要的概念,而HTTP的分布式本质也决定了它在HTTP中具有重要地位。

分布式事务 vs 幂等设计

为什么需要幂等性呢?我们先从一个例子说起,假设有一个从账户取钱的远程API(可以是HTTP的,也可以不是),我们暂时用类函数的方式记为:

bool withdraw(account_id, amount)\r\n

withdraw的语义是从account_id对应的账户中扣除amount数额的钱;如果扣除成功则返回true,账户余额减少amount;如果扣除失败则返回false,账户余额不变。值得注意的是:和本地环境相比,我们不能轻易假设分布式环境的可靠性。一种典型的情况是withdraw请求已经被服务器端正确处理,但服务器端的返回结果由于网络等原因被掉丢了,导致客户端无法得知处理结果。如果是在网页上,一些不恰当的设计可能会使用户认为上一次操作失败了,然后刷新页面,这就导致了withdraw被调用两次,账户也被多扣了一次钱。如图1所示:

\"20171007153621950.png\"

图1

这个问题的解决方案一是采用分布式事务,通过引入支持分布式事务的中间件来保证withdraw功能的事务性。分布式事务的优点是对于调用者很简单,复杂性都交给了中间件来管理。缺点则是一方面架构太重量级,容易被绑在特定的中间件上,不利于异构系统的集成;另一方面分布式事务虽然能保证事务的ACID性质,而但却无法提供性能和可用性的保证。

另一种更轻量级的解决方案是幂等设计。我们可以通过一些技巧把withdraw变成幂等的,比如:

int create_ticket() \r\nbool idempotent_withdraw(ticket_id, account_id, amount)\r\n

create_ticket的语义是获取一个服务器端生成的唯一的处理号ticket_id,它将用于标识后续的操作。idempotent_withdraw和withdraw的区别在于关联了一个ticket_id,一个ticket_id表示的操作至多只会被处理一次,每次调用都将返回第一次调用时的处理结果。这样,idempotent_withdraw就符合幂等性了,客户端就可以放心地多次调用。

基于幂等性的解决方案中一个完整的取钱流程被分解成了两个步骤:1.调用create_ticket()获取ticket_id;2.调用idempotent_withdraw(ticket_id, account_id, amount)。虽然create_ticket不是幂等的,但在这种设计下,它对系统状态的影响可以忽略,加上idempotent_withdraw是幂等的,所以任何一步由于网络等原因失败或超时,客户端都可以重试,直到获得结果。如图2所示:

\"20171007153648208.png\"

图2

和分布式事务相比,幂等设计的优势在于它的轻量级,容易适应异构环境,以及性能和可用性方面。在某些性能要求比较高的应用,幂等设计往往是唯一的选择。

HTTP的幂等性

HTTP协议本身是一种面向资源的应用层协议,但对HTTP协议的使用实际上存在着两种不同的方式:一种是RESTful的,它把HTTP当成应用层协议,比较忠实地遵守了HTTP协议的各种规定;另一种是SOA的,它并没有完全把HTTP当成应用层协议,而是把HTTP协议作为了传输层协议,然后在HTTP之上建立了自己的应用层协议。本文所讨论的HTTP幂等性主要针对RESTful风格的,不过正如上一节所看到的那样,幂等性并不属于特定的协议,它是分布式系统的一种特性;所以,不论是SOA还是RESTful的Web API设计都应该考虑幂等性。下面将介绍HTTP GET、DELETE、PUT、POST四种主要方法的语义和幂等性。

HTTP GET方法用于获取资源,不应有副作用,所以是幂等的。比如:GET http://www.bank.com/account/123456,不会改变资源的状态,不论调用一次还是N次都没有副作用。请注意,这里强调的是一次和N次具有相同的副作用,而不是每次GET的结果相同。GET http://www.news.com/latest-news这个HTTP请求可能会每次得到不同的结果,但它本身并没有产生任何副作用,因而是满足幂等性的。

HTTP DELETE方法用于删除资源,有副作用,但它应该满足幂等性。比如:DELETE http://www.forum.com/article/4231,调用一次和N次对系统产生的副作用是相同的,即删掉id为4231的帖子;因此,调用者可以多次调用或刷新页面而不必担心引起错误。

比较容易混淆的是HTTP POST和PUT。POST和PUT的区别容易被简单地误认为“POST表示创建资源,PUT表示更新资源”;而实际上,二者均可用于创建资源,更为本质的差别是在幂等性方面。在HTTP规范中对POST和PUT是这样定义的:

The POST method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line ...... If a resource has been created on the origin server, the response SHOULD be 201 (Created) and contain an entity which describes the status of the request and refers to the new resource, and a Location header.

The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.

POST所对应的URI并非创建的资源本身,而是资源的接收者。比如:POST http://www.forum.com/articles的语义是在http://www.forum.com/articles下创建一篇帖子,HTTP响应中应包含帖子的创建状态以及帖子的URI。两次相同的POST请求会在服务器端创建两份资源,它们具有不同的URI;所以,POST方法不具备幂等性。而PUT所对应的URI是要创建或更新的资源本身。比如:PUT http://www.forum/articles/4231的语义是创建或更新ID为4231的帖子。对同一URI进行多次PUT的副作用和一次PUT是相同的;因此,PUT方法具有幂等性。

在介绍了几种操作的语义和幂等性之后,我们来看看如何通过Web API的形式实现前面所提到的取款功能。很简单,用POST /tickets来实现create_ticket;用PUT /accounts/account_id/ticket_id&amount=xxx来实现idempotent_withdraw。值得注意的是严格来讲amount参数不应该作为URI的一部分,真正的URI应该是/accounts/account_id/ticket_id,而amount应该放在请求的body中。这种模式可以应用于很多场合,比如:论坛网站中防止意外的重复发帖。

总结

上面简单介绍了幂等性的概念,用幂等设计取代分布式事务的方法,以及HTTP主要方法的语义和幂等性特征。其实,如果要追根溯源,幂等性是数学中的一个概念,表达的是N次变换与1次变换的结果相同,有兴趣的读者可以从Wikipedia上进一步了解。

参考

RFC 2616, Hypertext Transfer Protocol -- HTTP/1.1, Method Definitions

The Importance of Idempotence

Stackoverflow - PUT vs POST in REST

 

 

原文地址:http://www.cnblogs.com/weidagang2046/archive/2011/06/04/idempotence.html

', '10', '14', '6', '0', '0', '0', '2017-10-07 15:37:20', '2017-10-07 15:37:20', '1', '1', '1'), ('3', '1', '使用rapid-framework继承jsp页面', '

对于多张网页页面,许多部分都是相同的,应该继承同一个页面,该页面是所有页面的父页面。

后来搜了一下找到一个rapid-framework的东西,由于我使用的是maven,所以引入很简单。

  1. <dependency>  
  2.   <groupId>com.googlecode.rapid-frameworkgroupId>  
  3.   <artifactId>rapid-coreartifactId>  
  4.   <version>4.0.5version>  
  5. dependency>  

父页面

  1. ...  
  2. <%@ taglib prefix=\"rapid\" uri=\"http://www.rapid-framework.org.cn/rapid\" %>  
  3. ...  
  4.  <body>  
  5.       
  6.     <rapid:block name=\"content\">  
  7.         123  
  8.     rapid:block>  
  9. body>  

 定义叫做content的一块,该部分可以让子页面重写。

子页面

  1. <%@ page contentType=\"text/html;charset=UTF-8\" language=\"java\" %>  
  2. <%@ taglib prefix=\"rapid\" uri=\"http://www.rapid-framework.org.cn/rapid\" %>  
  3. <rapid:override name=\"content\">  
  4.     这是内容  
  5. rapid:override>  
  6. <%@ include file=\"base.jsp\"%>  

  •  <%@ include file=\"base.jsp\"%>引入父页面
  • 重写父页面中叫做content的部分。
  • 访问子页面,父页面内部的元素不会显示,代替的是子页面重写的内容。
  • ', '1', '8', '18', '0', '0', '0', '2017-10-07 15:30:17', '2017-10-07 15:34:45', '1', '1', '1'), ('5', '1', 'JDBC常用API和使用', '

    一、JDBC 常用 API

    1、Connection 接口

    方法名称功能描述
    getMetaData()该方法用于返回数据库的元数据的 DatabaseMetaData 对象
    createStatement用于创建一个 Statement 对象来将 SQL 语句发送到数据库
    preparedStatement(String sql)用于创建一个 PreparedStatement 对象来将参数化的SQL语句发送到数据库
    prepareCall(String sql)用于创建一个 CallableStatement 对象来调用数据库存储过程

    2、Statement 接口

    方法名称功能描述
    boolean execute(String sql)用于执行各种 SQL 语句,该方法返回一个 boolean 类型的值。如果为 true,表示所执行的 SQL 语句具备查询结果,可通过 Statement 的getResultSet() 方法查询结果
    int executeUpdate(String sql)用于执行 SQL 中的 insert、update 和 delete 语句,该方法返回一个 int 类型的值,表示影响数据库中的行数
    ResultSet executeQuery(String sql)用于执行 SQL 中的 select 语句(查询,遍历),该方法返回一个表示查询结果的 ResultSet 对象

     execute是executeQuery和executeUpdate的综合.

    通常我们没有必要使用execute方法来执行SQL语句,而是使用 executeQuery 或 executeUpdate 更适合。

    具体可参考:execute、executeUpdate、executeQuery三者的区别

    3、PreparedStatement 接口

    方法名称功能描述
    executeUpdate()在此 PreparedStatement 对象中执行 SQL 语句,该语句必须是一个 DML 语句,或者无返回内容的 SQL 语句,比如 DDL 语句
    executeQuery()在此 PreparedStatement 对象中执行 SQL 语句,该方法返回的是 ResultSet 对象
    setInt(int parameterIndex, int x)将指定的参数设置为 int 值
    setFloat(int parameterIndex, float x)将指定的参数设置为 Float 值
    setString(int parameterIndex, String x)将指定参数设置的给定的 Date 值
    setDate(int parameterIndex, Date x)将指定参数设置给定的 Date 值
    addBatch()将一组参数添加到此 PreparedStatement 对象的批处理命令中
    setCharacterStream(parameterIndex, reader, length)将指定的输入流写入数据库的文本字段
    setBinaryStream(parameterIndex, x, length)将二进制的输入流数据写入到二进制的字段中

    DML 语句:SELECT、UPDATE、INSERT、DELETE

    DLL 语句:CREATE DROP ALERT

    具体参考: sql语句分为三类(DML,DDL,DCL)-介绍

    4、ResultSet 接口

    getString(int columnIndex)用于获取 指定字段的 String 类型的值,参数 columnIndex 代表字段的索引
    getString(String columnName) 用于获取指定字段的 String 类型的值,参数 columnIndex 代表字段名称
    getInt(int columnIndex) 用于获取指定字段的 int 类型的值,参数 columnIndex 代表字段的索引
    getInt(String columnName) 用于获取指定字段的 int 类型的值,参数 columnIndex 代表字段名称
    getDate(int columnIndex) 用于获取指定字段的 Date类型的值,参数 columnIndex 代表字段索引
    getDate(String columnName) 用于获取指定字段的 Date类型的值,参数 columnIndex 代表字段名称
    next() 将游标从当前位置移到下一位置
    absolute(int row)将游标移到此 ResultSet 对象的指定行
    afterLast()将游标移动到此 ResultSet 对象的末尾,即最后一行之后
    beforeFirst()将游标移动到此 ResultSet 对象开头,即第一行之前
    previous()将游标移动到此 ResultSet 对象的上一行
    last()将游标移动到此  Result 对象的最后一行

    二、案例

    1、往数据库里添加数据

    ① 数据表结构如下

    \"2017100715402014.png\"

    ② 代码如下

    1. package com.liuyanzhao;  
    2. import java.sql.Connection;  
    3. import java.sql.Date;  
    4. import java.sql.DriverManager;  
    5. import java.sql.PreparedStatement;  
    6. import java.sql.SQLException;  
    7. import java.text.ParseException;  
    8. import java.text.SimpleDateFormat;  
    9. /* 
    10.  * @author LiuYanzhao 
    11.  */  
    12. public class Demo1 {  
    13.     private static final String URL = \"jdbc:mysql://127.0.0.1:3306/jdbc_study?useUnicode=true&characterEncoding=utf8\";  
    14.     private static final String USER = \"root\";  
    15.     private static final String PASSWORD = \"\";  
    16.     public static Connection conn = null;  
    17.     public static void main(String[] args) throws ClassNotFoundException, SQLException, ParseException {  
    18.         //1、加载驱动,需要提前把 jar 包添加到 classpath 中  
    19.         Class.forName(\"com.mysql.jdbc.Driver\");  
    20.         //2、创建应用程序与数据库连接的 Connection 对象  
    21.         conn = DriverManager.getConnection(URL, USER, PASSWORD);  
    22.         //3、要执行的 sql 语句:name,password,email,status通过占位符填数,create_date 自动为当前时间  
    23.         String sql = \" INSERT INTO users\"+  
    24.                      \"(name,password,birthday,email,create_date,status) \"+  
    25.                      \"VALUES(\"+  
    26.                      \"?,?,?,?,current_date(),?)\";  
    27.         //4、创建执行 SQL 语句的 PreparedStatement 对象  
    28.         PreparedStatement ptmt = conn.prepareStatement(sql);  
    29.         ptmt.setString(1\"小美\");  
    30.         ptmt.setString(2\"123456\");  
    31.         ptmt.setDate(3,new Date((new SimpleDateFormat(\"yyyy-MM-dd\").parse(\"2011-10-1\")).getTime()) );  
    32.         ptmt.setString(4\"xiaomei@126.com\");  
    33.         ptmt.setInt(51);  
    34.         //5、真正执行 sql 语句,并返回影响的行数  
    35.         int x = ptmt.executeUpdate();  
    36.         System.out.println(\"影响行数:\" + x); //返回1  
    37.     }  
    38. }  

    ③运行结果

    上面的操作,将在 jdbc_study 数据库的 users 表中添加一条记录,终端显示 1

    2、查询(打印)数据库信息

    ①数据表如下

    \"20171007154127663.png\"

    ②代码如下

    1. package com.liuyanzhao;  
    2. import java.sql.Connection;  
    3. import java.sql.Date;  
    4. import java.sql.DriverManager;  
    5. import java.sql.PreparedStatement;  
    6. import java.sql.ResultSet;  
    7. import java.sql.SQLException;  
    8. import java.text.ParseException;  
    9. import java.text.SimpleDateFormat;  
    10. /* 
    11.  * @author LiuYanzhao 
    12.  */  
    13. public class Demo2 {  
    14.     private static final String URL = \"jdbc:mysql://127.0.0.1:3306/jdbc_study?useUnicode=true&characterEncoding=utf8\";  
    15.     private static final String USER = \"root\";  
    16.     private static final String PASSWORD = \"\";  
    17.     public static Connection conn = null;  
    18.     public static void main(String[] args) throws ClassNotFoundException, SQLException, ParseException {  
    19.         //1、加载驱动,需要提前把 jar 包添加到 classpath 中  
    20.         Class.forName(\"com.mysql.jdbc.Driver\");  
    21.         //2、创建应用程序与数据库连接的 Connection 对象  
    22.         conn = DriverManager.getConnection(URL, USER, PASSWORD);  
    23.         //3、要执行的 sql 语句:name,password,email,satic 通过占位符填数,create_date 自动为当前时间  
    24.         String sql = \" SELECT * FROM users\";  
    25.         //4、创建执行 SQL 语句的 PreparedStatement 对象  
    26.         PreparedStatement ptmt = conn.prepareStatement(sql);  
    27.         //5、真正执行 sql 语句,并返回影响的 ResultSet  
    28.         ResultSet rs = ptmt.executeQuery();  
    29.         //6、打印 ResultSet 数据集  
    30.         while(rs.next()) {  
    31.             System.out.print(rs.getInt(\"id\")+\" \");  
    32.             System.out.print(rs.getString(\"name\")+\" \");  
    33.             System.out.print(rs.getString(\"password\")+\" \");  
    34.             System.out.print(rs.getDate(\"birthday\")+\" \");  
    35.             System.out.print(rs.getString(\"email\")+\" \");  
    36.             System.out.print(rs.getDate(\"create_date\")+\" \");  
    37.             System.out.print(rs.getInt(\"status\")+\" \");  
    38.             System.out.println();  
    39.         }  
    40.     }  
    41. }  

    ③ 运行结果

    控制台上可以看到如下界面

    \"20171007154222212.jpg\"

    ', '1', '8', '20', '0', '0', '0', '2017-10-07 15:42:43', '2017-10-07 15:43:46', '1', '1', '1'), ('6', '1', 'Hibernate 简单的CURD操作', '

    一、单表操作 CURD 实例

     

    二、代码实现

    StudentTest.java

    1. @Test
    2. public void testSaveStudnets() {
    3.     //生成学生对象
    4.     Student s = new Student();
    5.     s.setName(\"陶渊明\");
    6.     s.setSex(1);
    7.     s.setBirthday(new Date());
    8.     Address address = new Address(\"332000\",\"13512345678\",\"江西九江\");
    9.     s.setAddress(address);
    10.     session.save(s);//保存对象进入数据库
    11. }
    12. @Test
    13. public void testGetStudents() {
    14.     Student s = (Student) session.get(Student.class1);
    15.     System.out.println(s);
    16. }
    17. @Test
    18. public void testLoadStudents() {
    19.     Student s = (Student) session.load(Student.class1);
    20.     System.out.println(s);
    21. }
    22. @Test
    23. public void testUpdateStudents() {
    24.     Student s = (Student) session.get(Student.class1);
    25.     s.setName(\"五柳先生\");
    26.     session.update(s);
    27. }
    28. @Test
    29. public void testDeleteStudents() {
    30.     Student s = (Student) session.get(Student.class1);
    31.     session.delete(s);
    32. }

     

    三、get 和 load 的区别

    区别一、

    区别二、

    p


    ', '1', '9', '21', '0', '0', '0', '2017-10-07 15:49:54', '2017-10-07 15:49:54', '1', '1', '1'), ('7', '1', 'Hibernate 基本类型', '

    这里介绍几个特殊的 Hibernate 类型和属性

    一、日期类型

    我们的 Student 类有一个成员变量 birthday

    1. private Date birthday;//出生日期

    在我们的 Studnet.hbm.xml 对应的是自动生成的 属性

    1. <property name=\"birthday\" type=\"java.util.Date\">
    2.     <column name=\"BIRTHDAY\" />
    3. </property>

    type 表示该字段的类型,不同的类型,会让数据表的字段类型也不同

    type(映射类型)描述
    java.util.Date年月日时分秒(2017-07-25 18:20:12)
    date年月日(2017-07-25)
    time时分秒(18:20:12)
    timestamp年月日时分秒(2017-07-25 18:20:12)

    二、对象类型

    我们这里使用使用 Blob 类型存照片

    1、Student 类中 成员变量如下

    1. private int sid;//学号
    2. private String name;//姓名
    3. private int sex;//性别
    4. private Date birthday;//出生日期
    5. private String address;//地址
    6. private Blob picture;//照片   

    2、然后删除原来的 Student.hbm.xml  文件,重新生成

    3、在测试类添加 testWriteBlob方法

    1. @Test
    2. public void testWriteBlob() throws Exception {
    3.     Student s = new Student(1,\"杜甫\",1,new Date(),\"四川\");
    4.     //先获得照片文件
    5.     File f = new File(\"d:\"+File.separator+\"boy.jpg\");
    6.     //获得照片文件的输入流
    7.     InputStream input = new FileInputStream(f);
    8.     //创建Blob对象
    9.     Blob image = Hibernate.getLobCreator(session).createBlob(input,input.available());
    10.     //设置照片属性
    11.     s.setPicture(image);
    12.     session.save(s);
    13. }

    注意:这里 Blob 是 java.sql.Blob 包下

    4、然后使用 Junit Test 运行 testWriteBlob 方法

    \"2017100715505630.png\"

    5、为了证明是否真的是写入数据库中,我们重新把照片读出来

    在测试类中添加 testReadBlob 方法

    1. @Test
    2. public void testReadBlob() throws Exception {
    3.     Student s = (Student)session.get(Student.class1);
    4.     //获得Blob对象
    5.     Blob image = s.getPicture();
    6.     //获得照片的输入流
    7.     InputStream input = image.getBinaryStream();
    8.     //创建输出流
    9.     File f = new File(\"d:\"+File.separator+\"dest.jpg\");
    10.     //获得输出流
    11.     OutputStream output = new FileOutputStream(f);
    12.     //创建缓冲区
    13.     byte[] buff = new byte[input.available()];
    14.     input.read(buff);
    15.     output.write(buff);
    16.     input.close();
    17.     output.close();
    18. }

    6、使用 Junit Test 运行 testReadBlob 方法

    我们可以看到 D 盘,已经生成了一张新照片,dest.jpg

     

    \"20171007155038568.png\"

    三、组件属性

    实体类中某个属性属于用户自定义的类的对象,下面我们通过一个例子来解释

    1、实体类 Student.java 成员变量如下

    1. private int sid;//学号
    2. private String name;//姓名
    3. private int sex;//性别
    4. private Date birthday;//出生日期
    5. //private String address;//地址
    6. private Blob picture;//照片   
    7. private Address address; //地址

    我们这里把之前的 String 类型的 address 改成 Address 类型

    2、Address 类 部分代码如下

    1. private String postcode;//邮编
    2. private String phone;//手机
    3. private String address;//地址
    4. public Address(String postcode, String phone, String address) {
    5.         this.postcode = postcode;
    6.         this.phone = phone;
    7.         this.address = address;
    8. }

    3、修改 Student.hbm.xml

    将之前的

    1. <property name=\"address\" type=\"java.lang.String\">
    2.          <column name=\"ADDRESS\" />
    3. </property>

    改成

    1. <component name=\"address\" class=\"Address\">
    2.     <property name=\"postcode\" column=\"POSTCODE\"/>
    3.     <property name=\"phone\" column=\"PHONE\"/>
    4.     <property name=\"address\" column=\"ADDRESS\"/>
    5. </component>

    注意:单词不要打错

    4、修改 测试类 testSaveStudent 代码

    1. @Test
    2.    public void testSaveStudnets() {
    3.     //生成学生对象
    4.     Student s = new Student();
    5.     s.setName(\"陶渊明\");
    6.     s.setSex(1);
    7.     s.setBirthday(new Date());
    8.     //s.setAddress(\"江西九江\");
    9.     Address address = new Address(\"332000\",\"13512345678\",\"江西九江\");
    10.     s.setAddress(address);
    11.     session.save(s);//保存对象进入数据库
    12.    }

    5、修改 hibernate.cfg.xml 里的创建表的策略

    因为要修改表结构,暂且把 update 改成 create

    6、用 Junit Test 运行 testSaveStudent 方法

    运行成功,查看数据表

    \"2017100715512437.jpg\"

    查看 表结构

    \"20171007155135604.jpg\"

     

    ', '1', '9', '21', '0', '0', '0', '2017-10-07 15:51:57', '2017-10-07 15:51:57', '1', '1', '1'), ('8', '1', 'Mybatis高级映射多对多查询', '

    紧接着上一篇文章:Mybatis高级映射一对多查询 写

    一、开发准备

    1、新建数据表(四张表)和添加测试数据

    1. DROP TABLE IF EXISTS `items`;
    2. DROP TABLE IF EXISTS `orders`;
    3. DROP TABLE IF EXISTS `user`;
    4. DROP TABLE IF EXISTS `orderdetail`;
    5. /*items是商品表*/
    6. CREATE TABLE `items` (
    7.   `id` INT(11) NOT NULL AUTO_INCREMENT,
    8.   `nameVARCHAR(32) NOT NULL COMMENT \'商品名称\',
    9.   `price` FLOAT(10,1) NOT NULL COMMENT \'商品定价\',
    10.   `detail` TEXT COMMENT \'商品描述\',
    11.   `pic` VARCHAR(64) DEFAULT NULL COMMENT \'商品图片\',
    12.   `createtime` DATETIME NOT NULL COMMENT \'生产日期\',
    13.   PRIMARY KEY (`id`)
    14. ) ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
    15. /*user是用户表*/
    16. CREATE TABLE `user` (
    17.   `id` INT(11) NOT NULL AUTO_INCREMENT,
    18.   `username` VARCHAR(32) NOT NULL COMMENT \'用户名称\',
    19.   `birthday` DATE DEFAULT NULL COMMENT \'生日\',
    20.   `gender` CHAR(1) DEFAULT NULL COMMENT \'性别\',
    21.   `address` VARCHAR(256) DEFAULT NULL COMMENT \'地址\',
    22.   PRIMARY KEY (`id`)
    23. ) ENGINE=INNODB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8;
    24. /*orders是订单表*/
    25. CREATE TABLE `orders` (
    26.   `id` INT(11) NOT NULL AUTO_INCREMENT,
    27.   `user_id` INT(11) NOT NULL COMMENT \'下单用户id\',
    28.   `number` VARCHAR(32) NOT NULL COMMENT \'订单号\',
    29.   `createtime` DATETIME NOT NULL COMMENT \'创建订单时间\',
    30.   `note` VARCHAR(100) DEFAULT NULL COMMENT \'备注\',
    31.   PRIMARY KEY (`id`),
    32.   KEY `FK_orders_1` (`user_id`),
    33.   CONSTRAINT `FK_orders_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
    34. ) ENGINE=INNODB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
    35. /*orderdetail是订单明细表*/
    36. DROP TABLE IF EXISTS orderdetail;
    37. CREATE TABLE `orderdetail` (
    38.   `id` INT(11) NOT NULL AUTO_INCREMENT,
    39.   `orders_id` INT(11) NOT NULL COMMENT \'订单id\',
    40.   `items_id` INT(11) NOT NULL COMMENT \'商品id\',
    41.   `items_num` INT(11) DEFAULT NULL COMMENT \'商品购买数量\',
    42.   PRIMARY KEY (`id`),
    43.   KEY `FK_orderdetail_1` (`orders_id`),
    44.   KEY `FK_orderdetail_2` (`items_id`),
    45.   CONSTRAINT `FK_orderdetail_1` FOREIGN KEY (`orders_id`) REFERENCES `orders` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
    46.   CONSTRAINT `FK_orderdetail_2` FOREIGN KEY (`items_id`) REFERENCES `items` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
    47. ) ENGINE=INNODB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

    为了测试,我这里随便填了些数据

    \"20171007155334244.png\"

    \"20171007155349805.png\"


    \"20171007155410485.png\"


    \"20171007155450773.png\"

    2、思路分析

    \"20171007155545742.png\"

    订单项和订单明细是一对多的关系,所以本文主要来查询订单表,然后关联订单明细表,这样就有一对多的问题出来了。

    因为多对多比较复杂,总公共有四张表,我们先来分析一下思路:

    1、将用户信息映射到User中;

    2、在User类中添加订单列表属性ListordersList,将用户创建的订单映射到ordersList中;

    3、在Orders中添加订单明细列表属性ListorderDetails,将订单的明细映射到orderDetails中;

    4、在OrderDetail中添加Items属性,将订单明细所对应的商品映射到Items中。

    经过这样分析后,感觉虽然有点复杂,但是好像不是很难的样子,映射的方法也跟前面的一样,只不过这里表有点多,关系有点复杂。下面来写映射文件:

    1. <select id=\"findUserAndItemsResultMap\" resultMap=\"UserAndItemsResultMap\">
    2.     SELECT
    3.       orders.*,
    4.       user.`username`,
    5.       user.`sex`,
    6.       user.`address`,
    7.       orderdetail.`id` orderdetail_id,
    8.       orderdetail.`items_id`,
    9.       orderdetail.`items_num`,
    10.       orderdetail.`orders_id`,
    11.       items.`name` items_name,
    12.       items.`detail` items_detail,
    13.       items.`price` items_price
    14.     FROM
    15.       orders,
    16.       USER,
    17.       orderdetail,
    18.       items
    19.     WHERE orders.`user_id`=user.`id` AND orders.`id` = orderdetail.`orders_id` AND orderdetail.`items_id`=items.`id`
    20. select>

    我们先看一下查询结果:

    \"2017100717420134.png\"


    二、代码实现

    1、四个持久化类

    ① User.java

    1. package com.liuyanzhao.mybatis.po;
    2. import java.util.Date;
    3. import java.util.List;
    4. /**
    5.  * 用户的持久类
    6.  */
    7. public class User {
    8.     private int id; //编号
    9.     private String username; //用户名
    10.     private String gender; //性别
    11.     private Date birthday; //生日
    12.     private String address; //地址
    13.     public List getOrdersList() {
    14.         return ordersList;
    15.     }
    16.     public void setOrdersList(List ordersList) {
    17.         this.ordersList = ordersList;
    18.     }
    19.     //用户创建的订单列表
    20.     private List ordersList;
    21.     public int getId() {
    22.         return id;
    23.     }
    24.     public void setId(int id) {
    25.         this.id = id;
    26.     }
    27.     public String getUsername() {
    28.         return username;
    29.     }
    30.     public void setUsername(String username) {
    31.         this.username = username;
    32.     }
    33.     public String getGender() {
    34.         return gender;
    35.     }
    36.     public void setGender(String gender) {
    37.         this.gender = gender;
    38.     }
    39.     public Date getBirthday() {
    40.         return birthday;
    41.     }
    42.     public void setBirthday(Date birthday) {
    43.         this.birthday = birthday;
    44.     }
    45.     public String getAddress() {
    46.         return address;
    47.     }
    48.     public void setAddress(String address) {
    49.         this.address = address;
    50.     }
    51. }

    注意:需要在用户表中加入 订单列表

     

    ② Items.java

    1. package com.liuyanzhao.mybatis.po;
    2. import java.util.Date;
    3. /**
    4.  * 商品的持久类
    5.  */
    6. public class Items {
    7.     private int id;
    8.     private String name;
    9.     private double price;
    10.     private String detail;
    11.     private String pic;
    12.     private Date createTime;
    13.     public int getId() {
    14.         return id;
    15.     }
    16.     public void setId(int id) {
    17.         this.id = id;
    18.     }
    19.     public String getName() {
    20.         return name;
    21.     }
    22.     public void setName(String name) {
    23.         this.name = name;
    24.     }
    25.     public double getPrice() {
    26.         return price;
    27.     }
    28.     public void setPrice(double price) {
    29.         this.price = price;
    30.     }
    31.     public String getDetail() {
    32.         return detail;
    33.     }
    34.     public void setDetail(String detail) {
    35.         this.detail = detail;
    36.     }
    37.     public String getPic() {
    38.         return pic;
    39.     }
    40.     public void setPic(String pic) {
    41.         this.pic = pic;
    42.     }
    43.     public Date getCreateTime() {
    44.         return createTime;
    45.     }
    46.     public void setCreateTime(Date createTime) {
    47.         this.createTime = createTime;
    48.     }
    49. }

     

    ③ Orders.java

    1. package com.liuyanzhao.mybatis.po;
    2. import java.util.Date;
    3. import java.util.List;
    4. /**
    5.  * 订单的持久类和扩展类
    6.  */
    7. public class Orders {
    8.     private int id;
    9.     private int userId;
    10.     private String number;
    11.     private Date createTime;
    12.     private String note;
    13.     //订单明细
    14.     private List orderdetails;
    15.     public List getOrderdetails() {
    16.         return orderdetails;
    17.     }
    18.     public void setOrderdetails(List orderdetails) {
    19.         this.orderdetails = orderdetails;
    20.     }
    21.     public int getId() {
    22.         return id;
    23.     }
    24.     public void setId(int id) {
    25.         this.id = id;
    26.     }
    27.     public int getUserId() {
    28.         return userId;
    29.     }
    30.     public void setUserId(int userId) {
    31.         this.userId = userId;
    32.     }
    33.     public String getNumber() {
    34.         return number;
    35.     }
    36.     public void setNumber(String number) {
    37.         this.number = number;
    38.     }
    39.     public Date getCreateTime() {
    40.         return createTime;
    41.     }
    42.     public void setCreateTime(Date createTime) {
    43.         this.createTime = createTime;
    44.     }
    45.     public String getNote() {
    46.         return note;
    47.     }
    48.     public void setNote(String note) {
    49.         this.note = note;
    50.     }
    51. }

    注意:订单列表中,需要订单的详细信息,不需要用户信息

     

    ④ Orderdetail.java

    1. package com.liuyanzhao.mybatis.po;
    2. /**
    3.  * 订单明细的持久类
    4.  */
    5. public class Orderdetail {
    6.     private int id;
    7.     private int ordersId;
    8.     private int itemsId;
    9.     private int itemsNum;
    10.     //明细对应的商品信息
    11.     private Items items;
    12.     public Items getItems() {
    13.         return items;
    14.     }
    15.     public void setItems(Items items) {
    16.         this.items = items;
    17.     }
    18.     public int getId() {
    19.         return id;
    20.     }
    21.     public void setId(int id) {
    22.         this.id = id;
    23.     }
    24.     public int getOrdersId() {
    25.         return ordersId;
    26.     }
    27.     public void setOrdersId(int ordersId) {
    28.         this.ordersId = ordersId;
    29.     }
    30.     public int getItemsId() {
    31.         return itemsId;
    32.     }
    33.     public void setItemsId(int itemsId) {
    34.         this.itemsId = itemsId;
    35.     }
    36.     public int getItemsNum() {
    37.         return itemsNum;
    38.     }
    39.     public void setItemsNum(int itemsNum) {
    40.         this.itemsNum = itemsNum;
    41.     }
    42. }

    注意:订单明细里,需要 商品信息

     

    2、订单代理 即mapper.java

    OrdersMapperCustom.java

    1. package com.liuyanzhao.mybatis.mapper;
    2. import com.liuyanzhao.mybatis.po.User;
    3. import java.util.List;
    4. /**
    5.  * 订单 mapper
    6.  */
    7. public interface OrdersMapperCustom {
    8.     //查询用户购买的商品信息
    9.     public List findUserAndItemsResultMap() throws Exception;
    10. }

     

    3、OrdersMapperCustom.xml    映射文件

    1. xml version=\"1.0\" encoding=\"UTF-8\" ?>
    2.         PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"
    3.         \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">
    4. <mapper namespace=\"com.liuyanzhao.mybatis.mapper.OrdersMapperCustom\">
    5.     
    6.     <resultMap id=\"UserAndItemsResultMap\" type=\"com.liuyanzhao.mybatis.po.User\">
    7.         
    8.         <id column=\"user_id\" property=\"id\">id>
    9.         <result column=\"username\" property=\"username\">result>
    10.         <result column=\"gender\" property=\"gender\">result>
    11.         <result column=\"address\" property=\"address\">result>
    12.         
    13.         
    14.         <collection property=\"ordersList\" ofType=\"com.liuyanzhao.mybatis.po.Orders\">
    15.             <id column=\"id\" property=\"id\">id>
    16.             <result column=\"user_id\" property=\"userId\">result>
    17.             <result column=\"number\" property=\"number\">result>
    18.             <result column=\"createtime\" property=\"createTime\">result>
    19.             <result column=\"node\" property=\"node\">result>
    20.             
    21.             
    22.             <collection property=\"orderdetails\" ofType=\"com.liuyanzhao.mybatis.po.Orderdetail\">
    23.                 <id column=\"orderdetail_id\" property=\"id\">id>
    24.                 <result column=\"items_id\" property=\"itemsId\">result>
    25.                 <result column=\"items_num\" property=\"itemsNum\">result>
    26.                 <result column=\"orders_id\" property=\"ordersId\">result>
    27.                 
    28.                 
    29.                 <association property=\"items\" javaType=\"com.liuyanzhao.mybatis.po.Items\">
    30.                     <id column=\"items_id\" property=\"id\">id>
    31.                     <result column=\"items_name\" property=\"name\">result>
    32.                     <result column=\"items_price\" property=\"price\">result>
    33.                     <result column=\"items_detail\" property=\"detail\">result>
    34.                 association>
    35.             collection>
    36.         collection>
    37.     resultMap>
    38.     <select id=\"findUserAndItemsResultMap\" resultMap=\"UserAndItemsResultMap\">
    39.         SELECT
    40.         orders.*,
    41.         user.username,
    42.         user.gender,
    43.         user.address,
    44.         orderdetail.id orderdetail_id,
    45.         orderdetail.items_id,
    46.         orderdetail.items_num,
    47.         orderdetail.orders_id,
    48.         items.name items_name,
    49.         items.detail items_detail,
    50.         items.price items_price
    51.         FROM
    52.         orders,
    53.         user,
    54.         orderdetail,
    55.         items
    56.         WHERE orders.user_id=user.id AND orders.id = orderdetail.orders_id AND orderdetail.items_id=items.id
    57.     select>
    58. mapper>

     

    4、测试类 OrderMapperCustomTest.java

    1. package com.liuyanzhao.mybatis.test;
    2. import com.liuyanzhao.mybatis.mapper.OrdersMapperCustom;
    3. import com.liuyanzhao.mybatis.po.User;
    4. import org.apache.ibatis.io.Resources;
    5. import org.apache.ibatis.session.SqlSession;
    6. import org.apache.ibatis.session.SqlSessionFactory;
    7. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    8. import org.junit.Before;
    9. import org.junit.Test;
    10. import java.io.InputStream;
    11. import java.util.List;
    12. /**
    13.  * Created by Liu_Yanzhao on 2017/8/12.
    14.  */
    15. public class OrderMapperCustomTest {
    16.     SqlSessionFactory sqlSessionFactory;
    17.     @Before
    18.     public void setUp() throws Exception {
    19.         String resource = \"Configuration.xml\";
    20.         InputStream inputStream = Resources.getResourceAsStream(resource);
    21.         sqlSessionFactory = new SqlSessionFactoryBuilder()
    22.             .build(inputStream);
    23.     }
    24.     @Test
    25.     public void testFindUserAndItemsResultMap() throws Exception {
    26.         SqlSession sqlSession = sqlSessionFactory.openSession();
    27.         //创建代理对象
    28.         OrdersMapperCustom ordersMapperCustom = sqlSession.getMapper(OrdersMapperCustom.class);
    29.         //调用mapper对象
    30.         List list = ordersMapperCustom.findUserAndItemsResultMap();
    31.         System.out.println(list);
    32.         //释放资源
    33.         sqlSession.close();
    34.     }
    35. }

    还有其他文件就不补充了,如 mybatis 全局配置文件

     

    小结

    这样多对多的映射就搞定了。不过还有个问题,就是这里多对多的查询会把所有关联的表的信息都查询出来,然后放到pojo中的对应的List或者某个类中,所以即使我只查了个用户信息,但是这个用户里包含了订单,订单项,商品等信息,感觉装的有点多,好像有时候并不需要这么多冗余的数据出来,但是如果用resultType的话查询出来的字段必须对应pojo中的属性,如果有List等,需要手动装入才行。所以下面总结一下对于这种查询数据比较多的时候,resultType和resultMap各有什么作用?

    1. 比如我们只需要将查询用户购买的商品信息明细清单(如用户名、用户地址、购买商品名称、购买商品时间、购买商品数量),那么我们完全不需要其他的信息,这个时候就没必要使用resultMap将所有的信息都搞出来,我们可以自己定义一个pojo,包含我们需要的字段即可,然后查询语句只查询我们需要的字段,这样使用resultType会方便很多。
    2. 如果我们需要查询该用户的所有详细信息,比如用户点击该用户或者鼠标放上去,会出来跟该用户相关的订单啊,订单明细啊,商品啊之类的,然后我们要点进去看下详细情况的时候,那就需要使用resultMap了,必须将所有信息都装到这个User中,然后具体啥信息再从User中取,很好理解。
    3. 总结一点:使用resultMap是针对那些对查询结果映射有特殊要求的功能,,比如特殊要求映射成list中包括多个list。否则使用resultType比较直接。

    到这里,mybatis的多对多映射就总结完了。 

    ', '1', '9', '13', '6', '2', '0', '2017-10-07 15:56:00', '2017-10-07 17:42:03', '1', '1', '1'); INSERT INTO `article` VALUES ('9', '1', 'Servlet 实现验证码', '

    分析

    生成图片实现类

    ImageServlet 类

    ① 定义BufferedImage 对象

    ② 获得 Graphics 对象

    ③ 通过 Random 产生随机验证码信息

    ④ 使用 Graphics 绘制图片

    ⑤ 记录验证码信息到 session 中

    ⑥ 使用 ImageIO 输出图片

    代码实现

    1、新建 web 项目 CheckCode,在 WebContent 下新建 index.jsp

    index.jsp

    1. <%@ page language=\"java\" contentType=\"text/html; charset=UTF-8\"
    2.     pageEncoding=\"UTF-8\"%>
    3. <!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">
    4. <html>
    5. <head>
    6. <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">
    7. <title>Insert title here</title>
    8. <script type=\"text/javascript\">
    9.     function reloadCode() {
    10.         //传个时间参数,防止缓存
    11.         var time = new Date().getTime();
    12.         document.getElementById(\"imageCode\").src=\"<%=request.getContextPath()%>/ImageServlet?d=\"+time;
    13.     }
    14. </script>
    15. </head>
    16. <body>
    17. <form action=\"<%=request.getContextPath()%>/LoginServlet\" method=\"post\">
    18.     验证码:<input type=\"text\" name=\"checkcode\" />
    19.     <img id=\"imageCode\" src=\"<%=request.getContextPath()%>/ImageServlet\" alt=\"验证码\" />
    20.     <a href=\"javascript:reloadCode();\">看不清楚</a> <br />
    21.     <input type=\"submit\"  value=\"提交\"/>
    22.     </form>
    23. </body>
    24. </html>

    2、在 src 下新建 com.liuyanzhao 包,然后分别新建用于动态画图的 ImageServlet 类和用于判断验证码是否正确的 LoginServlet 类

    ImageServlet.java

    1. package com.liuyanzhao;
    2. import java.awt.Color;
    3. import java.awt.Graphics;
    4. import java.awt.image.BufferedImage;
    5. import java.io.IOException;
    6. import java.util.Random;
    7. import javax.imageio.ImageIO;
    8. import javax.servlet.ServletException;
    9. import javax.servlet.http.HttpServlet;
    10. import javax.servlet.http.HttpServletRequest;
    11. import javax.servlet.http.HttpServletResponse;
    12. /*
    13.  * @author LiuYanzhao
    14.  */
    15. public class ImageServlet extends HttpServlet {
    16.     @Override
    17.     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    18.         BufferedImage bi = new BufferedImage(6822, BufferedImage.TYPE_INT_RGB);
    19.         Graphics g = bi.getGraphics();
    20.         Color c = new Color(200,155,255);
    21.         g.setColor(c);
    22.         g.fillRect(006822);
    23.         char[] ch = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\".toCharArray();
    24.         Random r = new Random();
    25.         int len = ch.length,index;
    26.         StringBuffer sb = new StringBuffer();
    27.         for(int i=0;i<4;i++) {
    28.             index = r.nextInt(len);
    29.             g.setColor(new Color(r.nextInt(88),r.nextInt(188),r.nextInt(255)));
    30.             g.drawString(ch[index]+\"\", (i*15)+318);
    31.             sb.append(ch[index]);
    32.         }
    33.         request.getSession().setAttribute(\"piccode\", sb.toString());
    34.         ImageIO.write(bi, \"JPG\", response.getOutputStream());
    35.     }
    36.     @Override
    37.     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    38.         doGet(request, response);
    39.     }
    40. }

    LoginServlet.java

    1. package com.liuyanzhao;
    2. import java.io.IOException;
    3. import java.io.PrintWriter;
    4. import javax.servlet.ServletException;
    5. import javax.servlet.http.HttpServlet;
    6. import javax.servlet.http.HttpServletRequest;
    7. import javax.servlet.http.HttpServletResponse;
    8. /*
    9.  * @author LiuYanzhao
    10.  */
    11. public class LoginServlet extends HttpServlet{
    12.     @Override
    13.     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    14.         String piccode = (String)request.getSession().getAttribute(\"piccode\");
    15.         String checkcode = request.getParameter(\"checkcode\");
    16.         checkcode = checkcode.toUpperCase();//不区分大小写
    17.         response.setContentType(\"text/html;charset=utf-8\");
    18.         PrintWriter out = response.getWriter();
    19.         if(piccode.equals(checkcode)) {
    20.             out.println(\"验证码输入正确\");
    21.         } else {
    22.             out.println(\"验证码输入错误\");
    23.         }
    24.         out.flush();
    25.         out.close();
    26.     }
    27.     @Override
    28.     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    29.         doPost(request, response);
    30.     }
    31. }

    3、在 web.xml 里添加 Servlet 映射

    1. <servlet>
    2.      <servlet-name>ImageServlet</servlet-name>
    3.      <servlet-class>com.liuyanzhao.ImageServlet</servlet-class>
    4.  </servlet>
    5.  <servlet-mapping>
    6.      <!-- 映射为 ImageServlet -->
    7.      <servlet-name>ImageServlet</servlet-name>
    8.      <url-pattern>/ImageServlet</url-pattern>
    9.  </servlet-mapping>
    10.  <servlet>
    11.      <servlet-name>LoginServlet</servlet-name>
    12.      <servlet-class>com.liuyanzhao.LoginServlet</servlet-class>
    13.  </servlet>
    14.  <servlet-mapping>
    15.      <!-- 映射为 IndexServlet -->
    16.      <servlet-name>LoginServlet</servlet-name>
    17.      <url-pattern>/LoginServlet</url-pattern>
    18.  </servlet-mapping>

    4、运行 Tomcat 服务器,打开浏览器,访问 http://localhost:8080/CheckCode

    效果如下

    \"20171007155723341.png\"

    验证码不区分大小写,点击 “看不清楚”可以更换

    ', '1', '8', '19', '2', '2', '0', '2017-10-07 15:57:38', '2017-10-07 15:57:38', '1', '1', '1'), ('10', '1', 'Integer与int的种种比较你知道多少?', '

    如果面试官问Integer与int的区别:估计大多数人只会说道两点,Ingeter是int的包装类,int的初值为0,Ingeter的初值为null。但是如果面试官再问一下Integer a = 1;int b = 1; a==b为true还是为false?估计就有一部分人答不出来了,如果再问一下其他的,估计更多的人会头脑一片混乱。所以我对它们进行了总结,希望对大家有帮助。

    首先看代码:

    1. /* 
    2.  * @author LiuYanzhao
    3.  *
    4.  */
    5. public class Test {
    6.     public static void main(String[] args) {
    7.         int i = 128;
    8.         Integer i2 = 128;
    9.         Integer i3 = new Integer(128);
    10.         //Integer会自动拆箱为int,所以为true
    11.         System.out.println(i == i2);//true
    12.         System.out.println(i == i3);//true
    13.         //java在编译的时候,被翻译成-> Integer i5 = Integer.valueOf(127);
    14.         Integer i5 = 127;
    15.         Integer i6 = 127;
    16.         System.out.println(i5 == i6);//true
    17.         Integer i7 = 128;
    18.         Integer i8 = 128;
    19.         System.out.println(i7 == i8);//false
    20.         Integer i9 = new Integer(127);
    21.         Integer i10 = 127;
    22.         System.out.println(i9 == i10); //false
    23.         Integer i11 = new Integer(128);
    24.         Integer i12 = new Integer(123);
    25.         System.out.println(i11 == i12);  //false
    26.     }
    27. }

    详解如下:

    首先,13行和14行输出结果都为true,因为Integer和int比都会自动拆箱(jdk1.5以上)。

    19行的结果为true,而24行则为false,很多人都不懂为什么。其实java在编译Integer i5 = 127的时候,被翻译成-> Integer i5 = Integer.valueOf(127);所以关键就是看valueOf()函数了。只要看看valueOf()函数的源码就会明白了。JDK源码的valueOf函数式这样的:

    1. public static Integer valueOf(int i) {
    2.     assert IntegerCache.high >= 127;
    3.     if (i >= IntegerCache.low && i <= IntegerCache.high)
    4.         return IntegerCache.cache[i + (-IntegerCache.low)];
    5.     return new Integer(i);
    6. }

    看一下源码大家都会明白,对于-128到127之间的数,会进行缓存,Integer i5 = 127时,会将127进行缓存,下次再写Integer i6 = 127时,就会直接从缓存中取,就不会new了。所以22行的结果为true,而25行为false。

    对于29行和34行,因为对象不一样,所以为false。

    我对于以上的情况总结如下:

    ① 无论如何,Integer与new Integer不会相等。不会经历拆箱过程,i3的引用指向堆,而i4指向专门存放他的内存(常量池),他们的内存地址不一样,所以为false

    ② 两个都是非new出来的Integer,如果数在-128到127之间,则是true,否则为false

    java在编译Integer i2 = 128的时候,被翻译成-> Integer i2 = Integer.valueOf(128);而valueOf()函数会对-128到127之间的数进行缓存

    ③ 个都是new出来的,都为false

    ④ int和Integer(无论new否)比,都为true,因为会把Integer自动拆箱为int再去比

     

    参考:http://www.cnblogs.com/liuling/archive/2013/05/05/intAndInteger.html


    ', '1', '2', '1', '0', '1', '1', '2017-10-07 15:58:51', '2017-10-07 15:58:51', '1', '1', '1'), ('11', '1', 'Java如何让程序一直运行,不停止', '

    如何让程序一直运行,很容易实现,只需要一直等待输出即可啦

    1. import java.util.Scanner;
    2. /*
    3.  * @author LiuYanzhao
    4.  */
    5. public class Test {
    6.     private static final String OPERATION_EXIT = \"EXIT\";
    7.     public static void main(String[] args) {
    8.         System.out.println(\"请开始您的输入,EXIT/E 退出\");
    9.         //怎么让程序一直运行
    10.         Scanner scan = new Scanner(System.in);
    11.         while(scan.hasNext()) {
    12.             String in = scan.next().toString();
    13.             if(OPERATION_EXIT.equals(in.toUpperCase())
    14.                     || OPERATION_EXIT.substring(01).equals(in.toUpperCase())) {
    15.                 System.out.println(\"您成功已退出!\");
    16.                 break;
    17.             }
    18.             System.out.println(\"您输入的值:\"+in);
    19.         }
    20.     }
    21. }
    ', '1', '2', '1', '1', '2', '0', '2017-10-07 15:59:33', '2017-10-07 15:59:33', '1', '1', '1'), ('12', '1', 'Java中静态代码块、构造代码块的区别', '

    直接在类中定义且没有加static关键字的代码块称为{}构造代码块。

    构造代码块在创建对象时被调用,每次创建对象都会被调用,并且构造代码块的执行次序优先于类构造函数。

    静态代码块:在java中使用static关键字声明的代码块。静态块用于初始化类,为类的属性初始化。每个静态代码块只会执行一次。由于JVM在加载类时会执行静态代码块,所以静态代码块先于主方法执行

    注意:1 静态代码块不能存在于任何方法体内。2 静态代码块不能直接访问静态实例变量和实例方法,需要通过类的实例对象来访问。

    静态代码块、构造代码块、构造函数同时存在时的执行顺序:静态代码块 > 构造代码块  > 构造函数

     

    下面我们来举几个例子说明一切

    demo1:构造方法

    1. public class Test {
    2.      static {
    3.          System.out.println(\"静态块\");
    4.      }
    5.      {
    6.          System.out.println(\"构造块,在类中定义\");
    7.      }
    8.      public Test() {
    9.          System.out.println(\"构造方法执行\");
    10.      }
    11.      public static void main(String[] args) {
    12.          new Test();
    13.          new Test();
    14.      }
    15.  }
    16. /*
    17. 静态块
    18. 构造块,在类中定义
    19. 构造方法执行
    20. 构造块,在类中定义
    21. 构造方法执行
    22. */

    demo2:普通代码块

    1. /*普通代码块:在方法或语句中出现的{}就称为普通代码块。
    2. 普通代码块和一般的语句执行顺序由他们在代码中出现的次序决定--“先出现先执行”*/
    3. public class Test {
    4.     public static void main(String[] args) {
    5.         {
    6.             int x = 3;
    7.             System.out.println(\"1,普通代码块内的变量x=\" + x);
    8.         }
    9.         int x = 1;
    10.         System.out.println(\"主方法内的变量x=\" + x);
    11.         {
    12.             int y = 7;
    13.             System.out.println(\"2,普通代码块内的变量y=\" + y);
    14.         }
    15.     }
    16. }
    17. /* 
    18. 1,普通代码块内的变量x=3
    19. 主方法内的变量x=1
    20. 2,普通代码块内的变量y=7
    21. */

    demo3:构造代码块

    1. /*构造块:直接在类中定义且没有加static关键字的代码块称为{}构造代码块。
    2. 构造代码块在创建对象时被调用,每次创建对象都会被调用,
    3. 并且构造代码块的执行次序优先于类构造函数。*/
    4. public class Test {
    5.     {
    6.         System.out.println(\"第一代码块\");
    7.     }
    8.     public Test() {
    9.         System.out.println(\"构造方法\");
    10.     }
    11.     {
    12.         System.out.println(\"第二构造块\");
    13.     }
    14.     public static void main(String[] args) {
    15.         new Test();
    16.         new Test();
    17.         new Test();
    18.     }
    19. }
    20. /*
    21. 第一代码块
    22. 第二构造块
    23. 构造方法
    24. 第一代码块
    25. 第二构造块
    26. 构造方法
    27. 第一代码块
    28. 第二构造块
    29. 构造方法
    30. */

    demo4:静态代码块

    1. /*静态代码块:在java中使用static关键字声明的代码块。
    2. 静态块用于初始化类,为类的属性初始化。
    3. 每个静态代码块只会执行一次。
    4. 由于JVM在加载类时会执行静态代码块,所以静态代码块先于主方法执行。
    5. 如果类中包含多个静态代码块,那么将按照\"先定义的代码先执行,后定义的代码后执行\"。
    6. 注意:1 静态代码块不能存在于任何方法体内。
    7.           2 静态代码块不能直接访问静态实例变量和实例方法,需要通过类的实例对象来访问。*/
    8. class Code {
    9.     {
    10.         System.out.println(\"Code的构造块\");
    11.     }
    12.     static {
    13.         System.out.println(\"Code的静态代码块\");
    14.     }
    15.     public Code() {
    16.         System.out.println(\"Code的构造方法\");
    17.     }
    18. }
    19. public class Test {
    20.     {
    21.         System.out.println(\"Test的构造块\");
    22.     }
    23.     static {
    24.         System.out.println(\"Test的静态代码块\");
    25.     }
    26.     public Test() {
    27.         System.out.println(\"Test的构造方法\");
    28.     }
    29.     public static void main(String[] args) {
    30.         System.out.println(\"Test的主方法\");
    31.         new Code();
    32.         new Code();
    33.         new Test();
    34.         new Test();
    35.     }
    36. }
    37. /*
    38. Test的静态代码块
    39. Test的主方法
    40. Code的静态代码块
    41. Code的构造块
    42. Code的构造方法
    43. Code的构造块
    44. Code的构造方法
    45. Test的构造块
    46. Test的构造方法
    47. Test的构造块
    48. Test的构造方法
    49.  */
    ', '1', '2', '1', '1', '0', '0', '2017-10-07 16:00:06', '2017-10-07 16:00:06', '1', '1', '1'), ('13', '1', 'sql语句分为三类(DML,DDL,DCL)-介绍', '

    DML(data manipulation language):数据库操作语言
    它们是SELECT、UPDATE、INSERT、DELETE,就象它的名字一样

     

    DDL(data definition language):的数据库定义语言
    主要的命令有CREATE、ALTER、DROP等,DDL主要是用在定义或改变表(TABLE)的结构,数据类型,表之间的链接和约束等初始化工作上,他们大多在建立表时使用

     

    DCL(Data Control Language): 数据库控制语言
    是用来设置或更改数据库用户或角色权限的语句,包括(grant,deny,revoke等)语句。在默认状态下,只有sysadmin,dbcreator,db_owner或db_securityadmin等人员才有权力执行DCL

     

    详细解释:

    一、DDL is Data Definition Language statements

    数据定义语言,用于定义和管理 SQL 数据库中的所有对象的语言

    1.CREATE - to create objects in the database   创建

    2.ALTER - alters the structure of the database   修改

    3.DROP - delete objects from the database   删除

    4.TRUNCATE - remove all records from a table, including all spaces allocated for the records are removed

    TRUNCATE TABLE [Table Name]。

    下面是对Truncate语句在MSSQLServer2000中用法和原理的说明:

    Truncate table 表名 速度快,而且效率高,因为:

    TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同:二者均删除表中的全部行。但

    TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少。

    DELETE 语句每次删除一行,并在事务日志中为所删除的每行记录一项。TRUNCATE TABLE 通过释放存

    储表数据所用的数据页来删除数据,并且只在事务日志中记录页的释放。

    TRUNCATE TABLE 删除表中的所有行,但表结构及其列、约束、索引等保持不变。新行标识所用的计数值重置为该列的种子。如果想保留标识计数值,请改用 DELETE。如果要删除表定义及其数据,请使用 DROP TABLE 语句。

    对于由 FOREIGN KEY 约束引用的表,不能使用 TRUNCATE TABLE,而应使用不带 WHERE 子句的 DELETE 语句。由于 TRUNCATE TABLE 不记录在日志中,所以它不能激活触发器。

    TRUNCATE TABLE 不能用于参与了索引视图的表。

    5.COMMENT - add comments to the data dictionary 注释

    6.GRANT - gives user\'s access privileges to database 授权

    7.REVOKE - withdraw access privileges given with the GRANT command   收回已经授予的权限

     

    二、DML is Data Manipulation Language statements

    数据操作语言,SQL中处理数据等操作统称为数据操纵语言

    1.SELECT - retrieve data from the a database           查询

    2.INSERT - insert data into a table                    添加

    3.UPDATE - updates existing data within a table    更新

    4.DELETE - deletes all records from a table, the space for the records remain   删除

    5.CALL - call a PL/SQL or Java subprogram

    6.EXPLAIN PLAN - explain access path to data

    Oracle RDBMS执行每一条SQL语句,都必须经过Oracle优化器的评估。所以,了解优化器是如何选择(搜索)路径以及索引是如何被使用的,对优化SQL语句有很大的帮助。Explain可以用来迅速方便地查出对于给定SQL语句中的查询数据是如何得到的即搜索路径(我们通常称为Access Path)。从而使我们选择最优的查询方式达到最大的优化效果。

    7.LOCK TABLE - control concurrency 锁,用于控制并发

     

    三、DCL is Data Control Language statements

    数据控制语言,用来授予或回收访问数据库的某种特权,并控制数据库操纵事务发生的时间及效果,对数据库实行监视等

    1.COMMIT - save work done 提交

    2.SAVEPOINT - identify a point in a transaction to which you can later roll back 保存点

    3.ROLLBACK - restore database to original since the last COMMIT   回滚

    4.SET TRANSACTION - Change transaction options like what rollback segment to use   设置当前事务的特性,它对后面的事务没有影响


    ', '10', '13', '9,22', '1', '3', '1', '2017-10-07 16:00:50', '2017-10-07 16:01:07', '1', '1', '1'), ('14', '1', 'execute、executeUpdate、executeQuery三者的区别', '

    1. ResultSet executeQuery(String sql); 执行SQL查询,并返回 ResultSet 对象。

    例如:被执行最多的 SELECT 语句。

    2.int executeUpdate(String sql); 可执行增,删,改,返回执行受到影响的行数。

    例如: INSERT、UPDATE 或 DELETE 语句以及 SQL DDL(数据定义语言)语句

    3. boolean execute(String sql); 可执行任何SQL语句,返回一个布尔值,表示是否返回 ResultSet 。

           execute是executeQuery和executeUpdate的综合.

    通常我们没有必要使用execute方法来执行SQL语句,而是使用 executeQuery 或 executeUpdate 更适合。

    -----

     

    executeUpdate() 这是 PreparedStatement 接口中的方法

    executeUpdate(String sql) 这是 PreparedStatement 从父接口 Statement 中继承过来的方法

    executeUpdate() 中执行 SQL 语句需要在创建 PerparedStatement 时通过 Connection 的 prepareStatement(String sql) 方法中写出,因为 PerparedStatement 中的 SQL 语句数据库需要进行预编译和缓存,因此要在创建 PerparedStatement 对象时给出 SQL 语句。

    而 executeUpdate(String sql) 是 Statement 中的方法,参数中的 SQL 语句只是提交给数据库去执行,并不需要预编译。

    如果 SQL 语句中有 ? 占位符,那么在设置好占位符中的值后,必须使用 executeUpdate() 执行。而 executeUpdate(String sql) 只是提交一个 SQL 语句,且这个语句中不能带有 ? 占位符。

    当然,我们最好不要用 Statement 接口
    参考:http://www.cnblogs.com/bukudekong/archive/2011/06/22/2086531.html

    ', '1', '8', '20,22', '1', '3', '0', '2017-10-07 16:02:12', '2017-10-07 16:02:12', '1', '1', '1'), ('15', '1', 'JSP 中 forward 转发 和 sendRedirect 重定向的区别', '

    本质区别:

          一句话概括:重定向是客户端行为,转发是服务器行为.
         1.请求次数:
               重定向:重定向行为是做了两次请求,及产生了两个request对象,重定向会导致request对象信息丢失。
               请求转发:转发做了一次请求, 浏览器的地址栏一直是第一次请求的地址。转发是服务器内部request/response控制权的移交。
          2.请求资源地址:

    重定向:web组件可以将请求重定向到任意一个url,而不仅仅是同一个应用。

    重定向的源组件与目标组件不共用同一个HttpServletRequest对象,因此不能在request范围内共享数据。

    请求转发:转发的源组件与目标组件必须在同一个应用中,两者可以在request范围内共享数据。

           3.一种解释:

    假设你去办理某个执照

    重定向:你先去了A局,A局的人说:“这个事情不归我们管,去B局”,然后,你就从A退了出来,自己乘车去了B局。

    请求转发:你先去了A局,A局看了以后,知道这个事情其实应该B局来管,但是他没有把你退回来,而是让你坐一会儿,自己到后面办公室联系了B的人,让他们办好后,送了过来。

    工作流程:

    1.重定向

    浏览器发出http请求  >> 服务器接受请求并发送302状态码和新的对应的url到浏览器  >>

    浏览器接收响应并自动请求新的url  >> 服务器接收请求并寻找客户所需的资源响应到浏览器

    2.转发

    浏览器发出http求其  >> 服务器接收请求  >> 服务器调用内部的一个方法在容器内完成请求处理和转发动作  >>

    将客户所需资源发送到浏览

     

    调用方式:

    我们知道,在servlet中调用转发、重定向的语句如下:

    request.getRequestDispatcher(\"new.jsp\").forward(request, response);   //转发到new.jsp

    response.sendRedirect(\"new.jsp\");   //重定向到new.jsp

    在jsp页面中你也会看到通过下面的方式实现转发:

    <jsp:forward page=\"apage.jsp\" />

    当然也可以在jsp页面中实现重定向:

    <%response.sendRedirect(\"new.jsp\"); %> //重定向到new.jsp

     

         1.重定向:
              1).response.sendRedirect(url);
              2).response.setState(302);
                   response.setHeader(\"location\",url);
         2.转发:
              1).request.getRequestDispatcher(url).forward(request,response);
              2).request.getRequestDispatcher(url).include(request,response);
              3).<jsp:forward page=url />
    ', '1', '8', '18', '0', '0', '0', '2017-10-07 16:03:08', '2017-10-07 16:03:08', '1', '1', '1'), ('16', '1', 'Java中File类的使用', '

    一、File类常用API介绍

    内容见代码和注释

    1. package com.liuyanzhao;
    2. import java.io.File;
    3. import java.io.IOException;
    4. /*
    5.  *
    6.  * @author WellsLiu
    7.  *
    8.  */
    9. public class Test {
    10.     public static void main(String[] args) {
    11.         //1.创建一个文件夹对象
    12.         File file = new File(\"F:\\\\桌面\\\\music\");
    13.         //判断文件是否存在
    14.         System.out.println(file.exists());//true
    15.         //如果文件夹不存在创建之,否则删除之
    16.         if(!file.exists())
    17.             file.mkdir();//创建文件夹
    18.         //else 
    19.             //file.delete();
    20.         //是否是一个目录
    21.         System.out.println(file.isDirectory());//true
    22.         //是否是一个文件
    23.         System.out.println(file.isFile());//false
    24.         //2.创建一个文件对象
    25.         File file2 = new File(\"F:\\\\桌面\\\\music\\\\1.mp3\");
    26.         System.out.println(file.exists());
    27.         //如果文件不存在创建之,否则删除   之
    28.         if(file2.exists())
    29.             try {
    30.                 file2.createNewFile();
    31.             } catch (IOException e) {
    32.                 e.printStackTrace();
    33.             }
    34.         //else 
    35.             //file2.delete();
    36.         //3.常用的File对象API
    37.         System.out.println(file);//file.toString()的内容,F:\\桌面\\music
    38.         System.out.println(file.getAbsolutePath());//file的绝对路径F:\\桌面\\music
    39.         System.out.println(file.getName());//music
    40.         System.out.println(file2.getName());//1.mp3
    41.         System.out.println(file.getParent());//F:\\桌面
    42.     }
    43. }

     

    二、遍历目录

    1. package com.liuyanzhao;
    2. import java.io.File;
    3. import java.io.IOException;
    4. /*
    5.  *
    6.  * @author WellsLiu
    7.  *
    8.  */
    9. //列出File类一些常用操作比如过滤、遍历等操作
    10. class FileUtils {
    11.     //列出指定目录下(包括子目录)的所有文件
    12.     public static void listDirectory(File dir) throws IOException{
    13.         if(!dir.exists()) {
    14.             throw new IllegalArgumentException(\"目录\"+dir+\"不存在\");
    15.         }
    16.         if(!dir.isDirectory()) { //判断File类的对象是否为文件夹
    17.             throw new IllegalArgumentException(dir+\"不是目录\");
    18.         }
    19.         /*
    20.         //1.输出dir目录下的一级文件夹和文件
    21.         String [] filenames = dir.list();//返回的是字符串数组 直接子名称
    22.         for (String string : filenames) {
    23.             System.out.println(dir+\"\\\\\"+string);//dirstring是文件或文件夹名
    24.         }
    25.         */
    26.         //2.如果要遍历子目录下的目录及所有文件,就需要构造File对象做递归操作
    27.         File[] files = dir.listFiles();//返回的是直接子目录(文件)的抽象
    28.         if(files!=null&&files.length>0) {
    29.             for(File file:files) {
    30.                 if(file.isDirectory()) {
    31.                     //递归
    32.                     listDirectory(file);
    33.                 } else {
    34.                     System.out.println(file);
    35.                 }
    36.             }
    37.         }
    38.     }
    39. }
    40. public class Test1 {
    41.     public static void main(String[] args) throws IOException{
    42.         FileUtils.listDirectory(new File(\"C:\\\\Users\\\\Liu_Yanzhao\"));
    43.     }
    44. }
    ', '1', '2', '1,17', '3', '0', '0', '2017-10-07 16:04:25', '2017-10-07 16:04:33', '1', '1', '1'), ('17', '1', 'java中ImageIcon路径问题', '

    一、问题

    ImageIcon icon = new ImageIcon(\"logo.jpg\");

    执行结果是icon无法显示

     

    二、解决

    类中有如下调用:

    ImageIcon icon = new ImageIcon(\"1.jpg\");

     

    很自然地认为当前类文件和图片在同一路径下即可。

    其实所谓的相对路径起点是工程的根目录,即project。

    这行代码执行时在project目录下查找名为a.gif的文件,结果当然是找不到。

    要得到工程的相对路径可通过System.getProperty(\"user.dir\")得到。

    对图片和对文件的查找应该是一致的,如new File()。

     

    1、假如你的工程根目录为:Project1

    你的图片文件放在:Project1/src/images/1.jpg

    所以正确的调用是(不要加Project1):

    ImageIcon icon = new ImageIcon(\"src/images/1.jpg\");

     

    2、假如你的工程根目录为:Project1

    你的图片文件在:Project1/src/com.liuyanzhao/1.jpg

    所以正确的调用是(com.liuyanzhao是包名)

    ImageIcon icon = new ImageIcon(\"src/com/liuyanzhao/1.jpg\");

    这行代码执行时在project/test目录下查找到了文件

     

    总结起来就是一句话:所谓相对路径就是相对于工程根目录的位置^_^

     

    三、其他

    查看了一下ImageIcon的构造函数。

    Public ImageIcon(String filename)//参数可以是绝对路径也可以是相对路径

    Public ImageIcon(URL url)

     

    第一种构造不在赘述。

    第二种通过URL来得到图片

    URL url = getClass().getResource(\"a.gif\"); //当前编译后class文件所在目录查找

    ImageIcon icon = new ImageIcon(url);

     

    ImageIcon支持GIF、JPG、PNG等格式。

    ', '1', '2', '1', '7', '2', '0', '2017-10-07 16:05:55', '2017-10-07 16:05:55', '1', '1', '1'); COMMIT; -- ---------------------------- -- Table structure for `category` -- ---------------------------- DROP TABLE IF EXISTS `category`; CREATE TABLE `category` ( `category_id` int(5) unsigned NOT NULL AUTO_INCREMENT, `category_pid` int(5) DEFAULT NULL, `category_name` varchar(50) DEFAULT NULL, `category_description` varchar(255) DEFAULT NULL, `category_order` int(2) unsigned DEFAULT '1', `category_icon` varchar(20) DEFAULT NULL, `category_status` int(2) unsigned DEFAULT '1', PRIMARY KEY (`category_id`), UNIQUE KEY `category_name` (`category_name`) ) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of `category` -- ---------------------------- BEGIN; INSERT INTO `category` VALUES ('1', '0', 'Java', 'Java语言', '1', 'fa fa-coffee', '1'), ('2', '1', 'Java基础', '', '1', '', '1'), ('3', '1', 'Core Java', '', '1', '', '1'), ('4', '1', '多线程并发编程', '', '1', '', '1'), ('5', '1', 'Sockets和IO', '', '1', '', '1'), ('6', '1', '设计模式和反射', '', '1', '', '1'), ('7', '1', 'JVM', '', '1', '', '1'), ('8', '1', 'JavaWeb', '', '1', '', '1'), ('9', '1', 'Java框架', '', '1', '', '1'), ('10', '0', '计算机科学', '', '1', 'fa fa-cubes', '1'), ('11', '10', '数据结构和算法', '', '1', '', '1'), ('12', '10', '操作系统', '', '1', '', '1'), ('13', '10', '数据库', '', '1', '', '1'), ('14', '10', '计算机网络', '', '1', '', '1'), ('15', '0', '其他技术', '', '1', 'fa-snowflake-o fa', '1'), ('16', '15', '消息服务', '', '1', '', '1'), ('17', '15', '缓存服务', '', '1', '', '1'), ('18', '15', '负载均衡', '', '1', '', '1'); COMMIT; -- ---------------------------- -- Table structure for `comment` -- ---------------------------- DROP TABLE IF EXISTS `comment`; CREATE TABLE `comment` ( `comment_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `comment_pid` int(10) unsigned DEFAULT '0', `comment_pname` varchar(255) DEFAULT NULL, `comment_article_id` int(10) unsigned DEFAULT NULL, `comment_author_name` varchar(50) DEFAULT NULL, `comment_author_email` varchar(50) DEFAULT NULL, `comment_author_url` varchar(50) DEFAULT NULL, `comment_content` varchar(1000) DEFAULT NULL, `comment_agent` varchar(200) DEFAULT NULL, `comment_ip` varchar(50) DEFAULT NULL, `comment_create_time` datetime DEFAULT NULL, `comment_role` int(2) DEFAULT NULL, `comment_status` int(2) unsigned DEFAULT '1', PRIMARY KEY (`comment_id`) ) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of `comment` -- ---------------------------- BEGIN; INSERT INTO `comment` VALUES ('1', '0', '', '17', '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '评论测试', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:06:34', '1', '1'), ('2', '1', '言曌', '17', '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '再次测试', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:06:43', '1', '1'), ('3', '0', '', '8', '张三', '123@qq.com', '', '评论测试', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:08:06', '0', '1'), ('4', '0', '', '8', 'Tom', '123@126.com', '', 'Tom来测试', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:08:46', '0', '1'), ('5', '0', '', '14', 'Jack', '666@qq.com', '', 'Jack来此一游', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:09:27', '0', '1'), ('6', '5', 'Jack', '14', 'Jack', '666@qq.com', '', 'Jack回复Jack测试', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:10:10', '0', '1'), ('7', '5', 'Jack', '14', '娜美', '123456@sina.com', '', '娜美回复Jack测试', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:10:53', '0', '1'), ('8', '0', '', '9', '苏珊', '1212@sina.com', '', '苏珊来过', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:11:36', '0', '1'), ('9', '8', '苏珊', '9', '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '欢迎苏珊', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:12:00', '1', '1'), ('10', '0', '', '10', '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '博主测试', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:12:15', '1', '1'), ('11', '0', '', '13', '多伦', 'duolun@gmail.com', '', '多伦路过', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:13:09', '0', '1'), ('12', '0', '', '13', '贾克斯', 'jax@sohu.com', '', '我是贾克斯', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:13:44', '0', '1'), ('13', '12', '贾克斯', '13', '瑞克斯', 'ruikou@126.com', '', '贾克斯你好,我是瑞克斯', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:14:32', '0', '1'), ('14', '0', '', '11', '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '评论测试', null, '0:0:0:0:0:0:0:1', '2017-10-10 11:44:28', '1', '1'), ('15', '14', '言曌', '11', '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '再次测试', null, '0:0:0:0:0:0:0:1', '2017-10-10 11:44:36', '1', '1'); COMMIT; -- ---------------------------- -- Table structure for `link` -- ---------------------------- DROP TABLE IF EXISTS `link`; CREATE TABLE `link` ( `link_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `link_url` varchar(255) DEFAULT NULL, `link_name` varchar(255) DEFAULT NULL, `link_image` varchar(255) DEFAULT NULL, `link_description` varchar(255) DEFAULT NULL, `link_owner_nickname` varchar(40) DEFAULT NULL, `link_owner_contact` varchar(255) DEFAULT NULL, `link_update_time` datetime DEFAULT NULL, `link_create_time` datetime DEFAULT NULL, `link_order` int(2) unsigned DEFAULT '1', `link_status` int(2) unsigned DEFAULT '1', PRIMARY KEY (`link_id`), UNIQUE KEY `link_name` (`link_name`) ) ENGINE=MyISAM AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of `link` -- ---------------------------- BEGIN; INSERT INTO `link` VALUES ('1', 'http://liuyanzhao.com', '言曌博客', null, '一个码农的成长之路', null, '你好,我的丘丘是847064370', '2017-10-07 16:51:03', '2017-10-07 16:29:35', '1', '1'), ('6', 'http://liuyanzhao.com', '测试', null, '测试', null, '测试', '2017-10-07 16:51:49', '2017-10-07 16:51:49', '1', '0'); COMMIT; -- ---------------------------- -- Table structure for `menu` -- ---------------------------- DROP TABLE IF EXISTS `menu`; CREATE TABLE `menu` ( `menu_id` int(5) NOT NULL AUTO_INCREMENT, `menu_name` varchar(255) DEFAULT NULL, `menu_url` varchar(255) DEFAULT NULL, `menu_level` int(2) DEFAULT NULL, `menu_icon` varchar(255) DEFAULT NULL, `menu_order` int(2) DEFAULT NULL, `menu_status` int(2) DEFAULT '1', PRIMARY KEY (`menu_id`), UNIQUE KEY `menu_name` (`menu_name`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of `menu` -- ---------------------------- BEGIN; INSERT INTO `menu` VALUES ('1', '留言板', '/message', '2', 'fa fa-comment', '1', '1'), ('2', 'LeetCode', 'https://leetcode.com/problemset/all', '2', '', '2', '1'), ('3', '关于本站', '/aboutSite', '1', 'fa fa-info', '1', '1'), ('4', '文章归档', '/articleFile', '1', 'fa-list-alt fa', '2', '1'), ('5', '申请友链', '/applyLink', '1', 'fa fa-link', '3', '1'); COMMIT; -- ---------------------------- -- Table structure for `notice` -- ---------------------------- DROP TABLE IF EXISTS `notice`; CREATE TABLE `notice` ( `notice_id` int(10) NOT NULL AUTO_INCREMENT, `notice_title` varchar(255) DEFAULT NULL, `notice_content` varchar(10000) DEFAULT NULL, `notice_create_time` datetime DEFAULT NULL, `notice_update_time` datetime DEFAULT NULL, `notice_status` int(2) unsigned DEFAULT '1', `notice_order` int(2) DEFAULT NULL, PRIMARY KEY (`notice_id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of `notice` -- ---------------------------- BEGIN; INSERT INTO `notice` VALUES ('1', '本站下载地址', '

    本站后端基于 Spring+SpringMVC+Mybatis+JSP实现,对于初学SSM的小伙伴可以参考。

    下载地址:https://github.com/saysky/ForestBlog

    ', '2017-10-06 23:44:35', '2017-10-10 11:23:28', '1', '1'); COMMIT; -- ---------------------------- -- Table structure for `options` -- ---------------------------- DROP TABLE IF EXISTS `options`; CREATE TABLE `options` ( `option_id` int(11) NOT NULL, `option_site_title` varchar(255) DEFAULT NULL, `option_site_descrption` varchar(255) DEFAULT NULL, `option_meta_descrption` varchar(255) DEFAULT NULL, `option_meta_keyword` varchar(255) DEFAULT NULL, `option_aboutsite_avatar` varchar(255) DEFAULT NULL, `option_aboutsite_title` varchar(255) DEFAULT NULL, `option_aboutsite_content` varchar(255) DEFAULT NULL, `option_aboutsite_wechat` varchar(255) DEFAULT NULL, `option_aboutsite_qq` varchar(255) DEFAULT NULL, `option_aboutsite_github` varchar(255) DEFAULT NULL, `option_aboutsite_weibo` varchar(255) DEFAULT NULL, `option_tongji` varchar(255) DEFAULT NULL, `option_status` int(2) DEFAULT '1', PRIMARY KEY (`option_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of `options` -- ---------------------------- BEGIN; INSERT INTO `options` VALUES ('1', '风吟博客', '莫问收获,但问耕耘。', '风吟博客,一个简洁的Java博客,言曌程序学习的一个新起点。', '风吟,风吟博客,Java博客,SSM博客,言曌,言曌博客', '/uploads/2017/10/2017100622490186.png', '博客初心', '言曌程序学习的新起点,大学最后两年的成长记录。珍惜时间,保持努力的惯性。', '/uploads/2017/10/20171006224906122.jpg', '847064370', 'saysky', '5936412667', null, '1'); COMMIT; -- ---------------------------- -- Table structure for `page` -- ---------------------------- DROP TABLE IF EXISTS `page`; CREATE TABLE `page` ( `page_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `page_key` varchar(50) DEFAULT NULL, `page_title` varchar(50) DEFAULT NULL, `page_content` mediumtext, `page_create_time` datetime DEFAULT NULL, `page_update_time` datetime DEFAULT NULL, `page_view_count` int(10) unsigned DEFAULT '0', `page_comment_count` int(5) unsigned DEFAULT '0', `page_status` int(2) unsigned DEFAULT '1', PRIMARY KEY (`page_id`), UNIQUE KEY `page_key` (`page_key`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of `page` -- ---------------------------- BEGIN; INSERT INTO `page` VALUES ('1', 'map', '站点地图', null, null, null, '0', '0', '2'), ('2', 'articleFile', '文章归档', null, null, null, '0', '0', '2'), ('3', 'message', '留言板', null, null, null, '0', '0', '2'), ('4', 'applyLink', '申请友链', null, null, null, '0', '0', '2'), ('5', 'aboutSite', '关于本站', '

    关于项目

    该项目是博主暑假粗略学完 SSM(Spring+SpringMVC+Mybatis)后,开始着手做的一个博客系统。
    主要涉及的包括 JSP,JSTL,EL表达式,MySQL,Druid连接池,Spring,SpringMVC,MyBatis 等。
    前端采用Layui框架和扒了一个[网站](http://liuyanzhao.com)的前台样式。通过Maven管理依赖。

    效果预览

    预览地址:http://blog.liuyanzhao.com

    前台效果图
    \"一个简单漂亮的Java博客系统\"

    后台效果图
    \"一个简单漂亮的Java博客系统\"

     

    后台地址:/admin 或者 /login

    用户名为admin,密码为123456

     

    使用注意

    1、使用IDE导入项目

    将项目即(ForestBlog文件夹)放到 某个位置,用IDE(如Eclipse,IntelliJ IDEA)导入,然后可以在IDE里运行 Tomcat,访问项目。

    因为该项目使用了`Maven`,所有你需要新的IDE需要有Maven插件或者功能,这里就不多介绍了。

    \"一个简单漂亮的Java博客系统\"

    2、 导入数据库

    新建数据库 `forest_blog`,导入数据库(即forest_blog.sql)。注意,数据库的编码和排序规则是utf-8和utf-8_general_ci

    \"一个简单漂亮的Java博客系统\"

    3、修改项目中的数据库连接信息

    修改 `db.properties` 文件,该文件很容易找到,在 src/main/resources 中,注意修改数据库地址、表名、用户名和密码。

    如果不修改,会出现无法启动项目

     

    4、修改上传文件路径

    该项目中,文件上传是传到本地,且和项目文件夹不在一起,就是说是分离的。比如你的项目是在D盘,你可以修改上传路径到E盘。当然我们访问上传的图片时,需要   给Tomcat添加静态资源映射,比如访问 localhost:8080/uplaods/1.jpg tomcat可以在你的E盘找到。这个在第5步会说。

    现在是修改上传文件路径,该文件在 src/main/java/com/liuyanzhao/blog/controller/Common/UploadFileController.java

    修改第33行的 `String rootPath =\"/www/uploads/\";` 为你的 uploads 的路径。

    如果不修改,会出现无法上传图片

     

    5、给uploads文件夹添加静态资源映射

    同第4步,我们已经修改了uploads路径,该路径就要和下面的docBase一致。
    在 tomcat/conf/server.xml 的 Host 标签内添加如下代码

    1. <Context path=\"/uploads\" docBase=\"/www/uploads\" debug=\"0\" reloadable=\"true\" />

    对啦,如果你使用的是IDE,需要在该IDE里修改。

    比如 IntelliJ IDEA 是在Tomcat配置中添加

    \"一个简单漂亮的Java博客系统\"

    如果不修改,会出现uploads的图片无法加载,网页打开有延迟

     

    6、修改 Tomcat的首页为该项目

    跟第5步相似

    该项目中的所有根路径都是 / ,没有使用相对路径的 `${pagecontext.request.getcontextpath}` ,因为如何相对路径不存在,该值就会为空字符串,以致   首页链接我必须改为 / 。
    所以,你的项目首页应该是 localhost:8080,而不是loclahost:8080/ForestBlog

    所以要在 server.xml 的Host标签内里添加

    1. <Context path=\"\" docBase=\"/www/server/panel/vhost/tomcat/ForestBlog\" debug=\"0\" reloadable=\"true\" />

    其中path=\"\"表示为首页,即localhost:8080或者127.0.0.1:8080
    docBase-\"xxx\"是文件路径

    \"一个简单漂亮的Java博客系统\"

    对啦,如果你使用的是IDE,需要在该IDE里修改。

    比如 IntelliJ IDEA 是在Tomcat配置中添加

    \"一个简单漂亮的Java博客系统\"

    如果不修改,会出现内页很多链接不对

     

    下载地址:

    Github地址:https://github.com/saysky/ForestBlog

    (如果可以帮忙点一次Star和Fork)

     

    更新

    上一次更新:2017年10月10日14:02:02

    ', '2017-10-06 23:40:35', '2017-10-10 14:58:12', null, null, '1'); COMMIT; -- ---------------------------- -- Table structure for `tag` -- ---------------------------- DROP TABLE IF EXISTS `tag`; CREATE TABLE `tag` ( `tag_id` int(5) unsigned NOT NULL AUTO_INCREMENT, `tag_name` varchar(20) DEFAULT NULL, `tag_description` varchar(255) DEFAULT NULL, `tag_status` int(2) unsigned DEFAULT '1', PRIMARY KEY (`tag_id`), UNIQUE KEY `tag_name` (`tag_name`) ) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of `tag` -- ---------------------------- BEGIN; INSERT INTO `tag` VALUES ('1', 'Java', '', '1'), ('2', '算法', '', '1'), ('3', '数据结构', '', '1'), ('4', 'C', '', '1'), ('5', '操作系统', '', '1'), ('6', '计算机网络', '', '1'), ('8', '面试题', '', '1'), ('9', '数据库', '', '1'), ('10', 'MySQL', '', '1'), ('11', 'Spring', '', '1'), ('12', 'SpringMVC', '', '1'), ('13', 'MyBatis', '', '1'), ('14', 'JVM', '', '1'), ('15', '设计模式', '', '1'), ('16', '网络编程', '', '1'), ('17', 'IO', '', '1'), ('18', 'JSP', '', '1'), ('19', 'Servlet', '', '1'), ('20', 'JavaWeb', '', '1'), ('21', 'Hibernate', '', '1'), ('22', 'SQL', '', '1'); COMMIT; -- ---------------------------- -- Table structure for `user` -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `user_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `user_name` varchar(255) NOT NULL DEFAULT '', `user_pass` varchar(255) NOT NULL DEFAULT '', `user_nickname` varchar(255) NOT NULL DEFAULT '', `user_email` varchar(100) DEFAULT '', `user_url` varchar(100) DEFAULT '', `user_avatar` varchar(255) DEFAULT NULL, `user_last_login_ip` varchar(255) DEFAULT NULL, `user_register_time` datetime DEFAULT NULL, `user_last_login_time` datetime DEFAULT NULL, `user_status` int(2) unsigned DEFAULT '1', PRIMARY KEY (`user_id`), UNIQUE KEY `user_name` (`user_name`), UNIQUE KEY `user_email` (`user_email`) ) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of `user` -- ---------------------------- BEGIN; INSERT INTO `user` VALUES ('1', 'admin', '123456', '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '/uploads/2017/10/20171006225356181.jpg', '0:0:0:0:0:0:0:1', '2017-10-06 21:56:33', '2017-10-10 14:55:18', '1'); COMMIT; SET FOREIGN_KEY_CHECKS = 1;