本文会使用一个案例,就mybatis的一些基础语法进行讲解。案例中使用到的数据库表和对象如下:
article表:这个表存放的是文章的基础信息
-- ----------------------------
-- Table structure for article
-- ----------------------------
DROP TABLE IF EXISTS `article`;
CREATE TABLE `article` (
`article_id` int(11) NOT NULL AUTO_INCREMENT,
`article_user_id` int(11) unsigned DEFAULT NULL,
`article_title` varchar(255) DEFAULT NULL,
`article_content` mediumtext,
`article_view_count` int(11) DEFAULT '',
`article_comment_count` int(11) DEFAULT '',
`article_like_count` int(11) DEFAULT '',
`article_is_comment` int(1) unsigned DEFAULT NULL,
`article_status` int(1) unsigned DEFAULT '',
`article_order` int(11) unsigned DEFAULT NULL,
`article_update_time` datetime DEFAULT NULL,
`article_create_time` datetime DEFAULT NULL,
`article_summary` text,
PRIMARY KEY (`article_id`)
) ENGINE=MyISAM AUTO_INCREMENT=36 DEFAULT CHARSET=utf8;
表中的记录如下:
-- ----------------------------
-- Records of article
-- ----------------------------
INSERT INTO `article` VALUES ('', '', '[转载]SpringMVC中使用Interceptor拦截器', '<p>SpringMVC 中的Interceptor 拦截器也是相当重要和相当有用的,它的主要作用是拦截用户的请求并进行相应的处理。比如通过它来进行权限验证,或者是来判断用户是否登陆,或者是像12306 那样子判断当前时间是否是购票时间。</p><p><br></p><p>一、定义Interceptor实现类</p><p>SpringMVC 中的Interceptor 拦截请求是通过HandlerInterceptor 来实现的。在SpringMVC 中定义一个Interceptor 非常简单,主要有两种方式,第一种方式是要定义的Interceptor类要实现了Spring 的HandlerInterceptor 接口,或者是这个类继承实现了HandlerInterceptor 接口的类,比如Spring 已经提供的实现了HandlerInterceptor 接口的抽象类HandlerInterceptorAdapter ;第二种方式是实现Spring的WebRequestInterceptor接口,或者是继承实现了WebRequestInterceptor的类。</p><p><br></p><p>(一)实现HandlerInterceptor接口</p><p>HandlerInterceptor 接口中定义了三个方法,我们就是通过这三个方法来对用户的请求进行拦截处理的。</p><p><br></p><p>(1 )preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法,顾名思义,该方法将在请求处理之前进行调用。SpringMVC 中的Interceptor 是链式的调用的,在一个应用中或者说是在一个请求中可以同时存在多个Interceptor 。每个Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor 中的preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值Boolean 类型的,当它返回为false 时,表示请求结束,后续的Interceptor 和Controller 都不会再执行;当返回值为true 时就会继续调用下一个Interceptor 的preHandle 方法,如果已经是最后一个Interceptor 的时候就会是调用当前请求的Controller 方法。</p><p><br></p><p>(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 方法之后的内容就是反向的。</p><p><br></p><p>(3 )afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法,该方法也是需要当前对应的Interceptor 的preHandle 方法的返回值为true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。</p><p><br></p><p>下面是一个简单的代码说明:</p><p><br></p><p>import javax.servlet.http.HttpServletRequest;</p><p>import javax.servlet.http.HttpServletResponse;</p><p>import org.springframework.web.servlet.HandlerInterceptor;</p><p>import org.springframework.web.servlet.ModelAndView;</p><p>public class SpringMVCInterceptor implements HandlerInterceptor {</p><p> /** </p><p> * preHandle方法是进行处理器拦截用的,顾名思义,该方法将在Controller处理之前进行调用,SpringMVC中的Interceptor拦截器是链式的,可以同时存在 </p><p> * 多个Interceptor,然后SpringMVC会根据声明的前后顺序一个接一个的执行,而且所有的Interceptor中的preHandle方法都会在 </p><p> * Controller方法调用之前调用。SpringMVC的这种Interceptor链式结构也是可以进行中断的,这种中断方式是令preHandle的返 </p><p> * 回值为false,当preHandle的返回值为false的时候整个请求就结束了。 </p><p> */</p><p> @Override</p><p> public boolean preHandle(HttpServletRequest request,</p><p> HttpServletResponse response, Object handler) throws Exception {</p><p> // TODO Auto-generated method stub </p><p> return false;</p><p> }</p><p> /** </p><p> * 这个方法只会在当前这个Interceptor的preHandle方法返回值为true的时候才会执行。postHandle是进行处理器拦截用的,它的执行时间是在处理器进行处理之 </p><p> * 后,也就是在Controller的方法调用之后执行,但是它会在DispatcherServlet进行视图的渲染之前执行,也就是说在这个方法中你可以对ModelAndView进行操 </p><p> * 作。这个方法的链式结构跟正常访问的方向是相反的,也就是说先声明的Interceptor拦截器该方法反而会后调用,这跟Struts2里面的拦截器的执行过程有点像, </p><p> * 只是Struts2里面的intercept方法中要手动的调用ActionInvocation的invoke方法,Struts2中调用ActionInvocation的invoke方法就是调用下一个Interceptor </p><p> * 或者是调用action,然后要在Interceptor之前调用的内容都写在调用invoke之前,要在Interceptor之后调用的内容都写在调用invoke方法之后。 </p><p> */</p><p> @Override</p><p> public void postHandle(HttpServletRequest request,</p><p> HttpServletResponse response, Object handler,</p><p> ModelAndView modelAndView) throws Exception {</p><p> // TODO Auto-generated method stub </p><p> }</p><p> /** </p><p> * 该方法也是需要当前对应的Interceptor的preHandle方法的返回值为true时才会执行。该方法将在整个请求完成之后,也就是DispatcherServlet渲染了视图执行, </p><p> * 这个方法的主要作用是用于清理资源的,当然这个方法也只能在当前这个Interceptor的preHandle方法的返回值为true时才会执行。 </p><p> */</p><p> @Override</p><p> public void afterCompletion(HttpServletRequest request,</p><p> HttpServletResponse response, Object handler, Exception ex)</p><p> throws Exception {</p><p> // TODO Auto-generated method stub </p><p> }</p><p>}</p><p>(二)实现WebRequestInterceptor 接口</p><p>WebRequestInterceptor 中也定义了三个方法,我们也是通过这三个方法来实现拦截的。这三个方法都传递了同一个参数WebRequest ,那么这个WebRequest 是什么呢?这个WebRequest 是Spring 定义的一个接口,它里面的方法定义都基本跟HttpServletRequest 一样,在WebRequestInterceptor 中对WebRequest 进行的所有操作都将同步到HttpServletRequest 中,然后在当前请求中一直传递。</p><p><br></p><p>(1 )preHandle(WebRequest request) 方法。该方法将在请求处理之前进行调用,也就是说会在Controller 方法调用之前被调用。这个方法跟HandlerInterceptor 中的preHandle 是不同的,主要区别在于该方法的返回值是void ,也就是没有返回值,所以我们一般主要用它来进行资源的准备工作,比如我们在使用Hibernate 的时候可以在这个方法中准备一个Hibernate 的Session 对象,然后利用WebRequest 的setAttribute(name, value, scope) 把它放到WebRequest 的属性中。这里可以说说这个setAttribute 方法的第三个参数scope ,该参数是一个Integer 类型的。在WebRequest 的父层接口RequestAttributes 中对它定义了三个常量:</p><p><br></p><p>SCOPE_REQUEST :它的值是0 ,代表只有在request 中可以访问。</p><p><br></p><p>SCOPE_SESSION :它的值是1 ,如果环境允许的话它代表的是一个局部的隔离的session,否则就代表普通的session,并且在该session范围内可以访问。</p><p><br></p><p>SCOPE_GLOBAL_SESSION :它的值是2 ,如果环境允许的话,它代表的是一个全局共享的session,否则就代表普通的session,并且在该session 范围内可以访问。</p><p><br></p><p>(2 )postHandle(WebRequest request, ModelMap model) 方法。该方法将在请求处理之后,也就是在Controller 方法调用之后被调用,但是会在视图返回被渲染之前被调用,所以可以在这个方法里面通过改变数据模型ModelMap 来改变数据的展示。该方法有两个参数,WebRequest 对象是用于传递整个请求数据的,比如在preHandle 中准备的数据都可以通过WebRequest 来传递和访问;ModelMap 就是Controller 处理之后返回的Model 对象,我们可以通过改变它的属性来改变返回的Model 模型。</p><p><br></p><p>(3 )afterCompletion(WebRequest request, Exception ex) 方法。该方法会在整个请求处理完成,也就是在视图返回并被渲染之后执行。所以在该方法中可以进行资源的释放操作。而WebRequest 参数就可以把我们在preHandle 中准备的资源传递到这里进行释放。Exception 参数表示的是当前请求的异常对象,如果在Controller中抛出的异常已经被Spring 的异常处理器给处理了的话,那么这个异常对象就是是null 。</p><p><br></p><p> </p><p><br></p><p>下面是一个简单的代码说明:</p><p><br></p><p>import org.springframework.ui.ModelMap;</p><p>import org.springframework.web.context.request.WebRequest;</p><p>import org.springframework.web.context.request.WebRequestInterceptor;</p><p>public class AllInterceptor implements WebRequestInterceptor {</p><p> /** </p><p> * 在请求处理之前执行,该方法主要是用于准备资源数据的,然后可以把它们当做请求属性放到WebRequest中 </p><p> */</p><p> @Override</p><p> public void preHandle(WebRequest request) throws Exception {</p><p> // TODO Auto-generated method stub </p><p> System.out.println(\"AllInterceptor...............................\");</p><p> request.setAttribute(\"request\", \"request\", WebRequest.SCOPE_REQUEST);//这个是放到request范围内的,所以只能在当前请求中的request中获取到 </p><p> request.setAttribute(\"session\", \"session\", WebRequest.SCOPE_SESSION);//这个是放到session范围内的,如果环境允许的话它只能在局部的隔离的会话中访问,否则就是在普通的当前会话中可以访问 </p><p> request.setAttribute(\"globalSession\", \"globalSession\", WebRequest.SCOPE_GLOBAL_SESSION);//如果环境允许的话,它能在全局共享的会话中访问,否则就是在普通的当前会话中访问 </p><p> }</p><p> /** </p><p> * 该方法将在Controller执行之后,返回视图之前执行,ModelMap表示请求Controller处理之后返回的Model对象,所以可以在 </p><p> * 这个方法中修改ModelMap的属性,从而达到改变返回的模型的效果。 </p><p> */</p><p> @Override</p><p> public void postHandle(WebRequest request, ModelMap map) throws Exception {</p><p> // TODO Auto-generated method stub </p><p> for (String key:map.keySet())</p><p> System.out.println(key + \"-------------------------\");;</p><p> map.put(\"name3\", \"value3\");</p><p> map.put(\"name1\", \"name1\");</p><p> }</p><p> /** </p><p> * 该方法将在整个请求完成之后,也就是说在视图渲染之后进行调用,主要用于进行一些资源的释放 </p><p> */</p><p> @Override</p><p> public void afterCompletion(WebRequest request, Exception exception)</p><p> throws Exception {</p><p> // TODO Auto-generated method stub </p><p> System.out.println(exception + \"-=-=--=--=-=-=-=-=-=-=-=-==-=--=-=-=-=\");</p><p> }</p><p>}</p><p><br></p><p> 二、把定义的拦截器类加到SpringMVC的拦截体系中</p><p>1.在SpringMVC的配置文件中加上支持MVC的schema</p><p><br></p><p>xmlns:mvc=\"http://www.springframework.org/schema/mvc\"</p><p>xsi:schemaLocation=\" http://www.springframework.org/schema/mvc</p><p> http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd\"</p><p>下面是我的声明示例:</p><p><br></p><p><br></p><p><br></p><p>这样在SpringMVC的配置文件中就可以使用mvc标签了,mvc标签中有一个mvc:interceptors是用于声明SpringMVC的拦截器的。</p><p><br></p><p> </p><p><br></p><p>(二)使用mvc:interceptors标签来声明需要加入到SpringMVC拦截器链中的拦截器</p><p><br></p><p><br></p><p> </p><p> </p><p> </p><p> </p><p> </p><p> </p><p> </p><p><br></p><p><br></p><p>由上面的示例可以看出可以利用mvc:interceptors标签声明一系列的拦截器,然后它们就可以形成一个拦截器链,拦截器的执行顺序是按声明的先后顺序执行的,先声明的拦截器中的preHandle方法会先执行,然而它的postHandle方法和afterCompletion方法却会后执行。</p><p><br></p><p>在mvc:interceptors标签下声明interceptor主要有两种方式:</p><p><br></p><p>(1)直接定义一个Interceptor实现类的bean对象。使用这种方式声明的Interceptor拦截器将会对所有的请求进行拦截。</p><p><br></p><p>(2)使用mvc:interceptor标签进行声明。使用这种方式进行声明的Interceptor可以通过mvc:mapping子标签来定义需要进行拦截的请求路径。</p><p><br></p><p>经过上述两步之后,定义的拦截器就会发生作用对特定的请求进行拦截了。</p><p><br></p><p><br></p><p><br></p>', '', '', '', '', '', '', '2018-11-25 20:51:52', '2017-10-06 23:54:18', null);
INSERT INTO `article` VALUES ('', '', 'springmvc 表单中文乱码解决方案', '<p>基本上通过在 web.xml 了配置拦截器就可以解决。</p><p>这里需要注意的是,最好把这段代码放在web.xml中开头的位置,因为拦截有顺序,如果放在后面的话容易拦截不到。</p><p>拦截器代码如下</p><p></p><div><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol start=\"1\" class=\"dp-xml\"><li class=\"alt\"><span><span class=\"comments\"><!--post乱码过滤器--></span><span> </span></span></li><li class=\"\"><span><span class=\"comments\"><!-- 配置springMVC编码过滤器 --></span><span> </span></span></li><li class=\"alt\"><span><span class=\"tag\"><</span><span class=\"tag-name\">filter</span><span class=\"tag\">></span><span> </span></span></li><li class=\"\"><span> <span class=\"tag\"><</span><span class=\"tag-name\">filter-name</span><span class=\"tag\">></span><span>CharacterEncodingFilter</span><span class=\"tag\"><!--</span--><span class=\"tag-name\">filter-name</span><span class=\"tag\">></span><span> </span></span></span></li><li class=\"alt\"><span> <span class=\"tag\"><</span><span class=\"tag-name\">filter-class</span><span class=\"tag\">></span><span>org.springframework.web.filter.CharacterEncodingFilter</span><span class=\"tag\"><!--</span--><span class=\"tag-name\">filter-class</span><span class=\"tag\">></span><span> </span></span></span></li><li class=\"\"><span> <span class=\"comments\"><!-- 设置过滤器中的属性值 --></span><span> </span></span></li><li class=\"alt\"><span> <span class=\"tag\"><</span><span class=\"tag-name\">init-param</span><span class=\"tag\">></span><span> </span></span></li><li class=\"\"><span> <span class=\"tag\"><</span><span class=\"tag-name\">param-name</span><span class=\"tag\">></span><span>encoding</span><span class=\"tag\"><!--</span--><span class=\"tag-name\">param-name</span><span class=\"tag\">></span><span> </span></span></span></li><li class=\"alt\"><span> <span class=\"tag\"><</span><span class=\"tag-name\">param-value</span><span class=\"tag\">></span><span>UTF-8</span><span class=\"tag\"><!--</span--><span class=\"tag-name\">param-value</span><span class=\"tag\">></span><span> </span></span></span></li><li class=\"\"><span> <span class=\"tag\"><!--</span--><span class=\"tag-name\">init-param</span><span class=\"tag\">></span><span> </span></span></span></li><li class=\"alt\"><span> <span class=\"comments\"><!-- 启动过滤器 --></span><span> </span></span></li><li class=\"\"><span> <span class=\"tag\"><</span><span class=\"tag-name\">init-param</span><span class=\"tag\">></span><span> </span></span></li><li class=\"alt\"><span> <span class=\"tag\"><</span><span class=\"tag-name\">param-name</span><span class=\"tag\">></span><span>forceEncoding</span><span class=\"tag\"><!--</span--><span class=\"tag-name\">param-name</span><span class=\"tag\">></span><span> </span></span></span></li><li class=\"\"><span> <span class=\"tag\"><</span><span class=\"tag-name\">param-value</span><span class=\"tag\">></span><span>true</span><span class=\"tag\"><!--</span--><span class=\"tag-name\">param-value</span><span class=\"tag\">></span><span> </span></span></span></li><li class=\"alt\"><span> <span class=\"tag\"><!--</span--><span class=\"tag-name\">init-param</span><span class=\"tag\">></span><span> </span></span></span></li><li class=\"\"><span><span class=\"tag\"><!--</span--><span class=\"tag-name\">filter</span><span class=\"tag\">></span><span> </span></span></span></li><li class=\"alt\"><span><span class=\"comments\"><!-- 过滤所有请求 --></span><span> </span></span></li><li class=\"\"><span><span class=\"tag\"><</span><span class=\"tag-name\">filter-mapping</span><span class=\"tag\">></span><span> </span></span></li><li class=\"alt\"><span> <span class=\"tag\"><</span><span class=\"tag-name\">filter-name</span><span class=\"tag\">></span><span>CharacterEncodingFilter</span><span class=\"tag\"><!--</span--><span class=\"tag-name\">filter-name</span><span class=\"tag\">></span><span> </span></span></span></li><li class=\"\"><span> <span class=\"tag\"><</span><span class=\"tag-name\">url-pattern</span><span class=\"tag\">></span><span>/*</span><span class=\"tag\"><!--</span--><span class=\"tag-name\">url-pattern</span><span class=\"tag\">></span><span> </span></span></span></li><li class=\"alt\"><span><span class=\"tag\"><!--</span--><span class=\"tag-name\">filter-mapping</span><span class=\"tag\">></span><span> </span></span></span></li></ol></div></div><p><br></p><p>顺便再补充其他的几个可能原因。</p><p></p><p>1、数据库和数据表不是 utf-8 编码</p><p>就在前几天,我遇到的问题正是这个。当时是刚从 windows 搬到 mac。也是在提交 post 表单的时候,中文一直是乱码,后台百度发现,原来是 navicat 的原因。就是在新建 数据库连接(注意是连接),不能选择 utf-8,应该选择默认的自动。这个地方很坑人。</p><p>数据库和数据表当然是 utf-8,一般这种情况很少出错。</p><p> </p><p>2、修改 Tomcat 的 server.xml 文件,添加 utf-8 编码</p><p></p><div><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol start=\"1\" class=\"dp-j\"><li class=\"alt\"><span><span><connector port=< span=\"\"><span class=\"string\">\"8080\"</span><span> protocol=</span><span class=\"string\">\"HTTP/1.1\"</span><span> </span></connector port=<></span></span></li><li class=\"\"><span> connectionTimeout=<span class=\"string\">\"20000\"</span><span> </span></span></li><li class=\"alt\"><span> redirectPort=<span class=\"string\">\"8443\"</span><span> URIEncoding=</span><span class=\"string\">\"UTF-8\"</span><span> </span></span></li><li class=\"\"><span> useBodyEncodingForURI=<span class=\"string\">\"true\"</span><span> /> </span></span></li></ol></div></div><br><p></p><p>3、注意你的 IDE (如Eclipse,IntelliJ IDEA)的 jsp 文件编码,一般在右下角可以看出来,通常我们也要把默认的编码设置为 utf-8。</p><p> </p><p>4、还有一种方法就是 直接对接收到的数据编码转换,我感觉有点麻烦,所以不是很喜欢</p><p></p><div><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol start=\"1\" class=\"dp-j\"><li class=\"alt\"><span><span>String name=</span><span class=\"keyword\">new</span><span> String((user.getUname()).getBytes(</span><span class=\"string\">\"ISO-8859-1\"</span><span>),</span><span class=\"string\">\"utf8\"</span><span>); </span></span></li></ol></div></div><p><span>总结:一般来说,第一种拦截器方法是可用的,要注意的是要把拦截器代码放到 web.xml 头部,防止拦截不到,还有表单一定要 post 方式提交。</span></p><p></p>', '', '', '', '', '', '', '2018-11-25 20:52:13', '2017-10-07 12:12:42', null);
INSERT INTO `article` VALUES ('', '', '[转载]HTTP的幂等性', '<div id=\"header\"><h2>理解HTTP幂等性</h2></div><div id=\"post_detail\"><div class=\"post\"><div class=\"postText\"><div id=\"cnblogs_post_body\"><p>基于HTTP协议的Web API是时下最为流行的一种分布式服务提供方式。无论是在大型互联网应用还是企业级架构中,我们都见到了越来越多的SOA或RESTful的Web API。为什么Web API如此流行呢?我认为很大程度上应归功于简单有效的HTTP协议。HTTP协议是一种分布式的面向资源的网络应用层协议,无论是服务器端提供Web服务,还是客户端消费Web服务都非常简单。再加上浏览器、<a href=\"https://liuyanzhao.com/tag/java/\" title=\"查看与 Java 相关的文章\" target=\"_blank\">Java</a>script、AJAX、JSON以及HTML5等技术和工具的发展,互联网应用架构设计表现出了从传统的PHP、JSP、ASP.NET等服务器端动态网页向Web API + RIA(富互联网应用)过渡的趋势。Web API专注于提供业务服务,RIA专注于用户界面和交互设计,从此两个领域的分工更加明晰。在这种趋势下,Web API设计将成为服务器端程序员的必修课。然而,正如简单的<a href=\"https://liuyanzhao.com/tag/java/\" title=\"查看与 Java 相关的文章\" target=\"_blank\">Java</a>语言并不意味着高质量的<a href=\"https://liuyanzhao.com/tag/java/\" title=\"查看与 Java 相关的文章\" target=\"_blank\">Java</a>程序,简单的HTTP协议也不意味着高质量的Web API。要想设计出高质量的Web API,还需要深入理解分布式系统及HTTP协议的特性。</p><h2>幂等性定义</h2><p>本文所要探讨的正是HTTP协议涉及到的一种重要性质:幂等性(Idempotence)。在HTTP/1.1规范中幂等性的定义是:</p><blockquote><p>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.</p></blockquote><p>从定义上看,HTTP方法的幂等性是指一次和多次请求某一个资源应该具有同样的副作用。幂等性属于语义范畴,正如编译器只能帮助检查语法错误一样,HTTP规范也没有办法通过消息格式等语法手段来定义它,这可能是它不太受到重视的原因之一。但实际上,幂等性是分布式系统设计中十分重要的概念,而HTTP的分布式本质也决定了它在HTTP中具有重要地位。</p><h2>分布式事务 vs 幂等设计</h2><p>为什么需要幂等性呢?我们先从一个例子说起,假设有一个从账户取钱的远程API(可以是HTTP的,也可以不是),我们暂时用类函数的方式记为:</p><pre><code>bool withdraw(account_id, amount)\r\n</code></pre><p>withdraw的语义是从account_id对应的账户中扣除amount数额的钱;如果扣除成功则返回true,账户余额减少amount;如果扣除失败则返回false,账户余额不变。值得注意的是:和本地环境相比,我们不能轻易假设分布式环境的可靠性。一种典型的情况是withdraw请求已经被服务器端正确处理,但服务器端的返回结果由于网络等原因被掉丢了,导致客户端无法得知处理结果。如果是在网页上,一些不恰当的设计可能会使用户认为上一次操作失败了,然后刷新页面,这就导致了withdraw被调用两次,账户也被多扣了一次钱。如图1所示:</p><p><img src=\"/uploads/2017/10/20171007153621950.png\" alt=\"20171007153621950.png\"><br></p><p>图1</p><p>这个问题的解决方案一是采用分布式事务,通过引入支持分布式事务的中间件来保证withdraw功能的事务性。分布式事务的优点是对于调用者很简单,复杂性都交给了中间件来管理。缺点则是一方面架构太重量级,容易被绑在特定的中间件上,不利于异构系统的集成;另一方面分布式事务虽然能保证事务的ACID性质,而但却无法提供性能和可用性的保证。</p><p>另一种更轻量级的解决方案是幂等设计。我们可以通过一些技巧把withdraw变成幂等的,比如:</p><pre><code>int create_ticket() \r\nbool idempotent_withdraw(ticket_id, account_id, amount)\r\n</code></pre><p>create_ticket的语义是获取一个服务器端生成的唯一的处理号ticket_id,它将用于标识后续的操作。idempotent_withdraw和withdraw的区别在于关联了一个ticket_id,一个ticket_id表示的操作至多只会被处理一次,每次调用都将返回第一次调用时的处理结果。这样,idempotent_withdraw就符合幂等性了,客户端就可以放心地多次调用。</p><p>基于幂等性的解决方案中一个完整的取钱流程被分解成了两个步骤:1.调用create_ticket()获取ticket_id;2.调用idempotent_withdraw(ticket_id, account_id, amount)。虽然create_ticket不是幂等的,但在这种设计下,它对系统状态的影响可以忽略,加上idempotent_withdraw是幂等的,所以任何一步由于网络等原因失败或超时,客户端都可以重试,直到获得结果。如图2所示:</p><p><img src=\"/uploads/2017/10/20171007153648208.png\" alt=\"20171007153648208.png\"><br></p><div id=\"cnblogs_post_body\"><p>图2</p><p>和分布式事务相比,幂等设计的优势在于它的轻量级,容易适应异构环境,以及性能和可用性方面。在某些性能要求比较高的应用,幂等设计往往是唯一的选择。</p><h2>HTTP的幂等性</h2><p>HTTP协议本身是一种面向资源的应用层协议,但对HTTP协议的使用实际上存在着两种不同的方式:一种是RESTful的,它把HTTP当成应用层协议,比较忠实地遵守了HTTP协议的各种规定;另一种是SOA的,它并没有完全把HTTP当成应用层协议,而是把HTTP协议作为了传输层协议,然后在HTTP之上建立了自己的应用层协议。本文所讨论的HTTP幂等性主要针对RESTful风格的,不过正如上一节所看到的那样,幂等性并不属于特定的协议,它是分布式系统的一种特性;所以,不论是SOA还是RESTful的Web API设计都应该考虑幂等性。下面将介绍HTTP GET、DELETE、PUT、POST四种主要方法的语义和幂等性。</p><p>HTTP GET方法用于获取资源,不应有副作用,所以是幂等的。比如:GET http://www.bank.com/account/123456,不会改变资源的状态,不论调用一次还是N次都没有副作用。请注意,这里强调的是一次和N次具有相同的副作用,而不是每次GET的结果相同。GET http://www.news.com/latest-news这个HTTP请求可能会每次得到不同的结果,但它本身并没有产生任何副作用,因而是满足幂等性的。</p><p>HTTP DELETE方法用于删除资源,有副作用,但它应该满足幂等性。比如:DELETE http://www.forum.com/article/4231,调用一次和N次对系统产生的副作用是相同的,即删掉id为4231的帖子;因此,调用者可以多次调用或刷新页面而不必担心引起错误。</p><p>比较容易混淆的是HTTP POST和PUT。POST和PUT的区别容易被简单地误认为“POST表示创建资源,PUT表示更新资源”;而实际上,二者均可用于创建资源,更为本质的差别是在幂等性方面。在HTTP规范中对POST和PUT是这样定义的:</p><blockquote><p>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.</p><p>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.</p></blockquote><p>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方法具有幂等性。</p><p>在介绍了几种操作的语义和幂等性之后,我们来看看如何通过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中。这种模式可以应用于很多场合,比如:论坛网站中防止意外的重复发帖。</p><h2>总结</h2><p>上面简单介绍了幂等性的概念,用幂等设计取代分布式事务的方法,以及HTTP主要方法的语义和幂等性特征。其实,如果要追根溯源,幂等性是数学中的一个概念,表达的是N次变换与1次变换的结果相同,有兴趣的读者可以从Wikipedia上进一步了解。</p><h2>参考</h2><p><a href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html\">RFC 2616, Hypertext Transfer Protocol -- HTTP/1.1, Method Definitions</a></p><p><a href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=http://devhawk.net/2007/11/09/the-importance-of-idempotence/\">The Importance of Idempotence</a></p><p><a href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=http://stackoverflow.com/questions/630453/put-vs-post-in-rest\">Stackoverflow - PUT vs POST in REST</a></p></div><p> </p><p> </p><p>原文地址:<a href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=http://www.cnblogs.com/weidagang2046/archive/2011/06/04/idempotence.html\" target=\"_blank\" rel=\"noopener noreferrer\">http://www.cnblogs.com/weidagang2046/archive/2011/06/04/idempotence.html</a></p></div></div></div></div>', '', '', '', '', '', '', '2018-11-25 20:51:05', '2017-10-07 15:37:20', null);
INSERT INTO `article` VALUES ('', '', '使用rapid-framework继承jsp页面', '<p>对于多张网页页面,许多部分都是相同的,应该继承同一个页面,该页面是所有页面的父页面。<br></p><p>后来搜了一下找到一个<code>rapid-framework</code>的东西,由于我使用的是<code>maven</code>,所以引入很简单。</p><p></p><div><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol start=\"1\" class=\"dp-xml\"><li class=\"alt\"><span class=\"tag\"><</span><span class=\"tag-name\">dependency</span><span class=\"tag\">></span> </li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">groupId</span><span class=\"tag\">></span>com.googlecode.rapid-framework<span class=\"tag\"><!--</span--><span class=\"tag-name\">groupId</span><span class=\"tag\">></span> </span></li><li class=\"alt\"> <span class=\"tag\"><</span><span class=\"tag-name\">artifactId</span><span class=\"tag\">></span>rapid-core<span class=\"tag\"><!--</span--><span class=\"tag-name\">artifactId</span><span class=\"tag\">></span> </span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">version</span><span class=\"tag\">></span>4.0.5<span class=\"tag\"><!--</span--><span class=\"tag-name\">version</span><span class=\"tag\">></span> </span></li><li class=\"alt\"><span class=\"tag\"><!--</span--><span class=\"tag-name\">dependency</span><span class=\"tag\">></span> </span></li></ol></div></div><p>父页面</p><p></p><div><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol start=\"1\" class=\"dp-xml\"><li class=\"alt\">... </li><li class=\"\"><span class=\"tag\"><</span>%@ taglib <span class=\"attribute\">prefix</span>=<span class=\"attribute-value\">\"rapid\"</span> <span class=\"attribute\">uri</span>=<span class=\"attribute-value\">\"http://www.rapid-framework.org.cn/rapid\"</span> %<span class=\"tag\">></span> </li><li class=\"alt\">... </li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">body</span><span class=\"tag\">></span> </li><li class=\"alt\"> <span class=\"comments\"><!-- 正文 --></span> </li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">rapid:block</span> <span class=\"attribute\">name</span>=<span class=\"attribute-value\">\"content\"</span><span class=\"tag\">></span> </li><li class=\"alt\"> 123 </li><li class=\"\"> <span class=\"tag\"><!--</span--><span class=\"tag-name\">rapid:block</span><span class=\"tag\">></span> </span></li><li class=\"alt\"><span class=\"tag\"><!--</span--><span class=\"tag-name\">body</span><span class=\"tag\">></span> </span></li></ol></div></div><p></p><p></p><ul></ul><p> <code><rapid:block name=\"content\"></rapid:block></code>定义叫做<code>content</code>的一块,该部分可以让子页面重写。</p><p></p><p>子页面</p><p></p><div><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol start=\"1\" class=\"dp-xml\"><li class=\"alt\"><span class=\"tag\"><</span>%@ page <span class=\"attribute\">contentType</span>=<span class=\"attribute-value\">\"text/html;charset=UTF-8\"</span> <span class=\"attribute\">language</span>=<span class=\"attribute-value\">\"java\"</span> %<span class=\"tag\">></span> </li><li class=\"\"><span class=\"tag\"><</span>%@ taglib <span class=\"attribute\">prefix</span>=<span class=\"attribute-value\">\"rapid\"</span> <span class=\"attribute\">uri</span>=<span class=\"attribute-value\">\"http://www.rapid-framework.org.cn/rapid\"</span> %<span class=\"tag\">></span> </li><li class=\"alt\"><span class=\"tag\"><</span><span class=\"tag-name\">rapid:override</span> <span class=\"attribute\">name</span>=<span class=\"attribute-value\">\"content\"</span><span class=\"tag\">></span> </li><li class=\"\"> 这是内容 </li><li class=\"alt\"><span class=\"tag\"><!--</span--><span class=\"tag-name\">rapid:override</span><span class=\"tag\">></span> </span></li><li class=\"\"><span class=\"tag\"><</span>%@ include <span class=\"attribute\">file</span>=<span class=\"attribute-value\">\"base.jsp\"</span>%<span class=\"tag\">></span> </li></ol></div></div><p></p><p></p><li> <%@ include file=\"base.jsp\"%>引入父页面</li><p></p><li>重写父页面中叫做content的部分。</li><p></p><li>访问子页面,父页面内部的元素不会显示,代替的是子页面重写的内容。</li>', '', '', '', '', '', '', '2018-11-25 20:54:02', '2017-10-07 15:30:17', null);
INSERT INTO `article` VALUES ('', '', 'JDBC常用API和使用', '<h2>一、JDBC 常用 API</h2><p><strong>1、Connection 接口</strong></p><table width=\"695\" class=\"layui-table\"><tbody><tr><td>方法名称</td><td>功能描述</td></tr><tr><td>getMetaData()</td><td>该方法用于返回数据库的元数据的 DatabaseMetaData 对象</td></tr><tr><td>createStatement</td><td>用于创建一个 Statement 对象来将 SQL 语句发送到数据库</td></tr><tr><td>preparedStatement(String sql)</td><td>用于创建一个 PreparedStatement 对象来将参数化的SQL语句发送到数据库</td></tr><tr><td>prepareCall(String sql)</td><td>用于创建一个 CallableStatement 对象来调用数据库存储过程</td></tr></tbody></table><p><strong>2、Statement 接口</strong></p><table width=\"696\" class=\"layui-table\"><tbody><tr><td>方法名称</td><td>功能描述</td></tr><tr><td>boolean execute(String sql)</td><td>用于执行各种 SQL 语句,该方法返回一个 boolean 类型的值。如果为 true,表示所执行的 SQL 语句具备查询结果,可通过 Statement 的getResultSet() 方法查询结果</td></tr><tr><td>int executeUpdate(String sql)</td><td>用于执行 SQL 中的 insert、update 和 delete 语句,该方法返回一个 int 类型的值,表示影响数据库中的行数</td></tr><tr><td>ResultSet executeQuery(String sql)</td><td>用于执行 SQL 中的 select 语句(查询,遍历),该方法返回一个表示查询结果的 ResultSet 对象</td></tr></tbody></table><p><strong> <span>execute是executeQuery和executeUpdate的综合.</span></strong></p><p><strong><span>通常我们没有必要使用execute方法来执行SQL语句,而是使用 executeQuery 或 executeUpdate 更适合。</span></strong></p><p>具体可参考:<a href=\"https://liuyanzhao.com/5278.html\" target=\"_blank\" rel=\"noopener noreferrer\">execute、executeUpdate、executeQuery三者的区别</a></p><p><strong>3、PreparedStatement 接口</strong></p><table width=\"695\" class=\"layui-table\"><tbody><tr><td>方法名称</td><td>功能描述</td></tr><tr><td>executeUpdate()</td><td>在此 PreparedStatement 对象中执行 SQL 语句,该语句必须是一个 DML 语句,或者无返回内容的 SQL 语句,比如 DDL 语句</td></tr><tr><td>executeQuery()</td><td>在此 PreparedStatement 对象中执行 SQL 语句,该方法返回的是 ResultSet 对象</td></tr><tr><td>setInt(int parameterIndex, int x)</td><td>将指定的参数设置为 int 值</td></tr><tr><td>setFloat(int parameterIndex, float x)</td><td>将指定的参数设置为 Float 值</td></tr><tr><td>setString(int parameterIndex, String x)</td><td>将指定参数设置的给定的 Date 值</td></tr><tr><td>setDate(int parameterIndex, Date x)</td><td>将指定参数设置给定的 Date 值</td></tr><tr><td>addBatch()</td><td>将一组参数添加到此 PreparedStatement 对象的批处理命令中</td></tr><tr><td>setCharacterStream(parameterIndex, reader, length)</td><td>将指定的输入流写入数据库的文本字段</td></tr><tr><td>setBinaryStream(parameterIndex, x, length)</td><td>将二进制的输入流数据写入到二进制的字段中</td></tr></tbody></table><p>DML 语句:SELECT、UPDATE、INSERT、DELETE</p><p>DLL 语句:CREATE DROP ALERT</p><p>具体参考: <a href=\"https://liuyanzhao.com/5283.html\" target=\"_blank\" rel=\"noopener noreferrer\">sql语句分为三类(DML,DDL,DCL)-介绍</a></p><p>4、ResultSet 接口</p><table class=\"layui-table\"><tbody><tr><td>getString(int columnIndex)</td><td>用于获取 指定字段的 String 类型的值,参数 columnIndex 代表字段的索引</td></tr><tr><td>getString(String columnName)</td><td> 用于获取指定字段的 String 类型的值,参数 columnIndex 代表字段名称</td></tr><tr><td>getInt(int columnIndex)</td><td> 用于获取指定字段的 int 类型的值,参数 columnIndex 代表字段的索引</td></tr><tr><td>getInt(String columnName)</td><td> 用于获取指定字段的 int 类型的值,参数 columnIndex 代表字段名称</td></tr><tr><td>getDate(int columnIndex)</td><td> 用于获取指定字段的 Date类型的值,参数 columnIndex 代表字段索引</td></tr><tr><td>getDate(String columnName)</td><td> 用于获取指定字段的 Date类型的值,参数 columnIndex 代表字段名称</td></tr><tr><td>next()</td><td> 将游标从当前位置移到下一位置</td></tr><tr><td>absolute(int row)</td><td>将游标移到此 ResultSet 对象的指定行</td></tr><tr><td>afterLast()</td><td>将游标移动到此 ResultSet 对象的末尾,即最后一行之后</td></tr><tr><td>beforeFirst()</td><td>将游标移动到此 ResultSet 对象开头,即第一行之前</td></tr><tr><td>previous()</td><td>将游标移动到此 ResultSet 对象的上一行</td></tr><tr><td>last()</td><td>将游标移动到此 Result 对象的最后一行</td></tr></tbody></table><h2>二、案例</h2><p><strong>1、往数据库里添加数据</strong></p><p>① 数据表结构如下</p><p><img src=\"/uploads/2017/10/2017100715402014.png\" alt=\"2017100715402014.png\"><br></p><p><span>② 代码如下</span></p><p><span></span></p><div><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol start=\"1\" class=\"dp-j\"><li class=\"alt\"><span><span class=\"keyword\">package</span><span> com.liuyanzhao; </span></span></li><li class=\"\"><span><span class=\"keyword\">import</span><span> java.sql.Connection; </span></span></li><li class=\"alt\"><span><span class=\"keyword\">import</span><span> java.sql.Date; </span></span></li><li class=\"\"><span><span class=\"keyword\">import</span><span> java.sql.DriverManager; </span></span></li><li class=\"alt\"><span><span class=\"keyword\">import</span><span> java.sql.PreparedStatement; </span></span></li><li class=\"\"><span><span class=\"keyword\">import</span><span> java.sql.SQLException; </span></span></li><li class=\"alt\"><span><span class=\"keyword\">import</span><span> java.text.ParseException; </span></span></li><li class=\"\"><span><span class=\"keyword\">import</span><span> java.text.SimpleDateFormat; </span></span></li><li class=\"alt\"><span><span class=\"comment\">/*</span> </span></li><li class=\"\"><span><span class=\"comment\"> * @author LiuYanzhao</span> </span></li><li class=\"alt\"><span><span class=\"comment\"> */</span><span> </span></span></li><li class=\"\"><span><span class=\"keyword\">public</span><span> </span><span class=\"keyword\">class</span><span> Demo1 { </span></span></li><li class=\"alt\"><span> <span class=\"keyword\">private</span><span> </span><span class=\"keyword\">static</span><span> </span><span class=\"keyword\">final</span><span> String URL = </span><span class=\"string\">\"jdbc:mysql://127.0.0.1:3306/jdbc_study?useUnicode=true&characterEncoding=utf8\"</span><span>; </span></span></li><li class=\"\"><span> <span class=\"keyword\">private</span><span> </span><span class=\"keyword\">static</span><span> </span><span class=\"keyword\">final</span><span> String USER = </span><span class=\"string\">\"root\"</span><span>; </span></span></li><li class=\"alt\"><span> <span class=\"keyword\">private</span><span> </span><span class=\"keyword\">static</span><span> </span><span class=\"keyword\">final</span><span> String PASSWORD = </span><span class=\"string\">\"\"</span><span>; </span></span></li><li class=\"\"><span> <span class=\"keyword\">public</span><span> </span><span class=\"keyword\">static</span><span> Connection conn = </span><span class=\"keyword\">null</span><span>; </span></span></li><li class=\"alt\"><span> <span class=\"keyword\">public</span><span> </span><span class=\"keyword\">static</span><span> </span><span class=\"keyword\">void</span><span> main(String[] args) </span><span class=\"keyword\">throws</span><span> ClassNotFoundException, SQLException, ParseException { </span></span></li><li class=\"\"><span> <span class=\"comment\">//1、加载驱动,需要提前把 jar 包添加到 classpath 中</span><span> </span></span></li><li class=\"alt\"><span> Class.forName(<span class=\"string\">\"com.mysql.jdbc.Driver\"</span><span>); </span></span></li><li class=\"\"><span> <span class=\"comment\">//2、创建应用程序与数据库连接的 Connection 对象</span><span> </span></span></li><li class=\"alt\"><span> conn = DriverManager.getConnection(URL, USER, PASSWORD); </span></li><li class=\"\"><span> <span class=\"comment\">//3、要执行的 sql 语句:name,password,email,status通过占位符填数,create_date 自动为当前时间</span><span> </span></span></li><li class=\"alt\"><span> String sql = <span class=\"string\">\" INSERT INTO users\"</span><span>+ </span></span></li><li class=\"\"><span> <span class=\"string\">\"(name,password,birthday,email,create_date,status) \"</span><span>+ </span></span></li><li class=\"alt\"><span> <span class=\"string\">\"VALUES(\"</span><span>+ </span></span></li><li class=\"\"><span> <span class=\"string\">\"?,?,?,?,current_date(),?)\"</span><span>; </span></span></li><li class=\"alt\"><span> <span class=\"comment\">//4、创建执行 SQL 语句的 PreparedStatement 对象</span><span> </span></span></li><li class=\"\"><span> PreparedStatement ptmt = conn.prepareStatement(sql); </span></li><li class=\"alt\"><span> ptmt.setString(<span class=\"number\">1</span><span>, </span><span class=\"string\">\"小美\"</span><span>); </span></span></li><li class=\"\"><span> ptmt.setString(<span class=\"number\">2</span><span>, </span><span class=\"string\">\"123456\"</span><span>); </span></span></li><li class=\"alt\"><span> ptmt.setDate(<span class=\"number\">3</span><span>,</span><span class=\"keyword\">new</span><span> Date((</span><span class=\"keyword\">new</span><span> SimpleDateFormat(</span><span class=\"string\">\"yyyy-MM-dd\"</span><span>).parse(</span><span class=\"string\">\"2011-10-1\"</span><span>)).getTime()) ); </span></span></li><li class=\"\"><span> ptmt.setString(<span class=\"number\">4</span><span>, </span><span class=\"string\">\"[email protected]\"</span><span>); </span></span></li><li class=\"alt\"><span> ptmt.setInt(<span class=\"number\">5</span><span>, </span><span class=\"number\">1</span><span>); </span></span></li><li class=\"\"><span> <span class=\"comment\">//5、真正执行 sql 语句,并返回影响的行数</span><span> </span></span></li><li class=\"alt\"><span> <span class=\"keyword\">int</span><span> x = ptmt.executeUpdate(); </span></span></li><li class=\"\"><span> System.out.println(<span class=\"string\">\"影响行数:\"</span><span> + x); </span><span class=\"comment\">//返回1</span><span> </span></span></li><li class=\"alt\"><span> } </span></li><li class=\"\"><span>} </span></li></ol></div></div><br><p></p><p>③运行结果</p><p>上面的操作,将在 jdbc_study 数据库的 users 表中添加一条记录,终端显示 1</p><p><strong>2、查询(打印)数据库信息</strong></p><p>①数据表如下</p><p><img src=\"/uploads/2017/10/20171007154127663.png\" alt=\"20171007154127663.png\"><br></p><p><span>②代码如下</span></p><p><span></span></p><div><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol start=\"1\" class=\"dp-j\"><li class=\"alt\"><span><span class=\"keyword\">package</span><span> com.liuyanzhao; </span></span></li><li class=\"\"><span><span class=\"keyword\">import</span><span> java.sql.Connection; </span></span></li><li class=\"alt\"><span><span class=\"keyword\">import</span><span> java.sql.Date; </span></span></li><li class=\"\"><span><span class=\"keyword\">import</span><span> java.sql.DriverManager; </span></span></li><li class=\"alt\"><span><span class=\"keyword\">import</span><span> java.sql.PreparedStatement; </span></span></li><li class=\"\"><span><span class=\"keyword\">import</span><span> java.sql.ResultSet; </span></span></li><li class=\"alt\"><span><span class=\"keyword\">import</span><span> java.sql.SQLException; </span></span></li><li class=\"\"><span><span class=\"keyword\">import</span><span> java.text.ParseException; </span></span></li><li class=\"alt\"><span><span class=\"keyword\">import</span><span> java.text.SimpleDateFormat; </span></span></li><li class=\"\"><span><span class=\"comment\">/*</span> </span></li><li class=\"alt\"><span><span class=\"comment\"> * @author LiuYanzhao</span> </span></li><li class=\"\"><span><span class=\"comment\"> */</span><span> </span></span></li><li class=\"alt\"><span><span class=\"keyword\">public</span><span> </span><span class=\"keyword\">class</span><span> Demo2 { </span></span></li><li class=\"\"><span> <span class=\"keyword\">private</span><span> </span><span class=\"keyword\">static</span><span> </span><span class=\"keyword\">final</span><span> String URL = </span><span class=\"string\">\"jdbc:mysql://127.0.0.1:3306/jdbc_study?useUnicode=true&characterEncoding=utf8\"</span><span>; </span></span></li><li class=\"alt\"><span> <span class=\"keyword\">private</span><span> </span><span class=\"keyword\">static</span><span> </span><span class=\"keyword\">final</span><span> String USER = </span><span class=\"string\">\"root\"</span><span>; </span></span></li><li class=\"\"><span> <span class=\"keyword\">private</span><span> </span><span class=\"keyword\">static</span><span> </span><span class=\"keyword\">final</span><span> String PASSWORD = </span><span class=\"string\">\"\"</span><span>; </span></span></li><li class=\"alt\"><span> <span class=\"keyword\">public</span><span> </span><span class=\"keyword\">static</span><span> Connection conn = </span><span class=\"keyword\">null</span><span>; </span></span></li><li class=\"\"><span> <span class=\"keyword\">public</span><span> </span><span class=\"keyword\">static</span><span> </span><span class=\"keyword\">void</span><span> main(String[] args) </span><span class=\"keyword\">throws</span><span> ClassNotFoundException, SQLException, ParseException { </span></span></li><li class=\"alt\"><span> <span class=\"comment\">//1、加载驱动,需要提前把 jar 包添加到 classpath 中</span><span> </span></span></li><li class=\"\"><span> Class.forName(<span class=\"string\">\"com.mysql.jdbc.Driver\"</span><span>); </span></span></li><li class=\"alt\"><span> <span class=\"comment\">//2、创建应用程序与数据库连接的 Connection 对象</span><span> </span></span></li><li class=\"\"><span> conn = DriverManager.getConnection(URL, USER, PASSWORD); </span></li><li class=\"alt\"><span> <span class=\"comment\">//3、要执行的 sql 语句:name,password,email,satic 通过占位符填数,create_date 自动为当前时间</span><span> </span></span></li><li class=\"\"><span> String sql = <span class=\"string\">\" SELECT * FROM users\"</span><span>; </span></span></li><li class=\"alt\"><span> <span class=\"comment\">//4、创建执行 SQL 语句的 PreparedStatement 对象</span><span> </span></span></li><li class=\"\"><span> PreparedStatement ptmt = conn.prepareStatement(sql); </span></li><li class=\"alt\"><span> <span class=\"comment\">//5、真正执行 sql 语句,并返回影响的 ResultSet</span><span> </span></span></li><li class=\"\"><span> ResultSet rs = ptmt.executeQuery(); </span></li><li class=\"alt\"><span> <span class=\"comment\">//6、打印 ResultSet 数据集</span><span> </span></span></li><li class=\"\"><span> <span class=\"keyword\">while</span><span>(rs.next()) { </span></span></li><li class=\"alt\"><span> System.out.print(rs.getInt(<span class=\"string\">\"id\"</span><span>)+</span><span class=\"string\">\" \"</span><span>); </span></span></li><li class=\"\"><span> System.out.print(rs.getString(<span class=\"string\">\"name\"</span><span>)+</span><span class=\"string\">\" \"</span><span>); </span></span></li><li class=\"alt\"><span> System.out.print(rs.getString(<span class=\"string\">\"password\"</span><span>)+</span><span class=\"string\">\" \"</span><span>); </span></span></li><li class=\"\"><span> System.out.print(rs.getDate(<span class=\"string\">\"birthday\"</span><span>)+</span><span class=\"string\">\" \"</span><span>); </span></span></li><li class=\"alt\"><span> System.out.print(rs.getString(<span class=\"string\">\"email\"</span><span>)+</span><span class=\"string\">\" \"</span><span>); </span></span></li><li class=\"\"><span> System.out.print(rs.getDate(<span class=\"string\">\"create_date\"</span><span>)+</span><span class=\"string\">\" \"</span><span>); </span></span></li><li class=\"alt\"><span> System.out.print(rs.getInt(<span class=\"string\">\"status\"</span><span>)+</span><span class=\"string\">\" \"</span><span>); </span></span></li><li class=\"\"><span> System.out.println(); </span></li><li class=\"alt\"><span> } </span></li><li class=\"\"><span> } </span></li><li class=\"alt\"><span>} </span></li></ol></div></div><p>③ 运行结果</p><p></p><p>控制台上可以看到如下界面</p><p><img src=\"/uploads/2017/10/20171007154222212.jpg\" alt=\"20171007154222212.jpg\"><br></p>', '', '', '', '', '', '', '2018-11-25 20:51:27', '2017-10-07 15:42:43', null);
INSERT INTO `article` VALUES ('', '', 'Hibernate 简单的CURD操作', '<h2>一、单表操作 CURD 实例</h2><ul><li>save</li><li>update</li><li>delete</li><li>get/load (查询单个记录)</li></ul><p> </p><h2>二、代码实现</h2><p><strong>StudentTest.java</strong></p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"annotation\">@Test</span></li><li class=\"\"><span class=\"keyword\">public</span> <span class=\"keyword\">void</span> testSaveStudnets() {</li><li class=\"alt\"> <span class=\"comment\">//生成学生对象</span></li><li class=\"\"> Student s = <span class=\"keyword\">new</span> Student();</li><li class=\"alt\"> s.setName(<span class=\"string\">\"陶渊明\"</span>);</li><li class=\"\"> s.setSex(<span class=\"number\">1</span>);</li><li class=\"alt\"> s.setBirthday(<span class=\"keyword\">new</span> Date());</li><li class=\"\"> Address address = <span class=\"keyword\">new</span> Address(<span class=\"string\">\"332000\"</span>,<span class=\"string\">\"13512345678\"</span>,<span class=\"string\">\"江西九江\"</span>);</li><li class=\"alt\"> s.setAddress(address);</li><li class=\"\"> session.save(s);<span class=\"comment\">//保存对象进入数据库</span></li><li class=\"alt\">}</li><li class=\"\"></li><li class=\"alt\"><span class=\"annotation\">@Test</span></li><li class=\"\"><span class=\"keyword\">public</span> <span class=\"keyword\">void</span> testGetStudents() {</li><li class=\"alt\"> Student s = (Student) session.get(Student.<span class=\"keyword\">class</span>, <span class=\"number\">1</span>);</li><li class=\"\"> System.out.println(s);</li><li class=\"alt\">}</li><li class=\"\"></li><li class=\"alt\"><span class=\"annotation\">@Test</span></li><li class=\"\"><span class=\"keyword\">public</span> <span class=\"keyword\">void</span> testLoadStudents() {</li><li class=\"alt\"> Student s = (Student) session.load(Student.<span class=\"keyword\">class</span>, <span class=\"number\">1</span>);</li><li class=\"\"> System.out.println(s);</li><li class=\"alt\">}</li><li class=\"\"></li><li class=\"alt\"><span class=\"annotation\">@Test</span></li><li class=\"\"><span class=\"keyword\">public</span> <span class=\"keyword\">void</span> testUpdateStudents() {</li><li class=\"alt\"> Student s = (Student) session.get(Student.<span class=\"keyword\">class</span>, <span class=\"number\">1</span>);</li><li class=\"\"> s.setName(<span class=\"string\">\"五柳先生\"</span>);</li><li class=\"alt\"> session.update(s);</li><li class=\"\">}</li><li class=\"alt\"></li><li class=\"\"><span class=\"annotation\">@Test</span></li><li class=\"alt\"><span class=\"keyword\">public</span> <span class=\"keyword\">void</span> testDeleteStudents() {</li><li class=\"\"> Student s = (Student) session.get(Student.<span class=\"keyword\">class</span>, <span class=\"number\">1</span>);</li><li class=\"alt\"> session.delete(s);</li><li class=\"\">}</li></ol></div><p> </p><h2>三、get 和 load 的区别</h2><p><strong>区别一、</strong></p><ul><li>不考虑缓存的情况下,get 方法会在调用之后,立即向数据库发送 sql 语句,返回持久化对象。</li><li>load 方法会在调用后返回一个持久化对象。该代理对象只保留了实体对象的 id,直到使用实体对象的非主键属性时才发出 sql 语句。</li></ul><p><strong>区别二、</strong></p><ul><li>查询数据库中不存在的数据时,get 方法返回 null 。</li><li>load 方法返回异常 org.hibernate.ObjectNotFoundException</li></ul><p>p</p><div><br></div>', '', '', '', '', '', '', '2018-11-25 20:52:33', '2017-10-07 15:49:54', null);
INSERT INTO `article` VALUES ('', '', 'Hibernate 基本类型', '<p>这里介绍几个特殊的 <a href=\"https://liuyanzhao.com/tag/hibernate/\" title=\"查看与 Hibernate 相关的文章\" target=\"_blank\">Hibernate</a> 类型和属性</p><h2>一、日期类型</h2><p>我们的 Student 类有一个成员变量 birthday</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">private</span> Date birthday;<span class=\"comment\">//出生日期</span></li></ol></div><p>在我们的 Studnet.hbm.xml 对应的是自动生成的 属性</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-xml\" start=\"1\"><li class=\"alt\"><span class=\"tag\"><</span><span class=\"tag-name\">property</span> <span class=\"attribute\">name</span>=<span class=\"attribute-value\">\"birthday\"</span> <span class=\"attribute\">type</span>=<span class=\"attribute-value\">\"java.util.Date\"</span><span class=\"tag\">></span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">column</span> <span class=\"attribute\">name</span>=<span class=\"attribute-value\">\"BIRTHDAY\"</span> <span class=\"tag\">/></span></li><li class=\"alt\"><span class=\"tag\"><!--</span--><span class=\"tag-name\">property</span><span class=\"tag\">></span></span></li></ol></div><p>type 表示该字段的类型,不同的类型,会让数据表的字段类型也不同</p><table class=\"layui-table\"><tbody><tr><td>type(映射类型)</td><td>描述</td></tr><tr><td>java.util.Date</td><td>年月日时分秒(2017-07-25 18:20:12)</td></tr><tr><td>date</td><td>年月日(2017-07-25)</td></tr><tr><td>time</td><td>时分秒(18:20:12)</td></tr><tr><td>timestamp</td><td>年月日时分秒(2017-07-25 18:20:12)</td></tr></tbody></table><h2>二、对象类型</h2><p>我们这里使用使用 Blob 类型存照片</p><p>1、Student 类中 成员变量如下</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">private</span> <span class=\"keyword\">int</span> sid;<span class=\"comment\">//学号</span></li><li class=\"\"><span class=\"keyword\">private</span> String name;<span class=\"comment\">//姓名</span></li><li class=\"alt\"><span class=\"keyword\">private</span> <span class=\"keyword\">int</span> sex;<span class=\"comment\">//性别</span></li><li class=\"\"><span class=\"keyword\">private</span> Date birthday;<span class=\"comment\">//出生日期</span></li><li class=\"alt\"><span class=\"keyword\">private</span> String address;<span class=\"comment\">//地址</span></li><li class=\"\"><span class=\"keyword\">private</span> Blob picture;<span class=\"comment\">//照片 </span></li></ol></div><p>2、然后删除原来的 Student.hbm.xml 文件,重新生成</p><p>3、在测试类添加 testWriteBlob方法</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"annotation\">@Test</span></li><li class=\"\"><span class=\"keyword\">public</span> <span class=\"keyword\">void</span> testWriteBlob() <span class=\"keyword\">throws</span> Exception {</li><li class=\"alt\"> Student s = <span class=\"keyword\">new</span> Student(<span class=\"number\">1</span>,<span class=\"string\">\"杜甫\"</span>,<span class=\"number\">1</span>,<span class=\"keyword\">new</span> Date(),<span class=\"string\">\"四川\"</span>);</li><li class=\"\"> <span class=\"comment\">//先获得照片文件</span></li><li class=\"alt\"> File f = <span class=\"keyword\">new</span> File(<span class=\"string\">\"d:\"</span>+File.separator+<span class=\"string\">\"boy.jpg\"</span>);</li><li class=\"\"> <span class=\"comment\">//获得照片文件的输入流</span></li><li class=\"alt\"> InputStream input = <span class=\"keyword\">new</span> FileInputStream(f);</li><li class=\"\"> <span class=\"comment\">//创建Blob对象</span></li><li class=\"alt\"> Blob image = <a href=\"https://liuyanzhao.com/tag/hibernate/\" title=\"查看与 Hibernate 相关的文章\" target=\"_blank\">Hibernate</a>.getLobCreator(session).createBlob(input,input.available());</li><li class=\"\"> <span class=\"comment\">//设置照片属性</span></li><li class=\"alt\"> s.setPicture(image);</li><li class=\"\"> session.save(s);</li><li class=\"alt\">}</li></ol></div><p>注意:这里 Blob 是 java.sql.Blob 包下</p><p>4、然后使用 Junit Test 运行 testWriteBlob 方法</p><p><img src=\"/uploads/2017/10/2017100715505630.png\" alt=\"2017100715505630.png\"><br></p><p>5、为了证明是否真的是写入数据库中,我们重新把照片读出来</p><p>在测试类中添加 testReadBlob 方法</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"annotation\">@Test</span></li><li class=\"\"><span class=\"keyword\">public</span> <span class=\"keyword\">void</span> testReadBlob() <span class=\"keyword\">throws</span> Exception {</li><li class=\"alt\"> Student s = (Student)session.get(Student.<span class=\"keyword\">class</span>, <span class=\"number\">1</span>);</li><li class=\"\"> <span class=\"comment\">//获得Blob对象</span></li><li class=\"alt\"> Blob image = s.getPicture();</li><li class=\"\"> <span class=\"comment\">//获得照片的输入流</span></li><li class=\"alt\"> InputStream input = image.getBinaryStream();</li><li class=\"\"> <span class=\"comment\">//创建输出流</span></li><li class=\"alt\"> File f = <span class=\"keyword\">new</span> File(<span class=\"string\">\"d:\"</span>+File.separator+<span class=\"string\">\"dest.jpg\"</span>);</li><li class=\"\"> <span class=\"comment\">//获得输出流</span></li><li class=\"alt\"> OutputStream output = <span class=\"keyword\">new</span> FileOutputStream(f);</li><li class=\"\"> <span class=\"comment\">//创建缓冲区</span></li><li class=\"alt\"> <span class=\"keyword\">byte</span>[] buff = <span class=\"keyword\">new</span> <span class=\"keyword\">byte</span>[input.available()];</li><li class=\"\"> input.read(buff);</li><li class=\"alt\"> output.write(buff);</li><li class=\"\"> input.close();</li><li class=\"alt\"> output.close();</li><li class=\"\">}</li></ol></div><p>6、使用 Junit Test 运行 testReadBlob 方法</p><p>我们可以看到 D 盘,已经生成了一张新照片,dest.jpg</p><p> </p><p><img src=\"/uploads/2017/10/20171007155038568.png\" alt=\"20171007155038568.png\"><br></p><h2>三、组件属性</h2><p>实体类中某个属性属于用户自定义的类的对象,下面我们通过一个例子来解释</p><p>1、实体类 Student.java 成员变量如下</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">private</span> <span class=\"keyword\">int</span> sid;<span class=\"comment\">//学号</span></li><li class=\"\"><span class=\"keyword\">private</span> String name;<span class=\"comment\">//姓名</span></li><li class=\"alt\"><span class=\"keyword\">private</span> <span class=\"keyword\">int</span> sex;<span class=\"comment\">//性别</span></li><li class=\"\"><span class=\"keyword\">private</span> Date birthday;<span class=\"comment\">//出生日期</span></li><li class=\"alt\"><span class=\"comment\">//private String address;//地址</span></li><li class=\"\"><span class=\"keyword\">private</span> Blob picture;<span class=\"comment\">//照片 </span></li><li class=\"alt\"><span class=\"keyword\">private</span> Address address; <span class=\"comment\">//地址</span></li></ol></div><p>我们这里把之前的 String 类型的 address 改成 Address 类型</p><p>2、Address 类 部分代码如下</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">private</span> String postcode;<span class=\"comment\">//邮编</span></li><li class=\"\"><span class=\"keyword\">private</span> String phone;<span class=\"comment\">//手机</span></li><li class=\"alt\"><span class=\"keyword\">private</span> String address;<span class=\"comment\">//地址</span></li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">public</span> Address(String postcode, String phone, String address) {</li><li class=\"\"> <span class=\"keyword\">this</span>.postcode = postcode;</li><li class=\"alt\"> <span class=\"keyword\">this</span>.phone = phone;</li><li class=\"\"> <span class=\"keyword\">this</span>.address = address;</li><li class=\"alt\">}</li></ol></div><p>3、修改 Student.hbm.xml</p><p>将之前的</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-xml\" start=\"1\"><li class=\"alt\"><span class=\"tag\"><</span><span class=\"tag-name\">property</span> <span class=\"attribute\">name</span>=<span class=\"attribute-value\">\"address\"</span> <span class=\"attribute\">type</span>=<span class=\"attribute-value\">\"java.lang.String\"</span><span class=\"tag\">></span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">column</span> <span class=\"attribute\">name</span>=<span class=\"attribute-value\">\"ADDRESS\"</span> <span class=\"tag\">/></span></li><li class=\"alt\"><span class=\"tag\"><!--</span--><span class=\"tag-name\">property</span><span class=\"tag\">></span></span></li></ol></div><p>改成</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-xml\" start=\"1\"><li class=\"alt\"><span class=\"tag\"><</span><span class=\"tag-name\">component</span> <span class=\"attribute\">name</span>=<span class=\"attribute-value\">\"address\"</span> <span class=\"attribute\">class</span>=<span class=\"attribute-value\">\"Address\"</span><span class=\"tag\">></span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">property</span> <span class=\"attribute\">name</span>=<span class=\"attribute-value\">\"postcode\"</span> <span class=\"attribute\">column</span>=<span class=\"attribute-value\">\"POSTCODE\"</span><span class=\"tag\">/></span></li><li class=\"alt\"> <span class=\"tag\"><</span><span class=\"tag-name\">property</span> <span class=\"attribute\">name</span>=<span class=\"attribute-value\">\"phone\"</span> <span class=\"attribute\">column</span>=<span class=\"attribute-value\">\"PHONE\"</span><span class=\"tag\">/></span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">property</span> <span class=\"attribute\">name</span>=<span class=\"attribute-value\">\"address\"</span> <span class=\"attribute\">column</span>=<span class=\"attribute-value\">\"ADDRESS\"</span><span class=\"tag\">/></span></li><li class=\"alt\"><span class=\"tag\"><!--</span--><span class=\"tag-name\">component</span><span class=\"tag\">></span></span></li></ol></div><p>注意:单词不要打错</p><p>4、修改 测试类 testSaveStudent 代码</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"annotation\">@Test</span></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> testSaveStudnets() {</li><li class=\"alt\"> <span class=\"comment\">//生成学生对象</span></li><li class=\"\"> Student s = <span class=\"keyword\">new</span> Student();</li><li class=\"alt\"> s.setName(<span class=\"string\">\"陶渊明\"</span>);</li><li class=\"\"> s.setSex(<span class=\"number\">1</span>);</li><li class=\"alt\"> s.setBirthday(<span class=\"keyword\">new</span> Date());</li><li class=\"\"> <span class=\"comment\">//s.setAddress(\"江西九江\");</span></li><li class=\"alt\"> Address address = <span class=\"keyword\">new</span> Address(<span class=\"string\">\"332000\"</span>,<span class=\"string\">\"13512345678\"</span>,<span class=\"string\">\"江西九江\"</span>);</li><li class=\"\"> s.setAddress(address);</li><li class=\"alt\"> session.save(s);<span class=\"comment\">//保存对象进入数据库</span></li><li class=\"\"> }</li></ol></div><p>5、修改 hibernate.cfg.xml 里的创建表的策略</p><p>因为要修改表结构,暂且把 update 改成 create</p><p>6、用 Junit Test 运行 testSaveStudent 方法</p><p>运行成功,查看数据表</p><p><img src=\"/uploads/2017/10/2017100715512437.jpg\" alt=\"2017100715512437.jpg\"><br></p><p>查看 表结构</p><p><img src=\"/uploads/2017/10/20171007155135604.jpg\" alt=\"20171007155135604.jpg\"><br></p><p> </p>', '', '', '', '', '', '', '2018-11-25 20:55:13', '2017-10-07 15:51:57', null);
INSERT INTO `article` VALUES ('', '', 'Mybatis高级映射多对多查询', '<p>紧接着上一篇文章:<a href=\"https://liuyanzhao.com/5847.html\" target=\"_blank\" rel=\"noopener noreferrer\">Mybatis高级映射一对多查询</a> 写</p><h2>一、开发准备</h2><p><strong>1、新建数据表(四张表)和添加测试数据</strong></p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-sql\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">DROP</span> <span class=\"keyword\">TABLE</span> IF EXISTS `items`;</li><li class=\"\"><span class=\"keyword\">DROP</span> <span class=\"keyword\">TABLE</span> IF EXISTS `orders`;</li><li class=\"alt\"><span class=\"keyword\">DROP</span> <span class=\"keyword\">TABLE</span> IF EXISTS `<span class=\"func\">user</span>`;</li><li class=\"\"><span class=\"keyword\">DROP</span> <span class=\"keyword\">TABLE</span> IF EXISTS `orderdetail`;</li><li class=\"alt\"></li><li class=\"\">/*items是商品表*/</li><li class=\"alt\"><span class=\"keyword\">CREATE</span> <span class=\"keyword\">TABLE</span> `items` (</li><li class=\"\"> `id` <span class=\"keyword\">INT</span>(11) <span class=\"op\">NOT</span> <span class=\"op\">NULL</span> AUTO_INCREMENT,</li><li class=\"alt\"> `<span class=\"keyword\">name</span>` <span class=\"keyword\">VARCHAR</span>(32) <span class=\"op\">NOT</span> <span class=\"op\">NULL</span> COMMENT \'商品名称\',</li><li class=\"\"> `price` <span class=\"keyword\">FLOAT</span>(10,1) <span class=\"op\">NOT</span> <span class=\"op\">NULL</span> COMMENT \'商品定价\',</li><li class=\"alt\"> `detail` TEXT COMMENT \'商品描述\',</li><li class=\"\"> `pic` <span class=\"keyword\">VARCHAR</span>(64) <span class=\"keyword\">DEFAULT</span> <span class=\"op\">NULL</span> COMMENT \'商品图片\',</li><li class=\"alt\"> `createtime` DATETIME <span class=\"op\">NOT</span> <span class=\"op\">NULL</span> COMMENT \'生产日期\',</li><li class=\"\"> <span class=\"keyword\">PRIMARY</span> <span class=\"keyword\">KEY</span> (`id`)</li><li class=\"alt\">) ENGINE=INNODB AUTO_INCREMENT=4 <span class=\"keyword\">DEFAULT</span> CHARSET=utf8;</li><li class=\"\"></li><li class=\"alt\">/*<span class=\"func\">user</span>是用户表*/</li><li class=\"\"><span class=\"keyword\">CREATE</span> <span class=\"keyword\">TABLE</span> `<span class=\"func\">user</span>` (</li><li class=\"alt\"> `id` <span class=\"keyword\">INT</span>(11) <span class=\"op\">NOT</span> <span class=\"op\">NULL</span> AUTO_INCREMENT,</li><li class=\"\"> `username` <span class=\"keyword\">VARCHAR</span>(32) <span class=\"op\">NOT</span> <span class=\"op\">NULL</span> COMMENT \'用户名称\',</li><li class=\"alt\"> `birthday` <span class=\"keyword\">DATE</span> <span class=\"keyword\">DEFAULT</span> <span class=\"op\">NULL</span> COMMENT \'生日\',</li><li class=\"\"> `gender` <span class=\"keyword\">CHAR</span>(1) <span class=\"keyword\">DEFAULT</span> <span class=\"op\">NULL</span> COMMENT \'性别\',</li><li class=\"alt\"> `address` <span class=\"keyword\">VARCHAR</span>(256) <span class=\"keyword\">DEFAULT</span> <span class=\"op\">NULL</span> COMMENT \'地址\',</li><li class=\"\"> <span class=\"keyword\">PRIMARY</span> <span class=\"keyword\">KEY</span> (`id`)</li><li class=\"alt\">) ENGINE=INNODB AUTO_INCREMENT=27 <span class=\"keyword\">DEFAULT</span> CHARSET=utf8;</li><li class=\"\"></li><li class=\"alt\">/*orders是订单表*/</li><li class=\"\"><span class=\"keyword\">CREATE</span> <span class=\"keyword\">TABLE</span> `orders` (</li><li class=\"alt\"> `id` <span class=\"keyword\">INT</span>(11) <span class=\"op\">NOT</span> <span class=\"op\">NULL</span> AUTO_INCREMENT,</li><li class=\"\"> `user_id` <span class=\"keyword\">INT</span>(11) <span class=\"op\">NOT</span> <span class=\"op\">NULL</span> COMMENT \'下单用户id\',</li><li class=\"alt\"> `number` <span class=\"keyword\">VARCHAR</span>(32) <span class=\"op\">NOT</span> <span class=\"op\">NULL</span> COMMENT \'订单号\',</li><li class=\"\"> `createtime` DATETIME <span class=\"op\">NOT</span> <span class=\"op\">NULL</span> COMMENT \'创建订单时间\',</li><li class=\"alt\"> `note` <span class=\"keyword\">VARCHAR</span>(100) <span class=\"keyword\">DEFAULT</span> <span class=\"op\">NULL</span> COMMENT \'备注\',</li><li class=\"\"> <span class=\"keyword\">PRIMARY</span> <span class=\"keyword\">KEY</span> (`id`),</li><li class=\"alt\"> <span class=\"keyword\">KEY</span> `FK_orders_1` (`user_id`),</li><li class=\"\"> <span class=\"keyword\">CONSTRAINT</span> `FK_orders_id` <span class=\"keyword\">FOREIGN</span> <span class=\"keyword\">KEY</span> (`user_id`) <span class=\"keyword\">REFERENCES</span> `<span class=\"func\">user</span>` (`id`) <span class=\"keyword\">ON</span> <span class=\"keyword\">DELETE</span> <span class=\"keyword\">NO</span> <span class=\"keyword\">ACTION</span> <span class=\"keyword\">ON</span> <span class=\"keyword\">UPDATE</span> <span class=\"keyword\">NO</span> <span class=\"keyword\">ACTION</span></li><li class=\"alt\">) ENGINE=INNODB AUTO_INCREMENT=6 <span class=\"keyword\">DEFAULT</span> CHARSET=utf8;</li><li class=\"\"></li><li class=\"alt\">/*orderdetail是订单明细表*/</li><li class=\"\"><span class=\"keyword\">DROP</span> <span class=\"keyword\">TABLE</span> IF EXISTS orderdetail;</li><li class=\"alt\"><span class=\"keyword\">CREATE</span> <span class=\"keyword\">TABLE</span> `orderdetail` (</li><li class=\"\"> `id` <span class=\"keyword\">INT</span>(11) <span class=\"op\">NOT</span> <span class=\"op\">NULL</span> AUTO_INCREMENT,</li><li class=\"alt\"> `orders_id` <span class=\"keyword\">INT</span>(11) <span class=\"op\">NOT</span> <span class=\"op\">NULL</span> COMMENT \'订单id\',</li><li class=\"\"> `items_id` <span class=\"keyword\">INT</span>(11) <span class=\"op\">NOT</span> <span class=\"op\">NULL</span> COMMENT \'商品id\',</li><li class=\"alt\"> `items_num` <span class=\"keyword\">INT</span>(11) <span class=\"keyword\">DEFAULT</span> <span class=\"op\">NULL</span> COMMENT \'商品购买数量\',</li><li class=\"\"> <span class=\"keyword\">PRIMARY</span> <span class=\"keyword\">KEY</span> (`id`),</li><li class=\"alt\"> <span class=\"keyword\">KEY</span> `FK_orderdetail_1` (`orders_id`),</li><li class=\"\"> <span class=\"keyword\">KEY</span> `FK_orderdetail_2` (`items_id`),</li><li class=\"alt\"> <span class=\"keyword\">CONSTRAINT</span> `FK_orderdetail_1` <span class=\"keyword\">FOREIGN</span> <span class=\"keyword\">KEY</span> (`orders_id`) <span class=\"keyword\">REFERENCES</span> `orders` (`id`) <span class=\"keyword\">ON</span> <span class=\"keyword\">DELETE</span> <span class=\"keyword\">NO</span> <span class=\"keyword\">ACTION</span> <span class=\"keyword\">ON</span> <span class=\"keyword\">UPDATE</span> <span class=\"keyword\">NO</span> <span class=\"keyword\">ACTION</span>,</li><li class=\"\"> <span class=\"keyword\">CONSTRAINT</span> `FK_orderdetail_2` <span class=\"keyword\">FOREIGN</span> <span class=\"keyword\">KEY</span> (`items_id`) <span class=\"keyword\">REFERENCES</span> `items` (`id`) <span class=\"keyword\">ON</span> <span class=\"keyword\">DELETE</span> <span class=\"keyword\">NO</span> <span class=\"keyword\">ACTION</span> <span class=\"keyword\">ON</span> <span class=\"keyword\">UPDATE</span> <span class=\"keyword\">NO</span> <span class=\"keyword\">ACTION</span></li><li class=\"alt\">) ENGINE=INNODB AUTO_INCREMENT=5 <span class=\"keyword\">DEFAULT</span> CHARSET=utf8;</li></ol></div><p>为了测试,我这里随便填了些数据</p><p><img src=\"/uploads/2017/10/20171007155334244.png\" alt=\"20171007155334244.png\"><br></p><p><img src=\"/uploads/2017/10/20171007155349805.png\" alt=\"20171007155349805.png\"><br></p><p><br></p><p><img src=\"/uploads/2017/10/20171007155410485.png\" alt=\"20171007155410485.png\"><br></p><p><br></p><p><strong><img src=\"/uploads/2017/10/20171007155450773.png\" alt=\"20171007155450773.png\"></strong></p><p><strong>2、思路分析</strong></p><p><img src=\"/uploads/2017/10/20171007155545742.png\" alt=\"20171007155545742.png\"><br></p><p>订单项和订单明细是一对多的关系,所以本文主要来查询订单表,然后关联订单明细表,这样就有一对多的问题出来了。</p><p><span>因为多对多比较复杂,总公共有四张表,我们先来分析一下思路:</span></p><blockquote><p><span>1、将用户信息映射到User中;</span></p><p><span>2、在User类中添加订单列表属性<code>List<orders>ordersList</orders></code>,将用户创建的订单映射到ordersList中;</span></p><p><span>3、在Orders中添加订单明细列表属性<code>List<orderdetail>orderDetails</orderdetail></code>,将订单的明细映射到orderDetails中;</span></p><p><span>4、在OrderDetail中添加Items属性,将订单明细所对应的商品映射到Items中。</span></p></blockquote><p>经过这样分析后,感觉虽然有点复杂,但是好像不是很难的样子,映射的方法也跟前面的一样,只不过这里表有点多,关系有点复杂。下面来写映射文件:</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-sql\" start=\"1\"><li class=\"alt\"><<span class=\"keyword\">select</span> id=<span class=\"string\">\"findUserAndItemsResultMap\"</span> resultMap=<span class=\"string\">\"UserAndItemsResultMap\"</span>></li><li class=\"\"> <span class=\"keyword\">SELECT</span></li><li class=\"alt\"> orders.*,</li><li class=\"\"> <span class=\"func\">user</span>.`username`,</li><li class=\"alt\"> <span class=\"func\">user</span>.`sex`,</li><li class=\"\"> <span class=\"func\">user</span>.`address`,</li><li class=\"alt\"> orderdetail.`id` orderdetail_id,</li><li class=\"\"> orderdetail.`items_id`,</li><li class=\"alt\"> orderdetail.`items_num`,</li><li class=\"\"> orderdetail.`orders_id`,</li><li class=\"alt\"> items.`<span class=\"keyword\">name</span>` items_name,</li><li class=\"\"> items.`detail` items_detail,</li><li class=\"alt\"> items.`price` items_price</li><li class=\"\"> <span class=\"keyword\">FROM</span></li><li class=\"alt\"> orders,</li><li class=\"\"> <span class=\"func\">USER</span>,</li><li class=\"alt\"> orderdetail,</li><li class=\"\"> items</li><li class=\"alt\"> <span class=\"keyword\">WHERE</span> orders.`user_id`=<span class=\"func\">user</span>.`id` <span class=\"op\">AND</span> orders.`id` = orderdetail.`orders_id` <span class=\"op\">AND</span> orderdetail.`items_id`=items.`id`</li><li class=\"\"><!--<span class=\"keyword\"-->select></li></ol></div><p>我们先看一下查询结果:</p><p><img src=\"/uploads/2017/10/2017100717420134.png\" alt=\"2017100717420134.png\"><br></p><p><br></p><h2>二、代码实现</h2><p><strong>1、四个持久化类</strong></p><p>① User.java</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">package</span> com.liuyanzhao.mybatis.po;</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">import</span> java.util.Date;</li><li class=\"\"><span class=\"keyword\">import</span> java.util.List;</li><li class=\"alt\"></li><li class=\"\"><span class=\"comment\">/**</span></li><li class=\"alt\"><span class=\"comment\"> * 用户的持久类</span></li><li class=\"\"><span class=\"comment\"> */</span></li><li class=\"alt\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> User {</li><li class=\"\"> <span class=\"keyword\">private</span> <span class=\"keyword\">int</span> id; <span class=\"comment\">//编号</span></li><li class=\"alt\"> <span class=\"keyword\">private</span> String username; <span class=\"comment\">//用户名</span></li><li class=\"\"> <span class=\"keyword\">private</span> String gender; <span class=\"comment\">//性别</span></li><li class=\"alt\"> <span class=\"keyword\">private</span> Date birthday; <span class=\"comment\">//生日</span></li><li class=\"\"> <span class=\"keyword\">private</span> String address; <span class=\"comment\">//地址</span></li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> List<orders> getOrdersList() {</orders></li><li class=\"alt\"> <span class=\"keyword\">return</span> ordersList;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setOrdersList(List<orders> ordersList) {</orders></li><li class=\"alt\"> <span class=\"keyword\">this</span>.ordersList = ordersList;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"comment\">//用户创建的订单列表</span></li><li class=\"alt\"> <span class=\"keyword\">private</span> List<orders> ordersList;</orders></li><li class=\"\"></li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">int</span> getId() {</li><li class=\"alt\"> <span class=\"keyword\">return</span> id;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setId(<span class=\"keyword\">int</span> id) {</li><li class=\"alt\"> <span class=\"keyword\">this</span>.id = id;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> String getUsername() {</li><li class=\"alt\"> <span class=\"keyword\">return</span> username;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setUsername(String username) {</li><li class=\"alt\"> <span class=\"keyword\">this</span>.username = username;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> String getGender() {</li><li class=\"alt\"> <span class=\"keyword\">return</span> gender;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setGender(String gender) {</li><li class=\"alt\"> <span class=\"keyword\">this</span>.gender = gender;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> Date getBirthday() {</li><li class=\"alt\"> <span class=\"keyword\">return</span> birthday;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setBirthday(Date birthday) {</li><li class=\"alt\"> <span class=\"keyword\">this</span>.birthday = birthday;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> String getAddress() {</li><li class=\"alt\"> <span class=\"keyword\">return</span> address;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setAddress(String address) {</li><li class=\"alt\"> <span class=\"keyword\">this</span>.address = address;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\">}</li></ol></div><p>注意:需要在用户表中加入 订单列表</p><p> </p><p>② Items.java</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">package</span> com.liuyanzhao.mybatis.po;</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">import</span> java.util.Date;</li><li class=\"\"></li><li class=\"alt\"><span class=\"comment\">/**</span></li><li class=\"\"><span class=\"comment\"> * 商品的持久类</span></li><li class=\"alt\"><span class=\"comment\"> */</span></li><li class=\"\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> Items {</li><li class=\"alt\"> <span class=\"keyword\">private</span> <span class=\"keyword\">int</span> id;</li><li class=\"\"> <span class=\"keyword\">private</span> String name;</li><li class=\"alt\"> <span class=\"keyword\">private</span> <span class=\"keyword\">double</span> price;</li><li class=\"\"> <span class=\"keyword\">private</span> String detail;</li><li class=\"alt\"> <span class=\"keyword\">private</span> String pic;</li><li class=\"\"> <span class=\"keyword\">private</span> Date createTime;</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">int</span> getId() {</li><li class=\"alt\"> <span class=\"keyword\">return</span> id;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setId(<span class=\"keyword\">int</span> id) {</li><li class=\"alt\"> <span class=\"keyword\">this</span>.id = id;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> String getName() {</li><li class=\"alt\"> <span class=\"keyword\">return</span> name;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setName(String name) {</li><li class=\"alt\"> <span class=\"keyword\">this</span>.name = name;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">double</span> getPrice() {</li><li class=\"alt\"> <span class=\"keyword\">return</span> price;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setPrice(<span class=\"keyword\">double</span> price) {</li><li class=\"alt\"> <span class=\"keyword\">this</span>.price = price;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> String getDetail() {</li><li class=\"alt\"> <span class=\"keyword\">return</span> detail;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setDetail(String detail) {</li><li class=\"alt\"> <span class=\"keyword\">this</span>.detail = detail;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> String getPic() {</li><li class=\"alt\"> <span class=\"keyword\">return</span> pic;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setPic(String pic) {</li><li class=\"alt\"> <span class=\"keyword\">this</span>.pic = pic;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> Date getCreateTime() {</li><li class=\"alt\"> <span class=\"keyword\">return</span> createTime;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setCreateTime(Date createTime) {</li><li class=\"alt\"> <span class=\"keyword\">this</span>.createTime = createTime;</li><li class=\"\"> }</li><li class=\"alt\">}</li></ol></div><p> </p><p>③ Orders.java</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">package</span> com.liuyanzhao.mybatis.po;</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">import</span> java.util.Date;</li><li class=\"\"><span class=\"keyword\">import</span> java.util.List;</li><li class=\"alt\"></li><li class=\"\"><span class=\"comment\">/**</span></li><li class=\"alt\"><span class=\"comment\"> * 订单的持久类和扩展类</span></li><li class=\"\"><span class=\"comment\"> */</span></li><li class=\"alt\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> Orders {</li><li class=\"\"> <span class=\"keyword\">private</span> <span class=\"keyword\">int</span> id;</li><li class=\"alt\"> <span class=\"keyword\">private</span> <span class=\"keyword\">int</span> userId;</li><li class=\"\"> <span class=\"keyword\">private</span> String number;</li><li class=\"alt\"> <span class=\"keyword\">private</span> Date createTime;</li><li class=\"\"> <span class=\"keyword\">private</span> String note;</li><li class=\"\"></li><li class=\"alt\"> <span class=\"comment\">//订单明细</span></li><li class=\"\"> <span class=\"keyword\">private</span> List<orderdetail> orderdetails;</orderdetail></li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> List<orderdetail> getOrderdetails() {</orderdetail></li><li class=\"alt\"> <span class=\"keyword\">return</span> orderdetails;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setOrderdetails(List<orderdetail> orderdetails) {</orderdetail></li><li class=\"alt\"> <span class=\"keyword\">this</span>.orderdetails = orderdetails;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">int</span> getId() {</li><li class=\"alt\"> <span class=\"keyword\">return</span> id;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setId(<span class=\"keyword\">int</span> id) {</li><li class=\"alt\"> <span class=\"keyword\">this</span>.id = id;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">int</span> getUserId() {</li><li class=\"alt\"> <span class=\"keyword\">return</span> userId;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setUserId(<span class=\"keyword\">int</span> userId) {</li><li class=\"alt\"> <span class=\"keyword\">this</span>.userId = userId;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> String getNumber() {</li><li class=\"alt\"> <span class=\"keyword\">return</span> number;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setNumber(String number) {</li><li class=\"alt\"> <span class=\"keyword\">this</span>.number = number;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> Date getCreateTime() {</li><li class=\"alt\"> <span class=\"keyword\">return</span> createTime;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setCreateTime(Date createTime) {</li><li class=\"alt\"> <span class=\"keyword\">this</span>.createTime = createTime;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> String getNote() {</li><li class=\"alt\"> <span class=\"keyword\">return</span> note;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setNote(String note) {</li><li class=\"alt\"> <span class=\"keyword\">this</span>.note = note;</li><li class=\"\"> }</li><li class=\"alt\">}</li></ol></div><p>注意:订单列表中,需要订单的详细信息,不需要用户信息</p><p> </p><p>④ Orderdetail.java</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">package</span> com.liuyanzhao.mybatis.po;</li><li class=\"\"></li><li class=\"alt\"><span class=\"comment\">/**</span></li><li class=\"\"><span class=\"comment\"> * 订单明细的持久类</span></li><li class=\"alt\"><span class=\"comment\"> */</span></li><li class=\"\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> Orderdetail {</li><li class=\"alt\"> <span class=\"keyword\">private</span> <span class=\"keyword\">int</span> id;</li><li class=\"\"> <span class=\"keyword\">private</span> <span class=\"keyword\">int</span> ordersId;</li><li class=\"alt\"> <span class=\"keyword\">private</span> <span class=\"keyword\">int</span> itemsId;</li><li class=\"\"> <span class=\"keyword\">private</span> <span class=\"keyword\">int</span> itemsNum;</li><li class=\"alt\"></li><li class=\"\"> <span class=\"comment\">//明细对应的商品信息</span></li><li class=\"alt\"> <span class=\"keyword\">private</span> Items items;</li><li class=\"\"></li><li class=\"alt\"> <span class=\"keyword\">public</span> Items getItems() {</li><li class=\"\"> <span class=\"keyword\">return</span> items;</li><li class=\"alt\"> }</li><li class=\"\"></li><li class=\"alt\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setItems(Items items) {</li><li class=\"\"> <span class=\"keyword\">this</span>.items = items;</li><li class=\"alt\"> }</li><li class=\"\"></li><li class=\"alt\"> <span class=\"keyword\">public</span> <span class=\"keyword\">int</span> getId() {</li><li class=\"\"> <span class=\"keyword\">return</span> id;</li><li class=\"alt\"> }</li><li class=\"\"></li><li class=\"alt\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setId(<span class=\"keyword\">int</span> id) {</li><li class=\"\"> <span class=\"keyword\">this</span>.id = id;</li><li class=\"alt\"> }</li><li class=\"\"></li><li class=\"alt\"> <span class=\"keyword\">public</span> <span class=\"keyword\">int</span> getOrdersId() {</li><li class=\"\"> <span class=\"keyword\">return</span> ordersId;</li><li class=\"alt\"> }</li><li class=\"\"></li><li class=\"alt\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setOrdersId(<span class=\"keyword\">int</span> ordersId) {</li><li class=\"\"> <span class=\"keyword\">this</span>.ordersId = ordersId;</li><li class=\"alt\"> }</li><li class=\"\"></li><li class=\"alt\"> <span class=\"keyword\">public</span> <span class=\"keyword\">int</span> getItemsId() {</li><li class=\"\"> <span class=\"keyword\">return</span> itemsId;</li><li class=\"alt\"> }</li><li class=\"\"></li><li class=\"alt\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setItemsId(<span class=\"keyword\">int</span> itemsId) {</li><li class=\"\"> <span class=\"keyword\">this</span>.itemsId = itemsId;</li><li class=\"alt\"> }</li><li class=\"\"></li><li class=\"alt\"> <span class=\"keyword\">public</span> <span class=\"keyword\">int</span> getItemsNum() {</li><li class=\"\"> <span class=\"keyword\">return</span> itemsNum;</li><li class=\"alt\"> }</li><li class=\"\"></li><li class=\"alt\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setItemsNum(<span class=\"keyword\">int</span> itemsNum) {</li><li class=\"\"> <span class=\"keyword\">this</span>.itemsNum = itemsNum;</li><li class=\"alt\"> }</li><li class=\"\">}</li></ol></div><p>注意:订单明细里,需要 商品信息</p><p> </p><p><strong>2、订单代理 即mapper.java</strong></p><p>OrdersMapperCustom.java</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">package</span> com.liuyanzhao.mybatis.mapper;</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">import</span> com.liuyanzhao.mybatis.po.User;</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">import</span> java.util.List;</li><li class=\"\"></li><li class=\"alt\"><span class=\"comment\">/**</span></li><li class=\"\"><span class=\"comment\"> * 订单 mapper</span></li><li class=\"alt\"><span class=\"comment\"> */</span></li><li class=\"\"><span class=\"keyword\">public</span> <span class=\"keyword\">interface</span> OrdersMapperCustom {</li><li class=\"alt\"></li><li class=\"\"> <span class=\"comment\">//查询用户购买的商品信息</span></li><li class=\"alt\"> <span class=\"keyword\">public</span> List<user> findUserAndItemsResultMap() <span class=\"keyword\">throws</span> Exception;</user></li><li class=\"\">}</li></ol></div><p> </p><p><span><strong>3、OrdersMapperCustom.xml 映射文件</strong></span></p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-xml\" start=\"1\"><li class=\"alt\"><span class=\"tag\"><!--?</span--><span class=\"tag-name\">xml</span> <span class=\"attribute\">version</span>=<span class=\"attribute-value\">\"1.0\"</span> <span class=\"attribute\">encoding</span>=<span class=\"attribute-value\">\"UTF-8\"</span> <span class=\"tag\">?></span></span></li><li class=\"\"></li><li class=\"alt\"> PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"</li><li class=\"\"> \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\"<span class=\"tag\">></span></li><li class=\"alt\"></li><li class=\"\"><span class=\"tag\"><</span><span class=\"tag-name\">mapper</span> <span class=\"attribute\">namespace</span>=<span class=\"attribute-value\">\"com.liuyanzhao.mybatis.mapper.OrdersMapperCustom\"</span><span class=\"tag\">></span></li><li class=\"alt\"></li><li class=\"\"> <span class=\"comments\"><!--查询用户购买的商品--></span></li><li class=\"alt\"> <span class=\"tag\"><</span><span class=\"tag-name\">resultMap</span> <span class=\"attribute\">id</span>=<span class=\"attribute-value\">\"UserAndItemsResultMap\"</span> <span class=\"attribute\">type</span>=<span class=\"attribute-value\">\"com.liuyanzhao.mybatis.po.User\"</span><span class=\"tag\">></span></li><li class=\"\"> <span class=\"comments\"><!--用户信息--></span></li><li class=\"alt\"> <span class=\"tag\"><</span><span class=\"tag-name\">id</span> <span class=\"attribute\">column</span>=<span class=\"attribute-value\">\"user_id\"</span> <span class=\"attribute\">property</span>=<span class=\"attribute-value\">\"id\"</span><span class=\"tag\">></span><span class=\"tag\"><!--</span--><span class=\"tag-name\">id</span><span class=\"tag\">></span></span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">result</span> <span class=\"attribute\">column</span>=<span class=\"attribute-value\">\"username\"</span> <span class=\"attribute\">property</span>=<span class=\"attribute-value\">\"username\"</span><span class=\"tag\">></span><span class=\"tag\"><!--</span--><span class=\"tag-name\">result</span><span class=\"tag\">></span></span></li><li class=\"alt\"> <span class=\"tag\"><</span><span class=\"tag-name\">result</span> <span class=\"attribute\">column</span>=<span class=\"attribute-value\">\"gender\"</span> <span class=\"attribute\">property</span>=<span class=\"attribute-value\">\"gender\"</span><span class=\"tag\">></span><span class=\"tag\"><!--</span--><span class=\"tag-name\">result</span><span class=\"tag\">></span></span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">result</span> <span class=\"attribute\">column</span>=<span class=\"attribute-value\">\"address\"</span> <span class=\"attribute\">property</span>=<span class=\"attribute-value\">\"address\"</span><span class=\"tag\">></span><span class=\"tag\"><!--</span--><span class=\"tag-name\">result</span><span class=\"tag\">></span></span></li><li class=\"alt\"></li><li class=\"\"></li><li class=\"alt\"> <span class=\"comments\"><!--订单信息--></span></li><li class=\"\"> <span class=\"comments\"><!--一个用户可以对应多个订单,故使用collection映射--></span></li><li class=\"alt\"> <span class=\"tag\"><</span><span class=\"tag-name\">collection</span> <span class=\"attribute\">property</span>=<span class=\"attribute-value\">\"ordersList\"</span> <span class=\"attribute\">ofType</span>=<span class=\"attribute-value\">\"com.liuyanzhao.mybatis.po.Orders\"</span><span class=\"tag\">></span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">id</span> <span class=\"attribute\">column</span>=<span class=\"attribute-value\">\"id\"</span> <span class=\"attribute\">property</span>=<span class=\"attribute-value\">\"id\"</span><span class=\"tag\">></span><span class=\"tag\"><!--</span--><span class=\"tag-name\">id</span><span class=\"tag\">></span></span></li><li class=\"alt\"> <span class=\"tag\"><</span><span class=\"tag-name\">result</span> <span class=\"attribute\">column</span>=<span class=\"attribute-value\">\"user_id\"</span> <span class=\"attribute\">property</span>=<span class=\"attribute-value\">\"userId\"</span><span class=\"tag\">></span><span class=\"tag\"><!--</span--><span class=\"tag-name\">result</span><span class=\"tag\">></span></span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">result</span> <span class=\"attribute\">column</span>=<span class=\"attribute-value\">\"number\"</span> <span class=\"attribute\">property</span>=<span class=\"attribute-value\">\"number\"</span><span class=\"tag\">></span><span class=\"tag\"><!--</span--><span class=\"tag-name\">result</span><span class=\"tag\">></span></span></li><li class=\"alt\"> <span class=\"tag\"><</span><span class=\"tag-name\">result</span> <span class=\"attribute\">column</span>=<span class=\"attribute-value\">\"createtime\"</span> <span class=\"attribute\">property</span>=<span class=\"attribute-value\">\"createTime\"</span><span class=\"tag\">></span><span class=\"tag\"><!--</span--><span class=\"tag-name\">result</span><span class=\"tag\">></span></span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">result</span> <span class=\"attribute\">column</span>=<span class=\"attribute-value\">\"node\"</span> <span class=\"attribute\">property</span>=<span class=\"attribute-value\">\"node\"</span><span class=\"tag\">></span><span class=\"tag\"><!--</span--><span class=\"tag-name\">result</span><span class=\"tag\">></span></span></li><li class=\"alt\"></li><li class=\"\"> <span class=\"comments\"><!--订单明细--></span></li><li class=\"alt\"> <span class=\"comments\"><!--一个订单包括多个明细,故使用collection--></span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">collection</span> <span class=\"attribute\">property</span>=<span class=\"attribute-value\">\"orderdetails\"</span> <span class=\"attribute\">ofType</span>=<span class=\"attribute-value\">\"com.liuyanzhao.mybatis.po.Orderdetail\"</span><span class=\"tag\">></span></li><li class=\"alt\"> <span class=\"tag\"><</span><span class=\"tag-name\">id</span> <span class=\"attribute\">column</span>=<span class=\"attribute-value\">\"orderdetail_id\"</span> <span class=\"attribute\">property</span>=<span class=\"attribute-value\">\"id\"</span><span class=\"tag\">></span><span class=\"tag\"><!--</span--><span class=\"tag-name\">id</span><span class=\"tag\">></span></span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">result</span> <span class=\"attribute\">column</span>=<span class=\"attribute-value\">\"items_id\"</span> <span class=\"attribute\">property</span>=<span class=\"attribute-value\">\"itemsId\"</span><span class=\"tag\">></span><span class=\"tag\"><!--</span--><span class=\"tag-name\">result</span><span class=\"tag\">></span></span></li><li class=\"alt\"> <span class=\"tag\"><</span><span class=\"tag-name\">result</span> <span class=\"attribute\">column</span>=<span class=\"attribute-value\">\"items_num\"</span> <span class=\"attribute\">property</span>=<span class=\"attribute-value\">\"itemsNum\"</span><span class=\"tag\">></span><span class=\"tag\"><!--</span--><span class=\"tag-name\">result</span><span class=\"tag\">></span></span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">result</span> <span class=\"attribute\">column</span>=<span class=\"attribute-value\">\"orders_id\"</span> <span class=\"attribute\">property</span>=<span class=\"attribute-value\">\"ordersId\"</span><span class=\"tag\">></span><span class=\"tag\"><!--</span--><span class=\"tag-name\">result</span><span class=\"tag\">></span></span></li><li class=\"alt\"></li><li class=\"\"> <span class=\"comments\"><!--商品信息--></span></li><li class=\"alt\"> <span class=\"comments\"><!--一个订单明细对应一个商品--></span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">association</span> <span class=\"attribute\">property</span>=<span class=\"attribute-value\">\"items\"</span> <span class=\"attribute\">javaType</span>=<span class=\"attribute-value\">\"com.liuyanzhao.mybatis.po.Items\"</span><span class=\"tag\">></span></li><li class=\"alt\"> <span class=\"tag\"><</span><span class=\"tag-name\">id</span> <span class=\"attribute\">column</span>=<span class=\"attribute-value\">\"items_id\"</span> <span class=\"attribute\">property</span>=<span class=\"attribute-value\">\"id\"</span><span class=\"tag\">></span><span class=\"tag\"><!--</span--><span class=\"tag-name\">id</span><span class=\"tag\">></span></span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">result</span> <span class=\"attribute\">column</span>=<span class=\"attribute-value\">\"items_name\"</span> <span class=\"attribute\">property</span>=<span class=\"attribute-value\">\"name\"</span><span class=\"tag\">></span><span class=\"tag\"><!--</span--><span class=\"tag-name\">result</span><span class=\"tag\">></span></span></li><li class=\"alt\"> <span class=\"tag\"><</span><span class=\"tag-name\">result</span> <span class=\"attribute\">column</span>=<span class=\"attribute-value\">\"items_price\"</span> <span class=\"attribute\">property</span>=<span class=\"attribute-value\">\"price\"</span><span class=\"tag\">></span><span class=\"tag\"><!--</span--><span class=\"tag-name\">result</span><span class=\"tag\">></span></span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">result</span> <span class=\"attribute\">column</span>=<span class=\"attribute-value\">\"items_detail\"</span> <span class=\"attribute\">property</span>=<span class=\"attribute-value\">\"detail\"</span><span class=\"tag\">></span><span class=\"tag\"><!--</span--><span class=\"tag-name\">result</span><span class=\"tag\">></span></span></li><li class=\"alt\"> <span class=\"tag\"><!--</span--><span class=\"tag-name\">association</span><span class=\"tag\">></span></span></li><li class=\"\"> <span class=\"tag\"><!--</span--><span class=\"tag-name\">collection</span><span class=\"tag\">></span></span></li><li class=\"alt\"> <span class=\"tag\"><!--</span--><span class=\"tag-name\">collection</span><span class=\"tag\">></span></span></li><li class=\"\"> <span class=\"tag\"><!--</span--><span class=\"tag-name\">resultMap</span><span class=\"tag\">></span></span></li><li class=\"alt\"></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">select</span> <span class=\"attribute\">id</span>=<span class=\"attribute-value\">\"findUserAndItemsResultMap\"</span> <span class=\"attribute\">resultMap</span>=<span class=\"attribute-value\">\"UserAndItemsResultMap\"</span><span class=\"tag\">></span></li><li class=\"alt\"> SELECT</li><li class=\"\"> orders.*,</li><li class=\"alt\"> user.username,</li><li class=\"\"> user.gender,</li><li class=\"alt\"> user.address,</li><li class=\"\"> orderdetail.id orderdetail_id,</li><li class=\"alt\"> orderdetail.items_id,</li><li class=\"\"> orderdetail.items_num,</li><li class=\"alt\"> orderdetail.orders_id,</li><li class=\"\"> items.name items_name,</li><li class=\"alt\"> items.detail items_detail,</li><li class=\"\"> items.price items_price</li><li class=\"alt\"> FROM</li><li class=\"\"> orders,</li><li class=\"alt\"> user,</li><li class=\"\"> orderdetail,</li><li class=\"alt\"> items</li><li class=\"\"> WHERE <span class=\"attribute\">orders.user_id</span>=user.id AND <span class=\"attribute\">orders.id</span> = <span class=\"attribute-value\">orderdetail</span>.orders_id AND <span class=\"attribute\">orderdetail.items_id</span>=items.id</li><li class=\"alt\"> <span class=\"tag\"><!--</span--><span class=\"tag-name\">select</span><span class=\"tag\">></span></span></li><li class=\"\"></li><li class=\"alt\"><span class=\"tag\"><!--</span--><span class=\"tag-name\">mapper</span><span class=\"tag\">></span></span></li></ol></div><p> </p><p><span>4、测试类 OrderMapperCustomTest.java</span></p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">package</span> com.liuyanzhao.mybatis.test;</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">import</span> com.liuyanzhao.mybatis.mapper.OrdersMapperCustom;</li><li class=\"\"><span class=\"keyword\">import</span> com.liuyanzhao.mybatis.po.User;</li><li class=\"alt\"><span class=\"keyword\">import</span> org.apache.ibatis.io.Resources;</li><li class=\"\"><span class=\"keyword\">import</span> org.apache.ibatis.session.SqlSession;</li><li class=\"alt\"><span class=\"keyword\">import</span> org.apache.ibatis.session.SqlSessionFactory;</li><li class=\"\"><span class=\"keyword\">import</span> org.apache.ibatis.session.SqlSessionFactoryBuilder;</li><li class=\"alt\"><span class=\"keyword\">import</span> org.junit.Before;</li><li class=\"\"><span class=\"keyword\">import</span> org.junit.Test;</li><li class=\"alt\"></li><li class=\"\"><span class=\"keyword\">import</span> java.io.InputStream;</li><li class=\"alt\"><span class=\"keyword\">import</span> java.util.List;</li><li class=\"\"></li><li class=\"alt\"><span class=\"comment\">/**</span></li><li class=\"\"><span class=\"comment\"> * Created by Liu_Yanzhao on 2017/8/12.</span></li><li class=\"alt\"><span class=\"comment\"> */</span></li><li class=\"\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> OrderMapperCustomTest {</li><li class=\"alt\"> SqlSessionFactory sqlSessionFactory;</li><li class=\"\"></li><li class=\"alt\"> <span class=\"annotation\">@Before</span></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setUp() <span class=\"keyword\">throws</span> Exception {</li><li class=\"alt\"> String resource = <span class=\"string\">\"Configuration.xml\"</span>;</li><li class=\"\"> InputStream inputStream = Resources.getResourceAsStream(resource);</li><li class=\"alt\"> sqlSessionFactory = <span class=\"keyword\">new</span> SqlSessionFactoryBuilder()</li><li class=\"\"> .build(inputStream);</li><li class=\"alt\"> }</li><li class=\"\"></li><li class=\"alt\"> <span class=\"annotation\">@Test</span></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> testFindUserAndItemsResultMap() <span class=\"keyword\">throws</span> Exception {</li><li class=\"alt\"> SqlSession sqlSession = sqlSessionFactory.openSession();</li><li class=\"\"> <span class=\"comment\">//创建代理对象</span></li><li class=\"alt\"> OrdersMapperCustom ordersMapperCustom = sqlSession.getMapper(OrdersMapperCustom.<span class=\"keyword\">class</span>);</li><li class=\"\"> <span class=\"comment\">//调用mapper对象</span></li><li class=\"alt\"> List<user> list = ordersMapperCustom.findUserAndItemsResultMap();</user></li><li class=\"\"> System.out.println(list);</li><li class=\"alt\"> <span class=\"comment\">//释放资源</span></li><li class=\"\"> sqlSession.close();</li><li class=\"alt\"></li><li class=\"\"> }</li><li class=\"alt\">}</li></ol></div><p>还有其他文件就不补充了,如 mybatis 全局配置文件</p><p> </p><h2>小结</h2><p><span>这样多对多的映射就搞定了。不过还有个问题,就是这里多对多的查询会把所有关联的表的信息都查询出来,然后放到pojo中的对应的List或者某个类中,所以即使我只查了个用户信息,但是这个用户里包含了订单,订单项,商品等信息,感觉装的有点多,好像有时候并不需要这么多冗余的数据出来,但是如果用resultType的话查询出来的字段必须对应pojo中的属性,如果有List等,需要手动装入才行。所以下面总结一下对于这种查询数据比较多的时候,resultType和resultMap各有什么作用?</span></p><blockquote><ol><li>比如我们只需要将查询用户购买的商品信息明细清单(如用户名、用户地址、购买商品名称、购买商品时间、购买商品数量),那么我们完全不需要其他的信息,这个时候就没必要使用resultMap将所有的信息都搞出来,我们可以自己定义一个pojo,包含我们需要的字段即可,然后查询语句只查询我们需要的字段,这样使用resultType会方便很多。</li><li>如果我们需要查询该用户的所有详细信息,比如用户点击该用户或者鼠标放上去,会出来跟该用户相关的订单啊,订单明细啊,商品啊之类的,然后我们要点进去看下详细情况的时候,那就需要使用resultMap了,必须将所有信息都装到这个User中,然后具体啥信息再从User中取,很好理解。</li><li>总结一点:使用resultMap是针对那些对查询结果映射有特殊要求的功能,,比如特殊要求映射成list中包括多个list。否则使用resultType比较直接。</li></ol></blockquote><p><span>到这里,mybatis的多对多映射就总结完了。 </span></p>', '', '', '', '', '', '', '2018-11-25 20:53:43', '2017-10-07 15:56:00', null);
INSERT INTO `article` VALUES ('', '', 'Servlet 实现验证码', '<h2>分析</h2><p><strong>生成图片实现类</strong></p><p>Image<a href=\"https://liuyanzhao.com/tag/servlet/\" title=\"查看与 Servlet 相关的文章\" target=\"_blank\">Servlet</a> 类</p><p>① 定义BufferedImage 对象</p><p>② 获得 Graphics 对象</p><p>③ 通过 Random 产生随机验证码信息</p><p>④ 使用 Graphics 绘制图片</p><p>⑤ 记录验证码信息到 session 中</p><p>⑥ 使用 ImageIO 输出图片</p><h2>代码实现</h2><p>1、新建 web 项目 CheckCode,在 WebContent 下新建 index.jsp</p><p><strong>index.jsp</strong></p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-xml\" start=\"1\"><li class=\"alt\"><span class=\"tag\"><</span>%@ page <span class=\"attribute\">language</span>=<span class=\"attribute-value\">\"java\"</span> <span class=\"attribute\">contentType</span>=<span class=\"attribute-value\">\"text/html; charset=UTF-8\"</span></li><li class=\"\"> <span class=\"attribute\">pageEncoding</span>=<span class=\"attribute-value\">\"UTF-8\"</span>%<span class=\"tag\">></span></li><li class=\"alt\">></li><li class=\"\"><span class=\"tag\"><</span><span class=\"tag-name\">html</span><span class=\"tag\">></span></li><li class=\"alt\"><span class=\"tag\"><</span><span class=\"tag-name\">head</span><span class=\"tag\">></span></li><li class=\"\"><span class=\"tag\"><</span><span class=\"tag-name\">meta</span> <span class=\"attribute\">http-equiv</span>=<span class=\"attribute-value\">\"Content-Type\"</span> <span class=\"attribute\">content</span>=<span class=\"attribute-value\">\"text/html; charset=UTF-8\"</span><span class=\"tag\">></span></li><li class=\"alt\"><span class=\"tag\"><</span><span class=\"tag-name\">title</span><span class=\"tag\">></span>Insert title here<span class=\"tag\"><!--</span--><span class=\"tag-name\">title</span><span class=\"tag\">></span></span></li><li class=\"\"><span class=\"tag\"><</span><span class=\"tag-name\">script</span> <span class=\"attribute\">type</span>=<span class=\"attribute-value\">\"text/javascript\"</span><span class=\"tag\">></span></li><li class=\"alt\"> function reloadCode() {</li><li class=\"\"> //传个时间参数,防止缓存</li><li class=\"alt\"> var <span class=\"attribute\">time</span> = <span class=\"attribute-value\">new</span> Date().getTime();</li><li class=\"\"> document.getElementById(\"imageCode\")<span class=\"attribute\">.src</span>=<span class=\"attribute-value\">\"<%=request.getContextPath()%>/Image<a href=\"https://liuyanzhao.com/tag/servlet/\" title=\"查看与 Servlet 相关的文章\" target=\"_blank\">Servlet</a>?d=\"</span>+time;</li><li class=\"alt\"> }</li><li class=\"\"><span class=\"tag\"><!--</span--><span class=\"tag-name\">script</span><span class=\"tag\">></span></span></li><li class=\"alt\"><span class=\"tag\"><!--</span--><span class=\"tag-name\">head</span><span class=\"tag\">></span></span></li><li class=\"\"><span class=\"tag\"><</span><span class=\"tag-name\">body</span><span class=\"tag\">></span></li><li class=\"alt\"><span class=\"tag\"><</span><span class=\"tag-name\">form</span> <span class=\"attribute\">action</span>=<span class=\"attribute-value\">\"<%=request.getContextPath()%>/Login<a href=\"https://liuyanzhao.com/tag/servlet/\" title=\"查看与 Servlet 相关的文章\" target=\"_blank\">Servlet</a>\"</span> <span class=\"attribute\">method</span>=<span class=\"attribute-value\">\"post\"</span><span class=\"tag\">></span></li><li class=\"\"> 验证码:<span class=\"tag\"><</span><span class=\"tag-name\">input</span> <span class=\"attribute\">type</span>=<span class=\"attribute-value\">\"text\"</span> <span class=\"attribute\">name</span>=<span class=\"attribute-value\">\"checkcode\"</span> <span class=\"tag\">/></span></li><li class=\"alt\"> <span class=\"tag\"><</span><span class=\"tag-name\">img</span> <span class=\"attribute\">id</span>=<span class=\"attribute-value\">\"imageCode\"</span> <span class=\"attribute\">src</span>=<span class=\"attribute-value\">\"<%=request.getContextPath()%>/Image<a href=\"https://liuyanzhao.com/tag/servlet/\" title=\"查看与 Servlet 相关的文章\" target=\"_blank\">Servlet</a>\"</span> <span class=\"attribute\">alt</span>=<span class=\"attribute-value\">\"验证码\"</span> <span class=\"tag\">/></span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">a</span> <span class=\"attribute\">href</span>=<span class=\"attribute-value\">\"javascript:reloadCode();\"</span><span class=\"tag\">></span>看不清楚<span class=\"tag\"><!--</span--><span class=\"tag-name\">a</span><span class=\"tag\">></span> <span class=\"tag\"><</span><span class=\"tag-name\">br</span> <span class=\"tag\">/></span></span></li><li class=\"alt\"> <span class=\"tag\"><</span><span class=\"tag-name\">input</span> <span class=\"attribute\">type</span>=<span class=\"attribute-value\">\"submit\"</span> <span class=\"attribute\">value</span>=<span class=\"attribute-value\">\"提交\"</span><span class=\"tag\">/></span></li><li class=\"\"> <span class=\"tag\"><!--</span--><span class=\"tag-name\">form</span><span class=\"tag\">></span></span></li><li class=\"alt\"><span class=\"tag\"><!--</span--><span class=\"tag-name\">body</span><span class=\"tag\">></span></span></li><li class=\"\"><span class=\"tag\"><!--</span--><span class=\"tag-name\">html</span><span class=\"tag\">></span></span></li></ol></div><p>2、在 src 下新建 com.liuyanzhao 包,然后分别新建用于动态画图的 Image<a href=\"https://liuyanzhao.com/tag/servlet/\" title=\"查看与 Servlet 相关的文章\" target=\"_blank\">Servlet</a> 类和用于判断验证码是否正确的 Login<a href=\"https://liuyanzhao.com/tag/servlet/\" title=\"查看与 Servlet 相关的文章\" target=\"_blank\">Servlet</a> 类</p><p><strong>Image<a href=\"https://liuyanzhao.com/tag/servlet/\" title=\"查看与 Servlet 相关的文章\" target=\"_blank\">Servlet</a>.java</strong></p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">package</span> com.liuyanzhao;</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">import</span> java.awt.Color;</li><li class=\"\"><span class=\"keyword\">import</span> java.awt.Graphics;</li><li class=\"alt\"><span class=\"keyword\">import</span> java.awt.image.BufferedImage;</li><li class=\"\"><span class=\"keyword\">import</span> java.io.IOException;</li><li class=\"alt\"><span class=\"keyword\">import</span> java.util.Random;</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">import</span> javax.imageio.ImageIO;</li><li class=\"\"><span class=\"keyword\">import</span> javax.servlet.<a href=\"https://liuyanzhao.com/tag/servlet/\" title=\"查看与 Servlet 相关的文章\" target=\"_blank\">Servlet</a>Exception;</li><li class=\"alt\"><span class=\"keyword\">import</span> javax.servlet.http.Http<a href=\"https://liuyanzhao.com/tag/servlet/\" title=\"查看与 Servlet 相关的文章\" target=\"_blank\">Servlet</a>;</li><li class=\"\"><span class=\"keyword\">import</span> javax.servlet.http.HttpServletRequest;</li><li class=\"alt\"><span class=\"keyword\">import</span> javax.servlet.http.HttpServletResponse;</li><li class=\"\"></li><li class=\"alt\"></li><li class=\"\"><span class=\"comment\">/*</span></li><li class=\"alt\"><span class=\"comment\"> * @author LiuYanzhao</span></li><li class=\"\"><span class=\"comment\"> */</span></li><li class=\"alt\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> ImageServlet <span class=\"keyword\">extends</span> HttpServlet {</li><li class=\"\"> <span class=\"annotation\">@Override</span></li><li class=\"alt\"> <span class=\"keyword\">protected</span> <span class=\"keyword\">void</span> doGet(HttpServletRequest request, HttpServletResponse response) <span class=\"keyword\">throws</span> ServletException, IOException {</li><li class=\"\"> BufferedImage bi = <span class=\"keyword\">new</span> BufferedImage(<span class=\"number\">68</span>, <span class=\"number\">22</span>, BufferedImage.TYPE_INT_RGB);</li><li class=\"alt\"> Graphics g = bi.getGraphics();</li><li class=\"\"> Color c = <span class=\"keyword\">new</span> Color(<span class=\"number\">200</span>,<span class=\"number\">155</span>,<span class=\"number\">255</span>);</li><li class=\"alt\"> g.setColor(c);</li><li class=\"\"> g.fillRect(<span class=\"number\">0</span>, <span class=\"number\">0</span>, <span class=\"number\">68</span>, <span class=\"number\">22</span>);</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">char</span>[] ch = <span class=\"string\">\"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\"</span>.toCharArray();</li><li class=\"alt\"> Random r = <span class=\"keyword\">new</span> Random();</li><li class=\"\"> <span class=\"keyword\">int</span> len = ch.length,index;</li><li class=\"alt\"> StringBuffer sb = <span class=\"keyword\">new</span> StringBuffer();</li><li class=\"\"> <span class=\"keyword\">for</span>(<span class=\"keyword\">int</span> i=<span class=\"number\">0</span>;i<<span class=\"number\">4</span>;i++) {</li><li class=\"alt\"> index = r.nextInt(len);</li><li class=\"\"> g.setColor(<span class=\"keyword\">new</span> Color(r.nextInt(<span class=\"number\">88</span>),r.nextInt(<span class=\"number\">188</span>),r.nextInt(<span class=\"number\">255</span>)));</li><li class=\"alt\"> g.drawString(ch[index]+<span class=\"string\">\"\"</span>, (i*<span class=\"number\">15</span>)+<span class=\"number\">3</span>, <span class=\"number\">18</span>);</li><li class=\"\"> sb.append(ch[index]);</li><li class=\"alt\"> }</li><li class=\"\"> request.getSession().setAttribute(<span class=\"string\">\"piccode\"</span>, sb.toString());</li><li class=\"alt\"> ImageIO.write(bi, <span class=\"string\">\"JPG\"</span>, response.getOutputStream());</li><li class=\"\"> }</li><li class=\"alt\"> <span class=\"annotation\">@Override</span></li><li class=\"\"> <span class=\"keyword\">protected</span> <span class=\"keyword\">void</span> doPost(HttpServletRequest request, HttpServletResponse response) <span class=\"keyword\">throws</span> ServletException, IOException {</li><li class=\"alt\"> doGet(request, response);</li><li class=\"\"> }</li><li class=\"alt\">}</li></ol></div><p><strong>LoginServlet.java</strong></p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">package</span> com.liuyanzhao;</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">import</span> java.io.IOException;</li><li class=\"\"><span class=\"keyword\">import</span> java.io.PrintWriter;</li><li class=\"alt\"></li><li class=\"\"><span class=\"keyword\">import</span> javax.servlet.ServletException;</li><li class=\"alt\"><span class=\"keyword\">import</span> javax.servlet.http.HttpServlet;</li><li class=\"\"><span class=\"keyword\">import</span> javax.servlet.http.HttpServletRequest;</li><li class=\"alt\"><span class=\"keyword\">import</span> javax.servlet.http.HttpServletResponse;</li><li class=\"\"></li><li class=\"alt\"><span class=\"comment\">/*</span></li><li class=\"\"><span class=\"comment\"> * @author LiuYanzhao</span></li><li class=\"alt\"><span class=\"comment\"> */</span></li><li class=\"\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> LoginServlet <span class=\"keyword\">extends</span> HttpServlet{</li><li class=\"alt\"> <span class=\"annotation\">@Override</span></li><li class=\"\"> <span class=\"keyword\">protected</span> <span class=\"keyword\">void</span> doPost(HttpServletRequest request, HttpServletResponse response) <span class=\"keyword\">throws</span> ServletException, IOException {</li><li class=\"alt\"> String piccode = (String)request.getSession().getAttribute(<span class=\"string\">\"piccode\"</span>);</li><li class=\"\"> String checkcode = request.getParameter(<span class=\"string\">\"checkcode\"</span>);</li><li class=\"alt\"> checkcode = checkcode.toUpperCase();<span class=\"comment\">//不区分大小写</span></li><li class=\"\"> response.setContentType(<span class=\"string\">\"text/html;charset=utf-8\"</span>);</li><li class=\"alt\"> PrintWriter out = response.getWriter();</li><li class=\"\"> <span class=\"keyword\">if</span>(piccode.equals(checkcode)) {</li><li class=\"alt\"> out.println(<span class=\"string\">\"验证码输入正确\"</span>);</li><li class=\"\"> } <span class=\"keyword\">else</span> {</li><li class=\"alt\"> out.println(<span class=\"string\">\"验证码输入错误\"</span>);</li><li class=\"\"> }</li><li class=\"alt\"> out.flush();</li><li class=\"\"> out.close();</li><li class=\"alt\"> }</li><li class=\"\"> <span class=\"annotation\">@Override</span></li><li class=\"alt\"> <span class=\"keyword\">protected</span> <span class=\"keyword\">void</span> doGet(HttpServletRequest request, HttpServletResponse response) <span class=\"keyword\">throws</span> ServletException, IOException {</li><li class=\"\"> doPost(request, response);</li><li class=\"alt\"> }</li><li class=\"\">}</li></ol></div><p>3、在 web.xml 里添加 Servlet 映射</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-xml\" start=\"1\"><li class=\"alt\"><span class=\"tag\"><</span><span class=\"tag-name\">servlet</span><span class=\"tag\">></span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">servlet-name</span><span class=\"tag\">></span>ImageServlet<span class=\"tag\"><!--</span--><span class=\"tag-name\">servlet-name</span><span class=\"tag\">></span></span></li><li class=\"alt\"> <span class=\"tag\"><</span><span class=\"tag-name\">servlet-class</span><span class=\"tag\">></span>com.liuyanzhao.ImageServlet<span class=\"tag\"><!--</span--><span class=\"tag-name\">servlet-class</span><span class=\"tag\">></span></span></li><li class=\"\"> <span class=\"tag\"><!--</span--><span class=\"tag-name\">servlet</span><span class=\"tag\">></span></span></li><li class=\"alt\"> <span class=\"tag\"><</span><span class=\"tag-name\">servlet-mapping</span><span class=\"tag\">></span></li><li class=\"\"> <span class=\"comments\"><!-- 映射为 ImageServlet --></span></li><li class=\"alt\"> <span class=\"tag\"><</span><span class=\"tag-name\">servlet-name</span><span class=\"tag\">></span>ImageServlet<span class=\"tag\"><!--</span--><span class=\"tag-name\">servlet-name</span><span class=\"tag\">></span></span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">url-pattern</span><span class=\"tag\">></span>/ImageServlet<span class=\"tag\"><!--</span--><span class=\"tag-name\">url-pattern</span><span class=\"tag\">></span></span></li><li class=\"alt\"> <span class=\"tag\"><!--</span--><span class=\"tag-name\">servlet-mapping</span><span class=\"tag\">></span></span></li><li class=\"\"></li><li class=\"alt\"> <span class=\"tag\"><</span><span class=\"tag-name\">servlet</span><span class=\"tag\">></span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">servlet-name</span><span class=\"tag\">></span>LoginServlet<span class=\"tag\"><!--</span--><span class=\"tag-name\">servlet-name</span><span class=\"tag\">></span></span></li><li class=\"alt\"> <span class=\"tag\"><</span><span class=\"tag-name\">servlet-class</span><span class=\"tag\">></span>com.liuyanzhao.LoginServlet<span class=\"tag\"><!--</span--><span class=\"tag-name\">servlet-class</span><span class=\"tag\">></span></span></li><li class=\"\"> <span class=\"tag\"><!--</span--><span class=\"tag-name\">servlet</span><span class=\"tag\">></span></span></li><li class=\"alt\"> <span class=\"tag\"><</span><span class=\"tag-name\">servlet-mapping</span><span class=\"tag\">></span></li><li class=\"\"> <span class=\"comments\"><!-- 映射为 IndexServlet --></span></li><li class=\"alt\"> <span class=\"tag\"><</span><span class=\"tag-name\">servlet-name</span><span class=\"tag\">></span>LoginServlet<span class=\"tag\"><!--</span--><span class=\"tag-name\">servlet-name</span><span class=\"tag\">></span></span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">url-pattern</span><span class=\"tag\">></span>/LoginServlet<span class=\"tag\"><!--</span--><span class=\"tag-name\">url-pattern</span><span class=\"tag\">></span></span></li><li class=\"alt\"> <span class=\"tag\"><!--</span--><span class=\"tag-name\">servlet-mapping</span><span class=\"tag\">></span></span></li></ol></div><p>4、运行 Tomcat 服务器,打开浏览器,访问 http://localhost:8080/CheckCode</p><p>效果如下</p><p><img src=\"/uploads/2017/10/20171007155723341.png\" alt=\"20171007155723341.png\"><br></p><p>验证码不区分大小写,点击 “看不清楚”可以更换</p>', '', '', '', '', '', '', '2018-11-25 20:53:29', '2017-10-07 15:57:38', null);
INSERT INTO `article` VALUES ('', '', 'Integer与int的种种比较你知道多少?', '<p>如果面试官问Integer与int的区别:估计大多数人只会说道两点,Ingeter是int的包装类,int的初值为0,Ingeter的初值为null。但是如果面试官再问一下Integer a = 1;int b = 1; a==b为true还是为false?估计就有一部分人答不出来了,如果再问一下其他的,估计更多的人会头脑一片混乱。所以我对它们进行了总结,希望对大家有帮助。</p><p><strong>首先看代码:</strong></p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"comment\">/* </span></li><li class=\"\"><span class=\"comment\"> * @author LiuYanzhao</span></li><li class=\"alt\"><span class=\"comment\"> *</span></li><li class=\"\"><span class=\"comment\"> */</span></li><li class=\"alt\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> Test {</li><li class=\"\"></li><li class=\"alt\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> main(String[] args) {</li><li class=\"\"></li><li class=\"alt\"> <span class=\"keyword\">int</span> i = <span class=\"number\">128</span>;</li><li class=\"\"> Integer i2 = <span class=\"number\">128</span>;</li><li class=\"alt\"> Integer i3 = <span class=\"keyword\">new</span> Integer(<span class=\"number\">128</span>);</li><li class=\"\"> <span class=\"comment\">//Integer会自动拆箱为int,所以为true</span></li><li class=\"alt\"> System.out.println(i == i2);<span class=\"comment\">//true</span></li><li class=\"\"> System.out.println(i == i3);<span class=\"comment\">//true</span></li><li class=\"alt\"></li><li class=\"\"> <span class=\"comment\">//java在编译的时候,被翻译成-> Integer i5 = Integer.valueOf(127);</span></li><li class=\"alt\"> Integer i5 = <span class=\"number\">127</span>;</li><li class=\"\"> Integer i6 = <span class=\"number\">127</span>;</li><li class=\"alt\"> System.out.println(i5 == i6);<span class=\"comment\">//true</span></li><li class=\"\"></li><li class=\"alt\"></li><li class=\"\"> Integer i7 = <span class=\"number\">128</span>;</li><li class=\"alt\"> Integer i8 = <span class=\"number\">128</span>;</li><li class=\"\"> System.out.println(i7 == i8);<span class=\"comment\">//false</span></li><li class=\"alt\"></li><li class=\"\"></li><li class=\"alt\"> Integer i9 = <span class=\"keyword\">new</span> Integer(<span class=\"number\">127</span>);</li><li class=\"\"> Integer i10 = <span class=\"number\">127</span>;</li><li class=\"alt\"> System.out.println(i9 == i10); <span class=\"comment\">//false</span></li><li class=\"\"></li><li class=\"alt\"></li><li class=\"\"> Integer i11 = <span class=\"keyword\">new</span> Integer(<span class=\"number\">128</span>);</li><li class=\"alt\"> Integer i12 = <span class=\"keyword\">new</span> Integer(<span class=\"number\">123</span>);</li><li class=\"\"> System.out.println(i11 == i12); <span class=\"comment\">//false</span></li><li class=\"alt\"> }</li><li class=\"\"></li><li class=\"alt\">}</li></ol></div><p><strong>详解如下:</strong></p><p>首先,13行和14行输出结果都为true,因为Integer和int比都会自动拆箱(jdk1.5以上)。</p><p>19行的结果为true,而24行则为false,很多人都不懂为什么。其实java在编译Integer i5 = 127的时候,被翻译成-> Integer i5 = Integer.valueOf(127);所以关键就是看valueOf()函数了。只要看看valueOf()函数的源码就会明白了。JDK源码的valueOf函数式这样的:</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">public</span> <span class=\"keyword\">static</span> Integer valueOf(<span class=\"keyword\">int</span> i) {</li><li class=\"\"> <span class=\"keyword\">assert</span> IntegerCache.high >= <span class=\"number\">127</span>;</li><li class=\"alt\"> <span class=\"keyword\">if</span> (i >= IntegerCache.low && i <= IntegerCache.high)</li><li class=\"\"> <span class=\"keyword\">return</span> IntegerCache.cache[i + (-IntegerCache.low)];</li><li class=\"alt\"> <span class=\"keyword\">return</span> <span class=\"keyword\">new</span> Integer(i);</li><li class=\"\">}</li></ol></div><p>看一下源码大家都会明白,对于-128到127之间的数,会进行缓存,Integer i5 = 127时,会将127进行缓存,下次再写Integer i6 = 127时,就会直接从缓存中取,就不会new了。所以22行的结果为true,而25行为false。</p><p>对于29行和34行,因为对象不一样,所以为false。</p><p>我对于以上的情况总结如下:</p><p>① 无论如何,<span><strong>Integer与new Integer不会相等</strong></span>。不会经历拆箱过程,i3的引用指向堆,而i4指向专门存放他的内存(常量池),他们的内存地址不一样,所以为false</p><p>② <strong><span>两个都是非new出来的Integer,如果数在-128到127之间,则是true,否则为false</span></strong></p><p>java在编译Integer i2 = 128的时候,被翻译成-> Integer i2 = Integer.valueOf(128);而valueOf()函数会对-128到127之间的数进行缓存</p><p>③ <strong><span>两</span><span>个都是new出来的,都为false</span></strong></p><p>④ <strong><span>int和Integer(无论new否)比,都为true</span></strong>,因为会把Integer自动拆箱为int再去比</p><p> </p><p>参考:<a href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=http://www.cnblogs.com/liuling/archive/2013/05/05/intAndInteger.html\" target=\"_blank\" rel=\"noopener noreferrer\">http://www.cnblogs.com/liuling/archive/2013/05/05/intAndInteger.html</a></p><div><br></div>', '', '', '', '', '', '', '2018-11-25 20:52:45', '2017-10-07 15:58:51', null);
INSERT INTO `article` VALUES ('', '', 'Java如何让程序一直运行,不停止', '<p>如何让程序一直运行,很容易实现,只需要一直等待输出即可啦</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">import</span> java.util.Scanner;</li><li class=\"\"></li><li class=\"alt\"><span class=\"comment\">/*</span></li><li class=\"\"><span class=\"comment\"> * @author LiuYanzhao</span></li><li class=\"alt\"><span class=\"comment\"> */</span></li><li class=\"\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> Test {</li><li class=\"alt\"> <span class=\"keyword\">private</span> <span class=\"keyword\">static</span> <span class=\"keyword\">final</span> String OPERATION_EXIT = <span class=\"string\">\"EXIT\"</span>;</li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> main(String[] args) {</li><li class=\"alt\"> System.out.println(<span class=\"string\">\"请开始您的输入,EXIT/E 退出\"</span>);</li><li class=\"\"> <span class=\"comment\">//怎么让程序一直运行</span></li><li class=\"alt\"> Scanner scan = <span class=\"keyword\">new</span> Scanner(System.in);</li><li class=\"\"></li><li class=\"alt\"> <span class=\"keyword\">while</span>(scan.hasNext()) {</li><li class=\"\"> String in = scan.next().toString();</li><li class=\"alt\"> <span class=\"keyword\">if</span>(OPERATION_EXIT.equals(in.toUpperCase())</li><li class=\"\"> || OPERATION_EXIT.substring(<span class=\"number\">0</span>, <span class=\"number\">1</span>).equals(in.toUpperCase())) {</li><li class=\"alt\"> System.out.println(<span class=\"string\">\"您成功已退出!\"</span>);</li><li class=\"\"> <span class=\"keyword\">break</span>;</li><li class=\"alt\"> }</li><li class=\"\"> System.out.println(<span class=\"string\">\"您输入的值:\"</span>+in);</li><li class=\"alt\"></li><li class=\"\"> }</li><li class=\"alt\"> }</li><li class=\"\">}</li><li></li></ol></div>', '', '', '', '', '', '', '2018-11-25 20:47:52', '2017-10-07 15:59:33', null);
INSERT INTO `article` VALUES ('', '', 'Java中静态代码块、构造代码块的区别', '<p>直接在类中定义且没有加static关键字的代码块称为{}构造代码块。</p><p><strong><span>构造代码块在创建对象时被调用,每次创建对象都会被调用</span></strong>,并且构造代码块的执行次序优先于类构造函数。</p><p>静态代码块:在java中使用static关键字声明的代码块。<span><strong>静态块用于初始化类,为类的属性初始化。每个静态代码块只会执行一次。由于JVM在加载类时会执行静态代码块,所以静态代码块先于主方法执行</strong></span>。</p><p>注意:1 静态代码块不能存在于任何方法体内。2 静态代码块不能直接访问静态实例变量和实例方法,需要通过类的实例对象来访问。</p><p>静态代码块、构造代码块、构造函数同时存在时的执行顺序:<strong><span>静态代码块 > 构造代码块 > 构造函数</span></strong>;</p><p> </p><p>下面我们来举几个例子说明一切</p><p><strong>demo1:构造方法</strong></p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> Test {</li><li class=\"\"> <span class=\"keyword\">static</span> {</li><li class=\"alt\"> System.out.println(<span class=\"string\">\"静态块\"</span>);</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> {</li><li class=\"alt\"> System.out.println(<span class=\"string\">\"构造块,在类中定义\"</span>);</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> Test() {</li><li class=\"alt\"> System.out.println(<span class=\"string\">\"构造方法执行\"</span>);</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> main(String[] args) {</li><li class=\"alt\"> <span class=\"keyword\">new</span> Test();</li><li class=\"\"> <span class=\"keyword\">new</span> Test();</li><li class=\"alt\"> }</li><li class=\"\"> }</li><li class=\"alt\"><span class=\"comment\">/*</span></li><li class=\"\"><span class=\"comment\">静态块</span></li><li class=\"alt\"><span class=\"comment\">构造块,在类中定义</span></li><li class=\"\"><span class=\"comment\">构造方法执行</span></li><li class=\"alt\"><span class=\"comment\">构造块,在类中定义</span></li><li class=\"\"><span class=\"comment\">构造方法执行</span></li><li class=\"alt\"><span class=\"comment\">*/</span></li></ol></div><p><strong>demo2:普通代码块</strong></p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"comment\">/*普通代码块:在方法或语句中出现的{}就称为普通代码块。</span></li><li class=\"\"><span class=\"comment\">普通代码块和一般的语句执行顺序由他们在代码中出现的次序决定--“先出现先执行”*/</span></li><li class=\"alt\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> Test {</li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> main(String[] args) {</li><li class=\"alt\"></li><li class=\"\"> {</li><li class=\"alt\"> <span class=\"keyword\">int</span> x = <span class=\"number\">3</span>;</li><li class=\"\"> System.out.println(<span class=\"string\">\"1,普通代码块内的变量x=\"</span> + x);</li><li class=\"alt\"> }</li><li class=\"\"></li><li class=\"alt\"> <span class=\"keyword\">int</span> x = <span class=\"number\">1</span>;</li><li class=\"\"> System.out.println(<span class=\"string\">\"主方法内的变量x=\"</span> + x);</li><li class=\"alt\"></li><li class=\"\"> {</li><li class=\"alt\"> <span class=\"keyword\">int</span> y = <span class=\"number\">7</span>;</li><li class=\"\"> System.out.println(<span class=\"string\">\"2,普通代码块内的变量y=\"</span> + y);</li><li class=\"alt\"> }</li><li class=\"\"> }</li><li class=\"alt\">}</li><li class=\"\"></li><li class=\"alt\"><span class=\"comment\">/* </span></li><li class=\"\"><span class=\"comment\">1,普通代码块内的变量x=3</span></li><li class=\"alt\"><span class=\"comment\">主方法内的变量x=1</span></li><li class=\"\"><span class=\"comment\">2,普通代码块内的变量y=7</span></li><li class=\"alt\"><span class=\"comment\">*/</span></li></ol></div><p><strong>demo3:构造代码块</strong></p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"comment\">/*构造块:直接在类中定义且没有加static关键字的代码块称为{}构造代码块。</span></li><li class=\"\"><span class=\"comment\">构造代码块在创建对象时被调用,每次创建对象都会被调用,</span></li><li class=\"alt\"><span class=\"comment\">并且构造代码块的执行次序优先于类构造函数。*/</span></li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> Test {</li><li class=\"\"> {</li><li class=\"alt\"> System.out.println(<span class=\"string\">\"第一代码块\"</span>);</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> Test() {</li><li class=\"alt\"> System.out.println(<span class=\"string\">\"构造方法\"</span>);</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> {</li><li class=\"alt\"> System.out.println(<span class=\"string\">\"第二构造块\"</span>);</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> main(String[] args) {</li><li class=\"alt\"> <span class=\"keyword\">new</span> Test();</li><li class=\"\"> <span class=\"keyword\">new</span> Test();</li><li class=\"alt\"> <span class=\"keyword\">new</span> Test();</li><li class=\"\"> }</li><li class=\"alt\">}</li><li class=\"\"><span class=\"comment\">/*</span></li><li class=\"alt\"><span class=\"comment\">第一代码块</span></li><li class=\"\"><span class=\"comment\">第二构造块</span></li><li class=\"alt\"><span class=\"comment\">构造方法</span></li><li class=\"\"><span class=\"comment\">第一代码块</span></li><li class=\"alt\"><span class=\"comment\">第二构造块</span></li><li class=\"\"><span class=\"comment\">构造方法</span></li><li class=\"alt\"><span class=\"comment\">第一代码块</span></li><li class=\"\"><span class=\"comment\">第二构造块</span></li><li class=\"alt\"><span class=\"comment\">构造方法</span></li><li class=\"\"><span class=\"comment\">*/</span></li></ol></div><p><strong>demo4:静态代码块</strong></p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"comment\">/*静态代码块:在java中使用static关键字声明的代码块。</span></li><li class=\"\"><span class=\"comment\">静态块用于初始化类,为类的属性初始化。</span></li><li class=\"alt\"><span class=\"comment\">每个静态代码块只会执行一次。</span></li><li class=\"\"><span class=\"comment\">由于JVM在加载类时会执行静态代码块,所以静态代码块先于主方法执行。</span></li><li class=\"alt\"><span class=\"comment\">如果类中包含多个静态代码块,那么将按照\"先定义的代码先执行,后定义的代码后执行\"。</span></li><li class=\"\"><span class=\"comment\">注意:1 静态代码块不能存在于任何方法体内。</span></li><li class=\"alt\"><span class=\"comment\"> 2 静态代码块不能直接访问静态实例变量和实例方法,需要通过类的实例对象来访问。*/</span></li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">class</span> Code {</li><li class=\"\"> {</li><li class=\"alt\"> System.out.println(<span class=\"string\">\"Code的构造块\"</span>);</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">static</span> {</li><li class=\"alt\"> System.out.println(<span class=\"string\">\"Code的静态代码块\"</span>);</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> Code() {</li><li class=\"alt\"> System.out.println(<span class=\"string\">\"Code的构造方法\"</span>);</li><li class=\"\"> }</li><li class=\"alt\">}</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> Test {</li><li class=\"\"> {</li><li class=\"alt\"> System.out.println(<span class=\"string\">\"Test的构造块\"</span>);</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">static</span> {</li><li class=\"alt\"> System.out.println(<span class=\"string\">\"Test的静态代码块\"</span>);</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> Test() {</li><li class=\"alt\"> System.out.println(<span class=\"string\">\"Test的构造方法\"</span>);</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> main(String[] args) {</li><li class=\"alt\"> System.out.println(<span class=\"string\">\"Test的主方法\"</span>);</li><li class=\"\"> <span class=\"keyword\">new</span> Code();</li><li class=\"alt\"> <span class=\"keyword\">new</span> Code();</li><li class=\"\"> <span class=\"keyword\">new</span> Test();</li><li class=\"alt\"> <span class=\"keyword\">new</span> Test();</li><li class=\"\"> }</li><li class=\"alt\">}</li><li class=\"\"><span class=\"comment\">/*</span></li><li class=\"alt\"><span class=\"comment\">Test的静态代码块</span></li><li class=\"\"><span class=\"comment\">Test的主方法</span></li><li class=\"alt\"><span class=\"comment\">Code的静态代码块</span></li><li class=\"\"><span class=\"comment\">Code的构造块</span></li><li class=\"alt\"><span class=\"comment\">Code的构造方法</span></li><li class=\"\"><span class=\"comment\">Code的构造块</span></li><li class=\"alt\"><span class=\"comment\">Code的构造方法</span></li><li class=\"\"><span class=\"comment\">Test的构造块</span></li><li class=\"alt\"><span class=\"comment\">Test的构造方法</span></li><li class=\"\"><span class=\"comment\">Test的构造块</span></li><li class=\"alt\"><span class=\"comment\">Test的构造方法</span></li><li class=\"\"><span class=\"comment\"> */</span></li><li></li></ol></div>', '', '', '', '', '', '', '2018-11-25 20:47:40', '2017-10-07 16:00:06', null);
INSERT INTO `article` VALUES ('', '', 'sql语句分为三类(DML,DDL,DCL)-介绍', '<p><span>DML(data manipulation language):数据库操作语言</span><br><span>它们是SELECT、UPDATE、INSERT、DELETE,就象它的名字一样</span></p><p> </p><p><span>DDL(data definition language):的数据库定义语言</span><br><span>主要的命令有CREATE、ALTER、DROP等,DDL主要是用在定义或改变表(TABLE)的结构,数据类型,表之间的链接和约束等初始化工作上,他们大多在建立表时使用</span></p><p> </p><p><span>DCL(Data Control Language): 数据库控制语言</span><br><span>是用来设置或更改数据库用户或角色权限的语句,包括(grant,deny,revoke等)语句。在默认状态下,只有sysadmin,dbcreator,db_owner或db_securityadmin等人员才有权力执行DCL</span></p><p> </p><p><strong>详细解释:</strong></p><h2>一、DDL is Data Definition Language statements</h2><p>数据定义语言,用于定义和管理 <a href=\"https://liuyanzhao.com/tag/sql/\" title=\"查看与 SQL 相关的文章\" target=\"_blank\">SQL</a> 数据库中的所有对象的语言</p><p>1.CREATE - to create objects in the database 创建</p><p>2.ALTER - alters the structure of the database 修改</p><p>3.DROP - delete objects from the database 删除</p><p>4.TRUNCATE - remove all records from a table, including all spaces allocated for the records are removed</p><p>TRUNCATE TABLE [Table Name]。</p><p>下面是对Truncate语句在MS<a href=\"https://liuyanzhao.com/tag/sql/\" title=\"查看与 SQL 相关的文章\" target=\"_blank\">SQL</a>Server2000中用法和原理的说明:</p><p>Truncate table 表名 速度快,而且效率高,因为:</p><p>TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同:二者均删除表中的全部行。但</p><p>TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少。</p><p>DELETE 语句每次删除一行,并在事务日志中为所删除的每行记录一项。TRUNCATE TABLE 通过释放存</p><p>储表数据所用的数据页来删除数据,并且只在事务日志中记录页的释放。</p><p>TRUNCATE TABLE 删除表中的所有行,但表结构及其列、约束、索引等保持不变。新行标识所用的计数值重置为该列的种子。如果想保留标识计数值,请改用 DELETE。如果要删除表定义及其数据,请使用 DROP TABLE 语句。</p><p>对于由 FOREIGN KEY 约束引用的表,不能使用 TRUNCATE TABLE,而应使用不带 WHERE 子句的 DELETE 语句。由于 TRUNCATE TABLE 不记录在日志中,所以它不能激活触发器。</p><p>TRUNCATE TABLE 不能用于参与了索引视图的表。</p><p>5.COMMENT - add comments to the data dictionary 注释</p><p>6.GRANT - gives user\'s access privileges to database 授权</p><p>7.REVOKE - withdraw access privileges given with the GRANT command 收回已经授予的权限</p><p> </p><h2>二、DML is Data Manipulation Language statements</h2><p>数据操作语言,SQL中处理数据等操作统称为数据操纵语言</p><p>1.SELECT - retrieve data from the a database 查询</p><p>2.INSERT - insert data into a table 添加</p><p>3.UPDATE - updates existing data within a table 更新</p><p>4.DELETE - deletes all records from a table, the space for the records remain 删除</p><p>5.CALL - call a PL/SQL or Java subprogram</p><p>6.EXPLAIN PLAN - explain access path to data</p><p>Oracle RDBMS执行每一条SQL语句,都必须经过Oracle优化器的评估。所以,了解优化器是如何选择(搜索)路径以及索引是如何被使用的,对优化SQL语句有很大的帮助。Explain可以用来迅速方便地查出对于给定SQL语句中的查询数据是如何得到的即搜索路径(我们通常称为Access Path)。从而使我们选择最优的查询方式达到最大的优化效果。</p><p>7.LOCK TABLE - control concurrency 锁,用于控制并发</p><p> </p><h2>三、DCL is Data Control Language statements</h2><p>数据控制语言,用来授予或回收访问数据库的某种特权,并控制数据库操纵事务发生的时间及效果,对数据库实行监视等</p><p>1.COMMIT - save work done 提交</p><p>2.SAVEPOINT - identify a point in a transaction to which you can later roll back 保存点</p><p>3.ROLLBACK - restore database to original since the last COMMIT 回滚</p><p>4.SET TRANSACTION - Change transaction options like what rollback segment to use 设置当前事务的特性,它对后面的事务没有影响</p><div><br></div>', '1', '3', '1', '1', '1', '1', '2018-11-25 20:47:30', '2017-10-07 16:00:50', null);
INSERT INTO `article` VALUES ('14', '1', 'execute、executeUpdate、executeQuery三者的区别', '<p>1. ResultSet executeQuery(String sql); 执行SQL查询,并返回 ResultSet 对象。</p><p>例如:被执行最多的 SELECT 语句。</p><p>2.int executeUpdate(String sql); 可执行增,删,改,返回执行受到影响的行数。</p><p>例如: INSERT、UPDATE 或 DELETE 语句以及 SQL DDL(数据定义语言)语句</p><p>3. boolean execute(String sql); 可执行任何SQL语句,返回一个布尔值,表示是否返回 ResultSet 。</p><p><strong> <span> execute是executeQuery和executeUpdate的综合.</span></strong></p><p><strong><span>通常我们没有必要使用execute方法来执行SQL语句,而是使用 executeQuery 或 executeUpdate 更适合。</span></strong></p><p>-----</p><p> </p><p>executeUpdate() 这是 PreparedStatement 接口中的方法</p><p>executeUpdate(String sql) 这是 PreparedStatement 从父接口 Statement 中继承过来的方法</p><p>executeUpdate() 中执行 SQL 语句需要在创建 PerparedStatement 时通过 Connection 的 prepareStatement(String sql) 方法中写出,因为 PerparedStatement 中的 SQL 语句数据库需要进行预编译和缓存,因此要在创建 PerparedStatement 对象时给出 SQL 语句。</p><p>而 executeUpdate(String sql) 是 Statement 中的方法,参数中的 SQL 语句只是提交给数据库去执行,并不需要预编译。</p><p><strong>如果 SQL 语句中有 ? 占位符,那么在设置好占位符中的值后,必须使用 executeUpdate() 执行。而 executeUpdate(String sql) 只是提交一个 SQL 语句,且这个语句中不能带有 ? 占位符。</strong></p><p>当然,我们最好不要用 Statement 接口<br>参考:http://www.cnblogs.com/bukudekong/archive/2011/06/22/2086531.html</p>', '4', '3', '0', '1', '1', '1', '2018-11-25 20:45:25', '2017-10-07 16:02:12', null);
INSERT INTO `article` VALUES ('', '', 'JSP 中 forward 转发 和 sendRedirect 重定向的区别', '<h2>本质区别:</h2><div> <b>一句话概括:</b>重定向是客户端行为,转发是服务器行为.</div><div><b> 1.请求次数:</b></div><div> 重定向:重定向行为是做了两次请求,及产生了两个request对象,重定向会导致request对象信息丢失。</div><div> 请求转发:转发做了一次请求, 浏览器的地址栏一直是第一次请求的地址。转发是服务器内部request/response控制权的移交。</div><div> <b>2.请求资源地址:</b></div><p><strong>重定向</strong>:web组件可以将请求重定向到任意一个url,而不仅仅是同一个应用。</p><p>重定向的源组件与目标组件不共用同一个HttpServletRequest对象,因此不能在request范围内共享数据。</p><p><strong>请求转发</strong>:转发的源组件与目标组件必须在同一个应用中,两者可以在request范围内共享数据。</p><div><strong> 3.一种解释:</strong></div><div><p>假设你去办理某个执照</p><p><strong>重定向</strong>:你先去了A局,A局的人说:“这个事情不归我们管,去B局”,然后,你就从A退了出来,自己乘车去了B局。</p><p><strong>请求转发</strong>:你先去了A局,A局看了以后,知道这个事情其实应该B局来管,但是他没有把你退回来,而是让你坐一会儿,自己到后面办公室联系了B的人,让他们办好后,送了过来。</p></div><div><h2><b>工作流程:</b></h2></div><p><strong>1.重定向</strong></p><p>浏览器发出http请求 <b>>></b> 服务器接受请求并发送302状态码和新的对应的url到浏览器 <b>>></b></p><p>浏览器接收响应并自动请求新的url <b>>></b> 服务器接收请求并寻找客户所需的资源响应到浏览器</p><p><strong>2.转发</strong></p><p>浏览器发出http求其 <b>>></b> 服务器接收请求 <b>>></b> 服务器调用内部的一个方法在容器内完成请求处理和转发动作 <b>>></b></p><p>将客户所需资源发送到浏览</p><p> </p><h2><b>调用方式:</b></h2><p>我们知道,在servlet中调用转发、重定向的语句如下:</p><p>request.getRequestDispatcher(\"new.jsp\").forward(request, response); //转发到new.jsp</p><p>response.sendRedirect(\"new.jsp\"); //重定向到new.jsp</p><p>在jsp页面中你也会看到通过下面的方式实现转发:</p><p><jsp:forward page=\"apage.jsp\"></jsp:forward></p><p>当然也可以在jsp页面中实现重定向:</p><p><%response.sendRedirect(\"new.jsp\"); %> //重定向到new.jsp</p><p> </p><div> <strong> 1.重定向:</strong></div><div> 1).response.sendRedirect(url);</div><div> 2).response.setState(302);</div><div> response.setHeader(\"location\",url);</div><div><strong> 2.转发:</strong></div><div> 1).request.getRequestDispatcher(url).forward(request,response);</div><div> 2).request.getRequestDispatcher(url).include(request,response);</div><div> 3).<jsp:forward page=\"url\"></jsp:forward></div>', '', '', '', '', '', '', '2018-11-25 20:47:10', '2017-10-07 16:03:08', null);
INSERT INTO `article` VALUES ('', '', 'Java中File类的使用', '<h2>一、File类常用API介绍</h2><p>内容见代码和注释</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">package</span> com.liuyanzhao;</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">import</span> java.io.File;</li><li class=\"\"><span class=\"keyword\">import</span> java.io.IOException;</li><li class=\"alt\"></li><li class=\"\"><span class=\"comment\">/*</span></li><li class=\"alt\"><span class=\"comment\"> *</span></li><li class=\"\"><span class=\"comment\"> * @author WellsLiu</span></li><li class=\"alt\"><span class=\"comment\"> *</span></li><li class=\"\"><span class=\"comment\"> */</span></li><li class=\"alt\"></li><li class=\"\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> Test {</li><li class=\"alt\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> main(String[] args) {</li><li class=\"\"> <span class=\"comment\">//1.创建一个文件夹对象</span></li><li class=\"alt\"> File file = <span class=\"keyword\">new</span> File(<span class=\"string\">\"F:\\\\桌面\\\\music\"</span>);</li><li class=\"\"> <span class=\"comment\">//判断文件是否存在</span></li><li class=\"alt\"> System.out.println(file.exists());<span class=\"comment\">//true</span></li><li class=\"\"> <span class=\"comment\">//如果文件夹不存在创建之,否则删除之</span></li><li class=\"alt\"> <span class=\"keyword\">if</span>(!file.exists())</li><li class=\"\"> file.mkdir();<span class=\"comment\">//创建文件夹</span></li><li class=\"alt\"> <span class=\"comment\">//else </span></li><li class=\"\"> <span class=\"comment\">//file.delete();</span></li><li class=\"alt\"> <span class=\"comment\">//是否是一个目录</span></li><li class=\"\"> System.out.println(file.isDirectory());<span class=\"comment\">//true</span></li><li class=\"alt\"> <span class=\"comment\">//是否是一个文件</span></li><li class=\"\"> System.out.println(file.isFile());<span class=\"comment\">//false</span></li><li class=\"alt\"></li><li class=\"\"> <span class=\"comment\">//2.创建一个文件对象</span></li><li class=\"alt\"> File file2 = <span class=\"keyword\">new</span> File(<span class=\"string\">\"F:\\\\桌面\\\\music\\\\1.mp3\"</span>);</li><li class=\"\"> System.out.println(file.exists());</li><li class=\"alt\"> <span class=\"comment\">//如果文件不存在创建之,否则删除 之</span></li><li class=\"\"> <span class=\"keyword\">if</span>(file2.exists())</li><li class=\"alt\"> <span class=\"keyword\">try</span> {</li><li class=\"\"> file2.createNewFile();</li><li class=\"alt\"> } <span class=\"keyword\">catch</span> (IOException e) {</li><li class=\"\"> e.printStackTrace();</li><li class=\"alt\"> }</li><li class=\"\"> <span class=\"comment\">//else </span></li><li class=\"alt\"> <span class=\"comment\">//file2.delete();</span></li><li class=\"\"></li><li class=\"alt\"> <span class=\"comment\">//3.常用的File对象API</span></li><li class=\"\"> System.out.println(file);<span class=\"comment\">//file.toString()的内容,F:\\桌面\\music</span></li><li class=\"alt\"> System.out.println(file.getAbsolutePath());<span class=\"comment\">//file的绝对路径F:\\桌面\\music</span></li><li class=\"\"> System.out.println(file.getName());<span class=\"comment\">//music</span></li><li class=\"alt\"> System.out.println(file2.getName());<span class=\"comment\">//1.mp3</span></li><li class=\"\"> System.out.println(file.getParent());<span class=\"comment\">//F:\\桌面</span></li><li class=\"alt\"> }</li><li class=\"\">}</li></ol></div><p> </p><h2>二、遍历目录</h2><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">package</span> com.liuyanzhao;</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">import</span> java.io.File;</li><li class=\"\"><span class=\"keyword\">import</span> java.io.IOException;</li><li class=\"alt\"></li><li class=\"\"><span class=\"comment\">/*</span></li><li class=\"alt\"><span class=\"comment\"> *</span></li><li class=\"\"><span class=\"comment\"> * @author WellsLiu</span></li><li class=\"alt\"><span class=\"comment\"> *</span></li><li class=\"\"><span class=\"comment\"> */</span></li><li class=\"alt\"></li><li class=\"\"><span class=\"comment\">//列出File类一些常用操作比如过滤、遍历等操作</span></li><li class=\"alt\"><span class=\"keyword\">class</span> FileUtils {</li><li class=\"\"></li><li class=\"alt\"> <span class=\"comment\">//列出指定目录下(包括子目录)的所有文件</span></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> listDirectory(File dir) <span class=\"keyword\">throws</span> IOException{</li><li class=\"alt\"> <span class=\"keyword\">if</span>(!dir.exists()) {</li><li class=\"\"> <span class=\"keyword\">throw</span> <span class=\"keyword\">new</span> IllegalArgumentException(<span class=\"string\">\"目录\"</span>+dir+<span class=\"string\">\"不存在\"</span>);</li><li class=\"alt\"> }</li><li class=\"\"> <span class=\"keyword\">if</span>(!dir.isDirectory()) { <span class=\"comment\">//判断File类的对象是否为文件夹</span></li><li class=\"alt\"> <span class=\"keyword\">throw</span> <span class=\"keyword\">new</span> IllegalArgumentException(dir+<span class=\"string\">\"不是目录\"</span>);</li><li class=\"\"> }</li><li class=\"alt\"> <span>/*</span></li><li class=\"\"><span class=\"comment\"> //1.输出dir目录下的一级文件夹和文件</span></li><li class=\"alt\"><span class=\"comment\"> String [] filenames = dir.list();//返回的是字符串数组 直接子名称</span></li><li class=\"\"><span class=\"comment\"> for (String string : filenames) {</span></li><li class=\"alt\"><span class=\"comment\"> System.out.println(dir+\"\\\\\"+string);//dirstring是文件或文件夹名</span></li><li class=\"\"><span class=\"comment\"> }</span></li><li class=\"alt\"><span class=\"comment\"> */</span></li><li class=\"\"></li><li class=\"alt\"> <span class=\"comment\">//2.如果要遍历子目录下的目录及所有文件,就需要构造File对象做递归操作</span></li><li class=\"\"> File[] files = dir.listFiles();<span class=\"comment\">//返回的是直接子目录(文件)的抽象</span></li><li class=\"alt\"> <span class=\"keyword\">if</span>(files!=<span class=\"keyword\">null</span>&&files.length><span class=\"number\">0</span>) {</li><li class=\"\"> <span class=\"keyword\">for</span>(File file:files) {</li><li class=\"alt\"> <span class=\"keyword\">if</span>(file.isDirectory()) {</li><li class=\"\"> <span class=\"comment\">//递归</span></li><li class=\"alt\"> listDirectory(file);</li><li class=\"\"> } <span class=\"keyword\">else</span> {</li><li class=\"alt\"> System.out.println(file);</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> }</li><li class=\"alt\"> }</li><li class=\"\"> }</li><li class=\"alt\">}</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> Test1 {</li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">static</span> <span class=\"keyword\">void</span> main(String[] args) <span class=\"keyword\">throws</span> IOException{</li><li class=\"alt\"> FileUtils.listDirectory(<span class=\"keyword\">new</span> File(\"C:\\\\Users\\\\Liu_Yanzhao\"));</li><li class=\"\"> }</li><li class=\"alt\">}</li></ol></div>', '', '', '', '', '', '', '2018-11-25 20:46:43', '2017-10-07 16:04:25', null);
INSERT INTO `article` VALUES ('', '', 'java中ImageIcon路径问题', '<h2><strong>一、问题</strong></h2><p>ImageIcon icon = new ImageIcon(\"logo.jpg\");</p><p>执行结果是icon无法显示</p><p> </p><h2><strong>二、解决</strong></h2><p>类中有如下调用:</p><p><span>ImageIcon icon = new ImageIcon(\"1.jpg\");</span></p><p> </p><p>很自然地认为当前类文件和图片在同一路径下即可。</p><p><span><strong>其实所谓的相对路径起点是工程的根目录,即project。</strong></span></p><p>这行代码执行时在project目录下查找名为a.gif的文件,结果当然是找不到。</p><p>要得到工程的相对路径可通过System.getProperty(\"user.dir\")得到。</p><p>对图片和对文件的查找应该是一致的,如new File()。</p><p> </p><p>1、假如你的工程根目录为:Project1</p><p>你的图片文件放在:Project1/src/images/1.jpg</p><p>所以正确的调用是(不要加Project1):</p><p>ImageIcon icon = new ImageIcon(\"src/images/1.jpg\");</p><p> </p><p>2、假如你的工程根目录为:Project1</p><p>你的图片文件在:Project1/src/com.liuyanzhao/1.jpg</p><p>所以正确的调用是(<strong>com.liuyanzhao</strong>是包名)</p><p>ImageIcon icon = new ImageIcon(\"src/com/liuyanzhao/1.jpg\");</p><p>这行代码执行时在project/test目录下查找到了文件</p><p> </p><p><strong>总结起来就是一句话:所谓相对路径就是相对于工程根目录的位置^_^</strong></p><p> </p><h2>三、其他</h2><p>查看了一下ImageIcon的构造函数。</p><p>Public ImageIcon(String filename)//参数可以是绝对路径也可以是相对路径</p><p>Public ImageIcon(URL url)</p><p> </p><p>第一种构造不在赘述。</p><p>第二种通过URL来得到图片</p><p>URL url = getClass().getResource(\"a.gif\"); //当前编译后class文件所在目录查找</p><p>ImageIcon icon = new ImageIcon(url);</p><p> </p><p>ImageIcon支持GIF、JPG、PNG等格式。</p>', '', '', '', '', '', '', '2018-11-25 20:46:27', '2017-10-07 16:05:55', null);
INSERT INTO `article` VALUES ('', '', 'SpringCloud 中使用 Eureka 和 Feign 实现增删改查', '<p>在没有接触微服务或者分布式项目之前,我们的项目时没有分层的,我们的服务也是没有分层的。</p><p>比如,我们有5台服务器使用 Nginx 做负载均衡,上面都是安装了我们的 Tomcat 服务器,部署的一样的项目。当用户访问某个请求的时候,按照负载均衡的机制(随机、轮询之类)来转发到对应的服务器上,然后在该服务器上先是到 Controller,然后 Service,再到 DAO,然后返回给 Service,返回给 Controller,最后返回给前台。似乎这样也行。</p><p>但是当我们网站访问量不断飙升的时候,似乎有点撑不住了,只能加服务器,成本也明显增加。我们能不能再分一分呢 ?</p><p>同时,我们发现真正比较耗费时间的是一些 IO 操作,比如数据库操作、上传文件之类的。为了方便开发,我们可以把 DAO 和 Service 层放在一起,暂且称为业务层。业务层耗费的远远大于 Controller 层的。我们可以在 5 台服务器中拿 1 台部署 Controller 层,另外 4 台部署业务层代码。</p><p>如果将他们连接起来呢?</p><p>Eureka + Feign!</p><p>Eureka 做注册中心,业务层和控制器层每一个实例可以作为一个服务,比如 UserService 是一个服务,UserController 也可以是一个服务,一个服务可以有多个状态(可以有多台机器注册到该服务)。</p><p>下面通过一个例子来讲解</p><p> </p><p>源码地址:<a href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=https://github.com/saysky/spring-cloud-demo\">https://github.com/saysky/spring-cloud-demo</a></p><p> </p><h2>一、下载源码</h2><p>请上 Github 上下载本例子源码</p><p>源码地址:<a href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=https://github.com/saysky/spring-cloud-demo\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/saysky/spring-cloud-demo</a></p><p> </p><h2>二、项目结构</h2><p>注意这是三个项目(不是一个项目),用 IDEA 打开分别打开三个项目,最终如图</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/springcloud40.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8631 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/springcloud40-1024x385.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/springcloud40-1024x385.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"595\" height=\"224\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/springcloud40-1024x385.png 1024w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/springcloud40-300x113.png 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/springcloud40-768x289.png 768w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/springcloud40-530x200.png 530w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/springcloud40.png 1334w\" sizes=\"(max-width: 595px) 100vw, 595px\"></a></p><p> </p><p>1、eureka-server(一个 SpringBoot 项目)</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/eureka-server.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8633 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/eureka-server.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/eureka-server.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"420\" height=\"262\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/eureka-server.png 772w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/eureka-server-300x187.png 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/eureka-server-768x480.png 768w\" sizes=\"(max-width: 420px) 100vw, 420px\"></a></p><p> </p><p> </p><p>2、cloud (一个多模块项目,API + 业务层) <a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud2.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8632 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud2.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud2.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"418\" height=\"121\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud2.png 574w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud2-300x87.png 300w\" sizes=\"(max-width: 418px) 100vw, 418px\"></a></p><p> </p><p> </p><p>(1)其中 cloud-api 是 api,里面有 model 和 service接口,cloud-core 和 cloud-web 将依赖它</p><p>到时候需要首先将该模块 install,即打包到本地仓库中</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/api.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8634 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/api.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/api.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"463\" height=\"414\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/api.png 672w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/api-300x269.png 300w\" sizes=\"(max-width: 463px) 100vw, 463px\"></a></p><p> </p><p>(2)其中 cloud-core,里面 dao 和 serviceImpl,作为生产者的存在</p><p>启动类在这里面</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/core.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8635 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/core.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/core.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"439\" height=\"460\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/core.png 800w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/core-286x300.png 286w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/core-768x804.png 768w\" sizes=\"(max-width: 439px) 100vw, 439px\"></a></p><p> </p><p>3、cloud-web( 一个 SpringBoot 项目)</p><p>只有控制器层,需要依赖 cloud-api ,作为消费者的存在(可以是其他人调用)</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-web.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8636 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-web.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-web.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"439\" height=\"359\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-web.png 746w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-web-300x245.png 300w\" sizes=\"(max-width: 439px) 100vw, 439px\"></a></p><p> </p><h2>三、如何启动</h2><p>1、新建数据库 summer,导入 user.sql</p><p>2、导入新项目,先后顺序随便(如eureka-server、cloud、cloud-web)</p><p>3、启动 eureka-server 项目,端口为 8761</p><p>4、对 cloud 项目中的 cloud-api 模块进行install,maven命令是 mvn clean install</p><p>5、启动 cloud-core 项目,端口为 8090</p><p>6、(可选)其实这时候我们可以在地址栏访问一下 localhost:8090/user 其实是可以访问的,如果你不行,说明有问题</p><p>7、启动 cloud-web 项目,端口为 8080</p><p>8、在地址栏访问 localhost:8080/user,如果可以看到数据,说明成功</p><p> </p><h2>四、补充</h2><p>1、我这里模拟了四台注册了业务层服务(cloud-producer) 和 一台控制器层服务(cloud-consumer)</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/server.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8637 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/server-1024x655.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/server-1024x655.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"644\" height=\"412\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/server-1024x655.png 1024w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/server-300x192.png 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/server-768x491.png 768w\" sizes=\"(max-width: 644px) 100vw, 644px\"></a></p><p>ps:因为我是用本机测试,所以开了不同的端口表示不同的服务器</p><p> </p><p>2、访问控制器层,然后 Feign 会帮我们在注册中心中找到对应的服务名和对应的方法</p><p>我们访问 localhost:8080/user/1 首先到的是控制器层,控制器层调用 userService,userService 是 Cloud-API 里的,添加了 @FeignClient(name=\"cloud-producer\") 注解,然后找到在注册中心中寻找 cloud-producer 的服务,从一台(负载均衡)服务器中找到 userService 的实现类,即 userServiceImpl,也就是进入了 cloud-core,然后完成一系列 DAO 和 Service 操作,最后返回给 Controller。</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/8080.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8638 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/8080-1024x680.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/8080-1024x680.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"625\" height=\"415\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/8080-1024x680.png 1024w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/8080-300x199.png 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/8080-768x510.png 768w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/8080.png 1148w\" sizes=\"(max-width: 625px) 100vw, 625px\"></a></p><p>其中 Service 和 Service 的实现都需要加 RequestMapping,Service 实现需要加 @RestController,否则会出现 404</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8639 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api-1024x373.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api-1024x373.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"627\" height=\"228\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api-1024x373.png 1024w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api-300x109.png 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api-768x280.png 768w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api.png 1538w\" sizes=\"(max-width: 627px) 100vw, 627px\"></a></p><p>所以,其实我们可以直接访问 cloud-core 这一层,在地址栏访问 localhost:8090/user/1</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api2.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8640 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api2.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api2.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"497\" height=\"383\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api2.png 784w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api2-300x231.png 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api2-768x592.png 768w\" sizes=\"(max-width: 497px) 100vw, 497px\"></a></p><p> </p><p>事实上,我们访问 控制器的时候,当需要某个 Service 的时候,Feign 帮我们向 Service 又发了一个同样的请求。我们可以直接访问这个 Service 请求</p><p>但是,通常我们会在生产环境中,不让别人直接访问到 Service,可以通过一些机制实现,比如内网啦。</p><p> </p><h2>六、学习过程中遇到的问题</h2><p>1、cloud-web 中注入 userService,一直注入失败,无法扫描到 userServiceImpl</p><p>解决方案:在 cloud-web 启动类上添加</p><p>@EnableFeignClients(basePackages = \"com.liuyanzhao.cloud\")</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud22.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8641 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud22-1024x288.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud22-1024x288.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"566\" height=\"159\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud22-1024x288.png 1024w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud22-300x84.png 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud22-768x216.png 768w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud22.png 1572w\" sizes=\"(max-width: 566px) 100vw, 566px\"></a></p><p> </p><p>2、出现 404 错误</p><p>cloud-core 里的 userServiceImpl 也需要加 @RequestMapping,并且需要将 @Service 改成 @RestController</p><p> </p><p>UserService(cloud-api)</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/user23.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8643 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/user23-1024x605.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/user23-1024x605.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"615\" height=\"363\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/user23-1024x605.png 1024w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/user23-300x177.png 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/user23-768x454.png 768w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/user23.png 1334w\" sizes=\"(max-width: 615px) 100vw, 615px\"></a></p><p> </p><p>UserServiceImpl(cloud-core)</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/user22.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8642 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/user22-1024x457.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/user22-1024x457.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"623\" height=\"278\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/user22-1024x457.png 1024w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/user22-300x134.png 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/user22-768x343.png 768w\" sizes=\"(max-width: 623px) 100vw, 623px\"></a></p><p> </p>', '', '', '', '', '', '', '2019-04-25 21:43:14', '2018-11-25 20:56:50', '在没有接触微服务或者分布式项目之前,我们的项目时没有分层的,我们的服务也是没有分层的。比如,我们有5台服务器使用 Nginx 做负载均衡,上面都是安装了我们的 Tomcat 服务器,部署的一样的项目。当用户访问某个请求的时候,按照负载均衡的机制(随机、轮询之类)来转发到对应的服务器上,然后在该服务器');
INSERT INTO `article` VALUES ('', '', '是是是搜索', '是是', '', '', '', '', '', '', '2018-11-25 23:56:08', '2018-11-25 16:47:05', null);
INSERT INTO `article` VALUES ('', '', 'sssss', 'sssss', '', '', '', '', '', '', '2018-11-25 17:28:38', '2018-11-25 17:19:09', null);
INSERT INTO `article` VALUES ('', '', 'sssssssss', 'ssssssss', '', '', '', '', '', '', '2018-11-25 17:25:31', '2018-11-25 17:22:10', null);
INSERT INTO `article` VALUES ('', '', 'SpringBoot 整合 elasticsearch 实例', '<p>上一篇文章介绍了安装 <a href=\"https://liuyanzhao.com/7152.html\" target=\"_blank\" rel=\"noopener noreferrer\">mac 上安装 elasticsearch</a></p><p>本文介绍 <a href=\"https://liuyanzhao.com/tag/springboot/\" title=\"查看与 SpringBoot 相关的文章\" target=\"_blank\">SpringBoot</a> 集成 elasticsearch</p><p> </p><h2>一、下载 并启动 elasticsearch</h2><p>下载地址:<a href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=https://www.elastic.co/downloads/past-releases\" target=\"_blank\" rel=\"noopener noreferrer\">https://www.elastic.co/downloads/past-releases</a></p><p>选择一个版本,下载</p><p>博主这里测试使用的是 2.4.4</p><p>下载方式可以选择 ZIP 包</p><p> </p><p>启动的话,windows 和 mac 有些细微区别</p><p>windows :进入文件目录下的 bin,然后点击 elasticsearch.bat 即可</p><p>mac:在终端执行命令 bin/elasticsearch</p><p> </p><p> </p><h2>二、配置 Maven</h2><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-xml\" start=\"1\"><li class=\"alt\"><span class=\"comments\"><!-- Spring Boot Elasticsearch 依赖 --></span></li><li class=\"\"><span class=\"tag\"><</span><span class=\"tag-name\">dependency</span><span class=\"tag\">></span></li><li class=\"alt\"> <span class=\"tag\"><</span><span class=\"tag-name\">groupId</span><span class=\"tag\">></span>org.springframework.boot<span class=\"tag\"></</span><span class=\"tag-name\">groupId</span><span class=\"tag\">></span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">artifactId</span><span class=\"tag\">></span>spring-boot-starter-data-elasticsearch<span class=\"tag\"></</span><span class=\"tag-name\">artifactId</span><span class=\"tag\">></span></li><li class=\"alt\"><span class=\"tag\"></</span><span class=\"tag-name\">dependency</span><span class=\"tag\">></span></li></ol></div><p> </p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-xml\" start=\"1\"><li class=\"alt\"><span class=\"tag\"><</span><span class=\"tag-name\">properties</span><span class=\"tag\">></span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">elasticsearch.version</span><span class=\"tag\">></span>2.4.4<span class=\"tag\"></</span><span class=\"tag-name\">elasticsearch.version</span><span class=\"tag\">></span></li><li class=\"alt\"><span class=\"tag\"></</span><span class=\"tag-name\">properties</span><span class=\"tag\">></span></li></ol></div><p> </p><p> </p><p>添加 spring-boot-starter-data-elasticsearch 依赖,并设置 elasticsearch 版本为 2.4.4</p><p> </p><p>ES 和 SpirngBoot 版本参考:</p><p><a href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=https://github.com/spring-projects/spring-data-elasticsearch/wiki/Spring-Data-Elasticsearch---Spring-Boot---version-matrix\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/spring-projects/spring-data-elasticsearch/wiki/Spring-Data-Elasticsearch---Spring-Boot---version-matrix</a></p><p> </p><h2>三、修改 application.properties</h2><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-xml\" start=\"1\"><li class=\"alt\">#Project</li><li class=\"\"><span class=\"attribute\">server.port</span>=<span class=\"attribute-value\">8080</span></li><li class=\"alt\"><span class=\"attribute\">debug</span>=<span class=\"attribute-value\">true</span></li><li class=\"\"><span class=\"attribute\">server.context-path</span>=/chuyun</li><li class=\"alt\"></li><li class=\"\"></li><li class=\"alt\"># DataSource</li><li class=\"\"><span class=\"attribute\">spring.datasource.driver-class-name</span>=<span class=\"attribute-value\">com</span>.mysql.jdbc.Driver</li><li class=\"alt\"><span class=\"attribute\">spring.datasource.username</span>=<span class=\"attribute-value\">root</span></li><li class=\"\"><span class=\"attribute\">spring.datasource.password</span>=<span class=\"attribute-value\">123456</span></li><li class=\"alt\"><span class=\"attribute\">spring.datasource.url</span>=<span class=\"attribute-value\">jdbc</span>:mysql://localhost:3306/chuyun?<span class=\"attribute\">characterEncodeing</span>=<span class=\"attribute-value\">utf</span>-8&<span class=\"attribute\">useSSL</span>=<span class=\"attribute-value\">false</span></li><li class=\"\"></li><li class=\"alt\"># JPA</li><li class=\"\"><span class=\"attribute\">spring.jpa.show-sql</span>=<span class=\"attribute-value\">true</span></li><li class=\"alt\"><span class=\"attribute\">spring.jpa.hibernate.ddl-auto</span>=<span class=\"attribute-value\">update</span></li><li class=\"\"></li><li class=\"alt\">#Thymeleaf</li><li class=\"\"><span class=\"attribute\">spring.thymeleaf.encoding</span>=<span class=\"attribute-value\">UTF</span>-8</li><li class=\"alt\"><span class=\"attribute\">spring.thymeleaf.cache</span>=<span class=\"attribute-value\">false</span></li><li class=\"\"><span class=\"attribute\">spring.thymeleaf.cache-period</span>=<span class=\"attribute-value\">0</span></li><li class=\"alt\"><span class=\"attribute\">spring.template.cache</span>=<span class=\"attribute-value\">false</span></li><li class=\"\"><span class=\"attribute\">spring.thymeleaf.mode</span>=<span class=\"attribute-value\">HTML5</span></li><li class=\"alt\"><span class=\"attribute\">spring.thymeleaf.prefix</span>=<span class=\"attribute-value\">classpath</span>:templates/</li><li class=\"\"><span class=\"attribute\">spring.thymeleaf.suffix</span>=.html</li><li class=\"alt\"></li><li class=\"\"></li><li class=\"alt\">#Elasticsearch</li><li class=\"\"><span class=\"attribute\">spring.data.elasticsearch.cluster-nodes</span>=<span class=\"attribute-value\">localhost</span>:9300</li><li class=\"alt\"><span class=\"attribute\">spring.data.elasticsearch.properties.transport.tcp.connect_timeout</span>=<span class=\"attribute-value\">120s</span></li></ol></div><p>主要是添加最后面两条</p><p> </p><h2>四、创建 ES Bean 和 Repository</h2><p>Article.java</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">package</span> com.liuyanzhao.chuyun.domain.es;</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">import</span> org.springframework.data.elasticsearch.annotations.Document;</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">import</span> javax.persistence.Id;</li><li class=\"\"><span class=\"keyword\">import</span> java.util.Date;</li><li class=\"alt\"></li><li class=\"\"><span class=\"comment\">/**</span></li><li class=\"alt\"><span class=\"comment\"> * @author 言曌</span></li><li class=\"\"><span class=\"comment\"> * @date 2018/1/22 下午4:45</span></li><li class=\"alt\"><span class=\"comment\"> */</span></li><li class=\"\"></li><li class=\"alt\"><span class=\"annotation\">@Document</span>(indexName=<span class=\"string\">\"chuyun\"</span>,type=<span class=\"string\">\"article\"</span>,indexStoreType=<span class=\"string\">\"fs\"</span>,shards=<span class=\"number\">5</span>,replicas=<span class=\"number\">1</span>,refreshInterval=<span class=\"string\">\"-1\"</span>)</li><li class=\"\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> Article {</li><li class=\"alt\"></li><li class=\"\"> <span class=\"comment\">//文章ID,这里必须为 id</span></li><li class=\"alt\"> <span class=\"annotation\">@Id</span></li><li class=\"\"> <span class=\"keyword\">private</span> Long id;</li><li class=\"alt\"></li><li class=\"\"> <span class=\"comment\">//标题</span></li><li class=\"alt\"> <span class=\"keyword\">private</span> String title;</li><li class=\"\"></li><li class=\"alt\"> <span class=\"comment\">//内容</span></li><li class=\"\"> <span class=\"keyword\">private</span> String content;</li><li class=\"alt\"></li><li class=\"\"> <span class=\"comment\">//浏览量</span></li><li class=\"alt\"> <span class=\"keyword\">private</span> Integer viewCount;</li><li class=\"\"></li><li class=\"alt\"> <span class=\"comment\">//发布时间</span></li><li class=\"\"> <span class=\"keyword\">private</span> Date createTime;</li><li class=\"alt\"></li><li class=\"\"> <span class=\"comment\">//更新时间</span></li><li class=\"alt\"> <span class=\"keyword\">private</span> Date updateTime;</li><li class=\"\"></li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> Article() {</li><li class=\"alt\"></li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> Long getId() {</li><li class=\"alt\"> <span class=\"keyword\">return</span> id;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setId(Long id) {</li><li class=\"alt\"> <span class=\"keyword\">this</span>.id = id;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> String getTitle() {</li><li class=\"alt\"> <span class=\"keyword\">return</span> title;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setTitle(String title) {</li><li class=\"alt\"> <span class=\"keyword\">this</span>.title = title;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> String getContent() {</li><li class=\"alt\"> <span class=\"keyword\">return</span> content;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setContent(String content) {</li><li class=\"alt\"> <span class=\"keyword\">this</span>.content = content;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> Integer getViewCount() {</li><li class=\"alt\"> <span class=\"keyword\">return</span> viewCount;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setViewCount(Integer viewCount) {</li><li class=\"alt\"> <span class=\"keyword\">this</span>.viewCount = viewCount;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> Date getCreateTime() {</li><li class=\"alt\"> <span class=\"keyword\">return</span> createTime;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setCreateTime(Date createTime) {</li><li class=\"alt\"> <span class=\"keyword\">this</span>.createTime = createTime;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> Date getUpdateTime() {</li><li class=\"alt\"> <span class=\"keyword\">return</span> updateTime;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> setUpdateTime(Date updateTime) {</li><li class=\"alt\"> <span class=\"keyword\">this</span>.updateTime = updateTime;</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"></li><li class=\"alt\"> <span class=\"annotation\">@Override</span></li><li class=\"\"> <span class=\"keyword\">public</span> String toString() {</li><li class=\"alt\"> <span class=\"keyword\">return</span> <span class=\"string\">\"Article{\"</span> +</li><li class=\"\"> <span class=\"string\">\"id=\"</span> + id +</li><li class=\"alt\"> <span class=\"string\">\", title=\'\"</span> + title + <span class=\"string\">\'\\\'\'</span> +</li><li class=\"\"> <span class=\"string\">\", content=\'\"</span> + content + <span class=\"string\">\'\\\'\'</span> +</li><li class=\"alt\"> <span class=\"string\">\", viewCount=\"</span> + viewCount +</li><li class=\"\"> <span class=\"string\">\", createTime=\"</span> + createTime +</li><li class=\"alt\"> <span class=\"string\">\", updateTime=\"</span> + updateTime +</li><li class=\"\"> \'}\';</li><li class=\"alt\"> }</li><li class=\"\">}</li></ol></div><p> </p><p>ArticleRepository.java</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">package</span> com.liuyanzhao.chuyun.repository.es;</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">import</span> com.liuyanzhao.chuyun.domain.es.Article;</li><li class=\"\"><span class=\"keyword\">import</span> org.springframework.data.domain.Page;</li><li class=\"alt\"><span class=\"keyword\">import</span> org.springframework.data.domain.Pageable;</li><li class=\"\"><span class=\"keyword\">import</span> org.springframework.data.elasticsearch.repository.ElasticsearchRepository;</li><li class=\"alt\"></li><li class=\"\"><span class=\"comment\">/**</span></li><li class=\"alt\"><span class=\"comment\"> * @author 言曌</span></li><li class=\"\"><span class=\"comment\"> * @date 2018/1/22 下午5:05</span></li><li class=\"alt\"><span class=\"comment\"> */</span></li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">public</span> <span class=\"keyword\">interface</span> ArticleRepository <span class=\"keyword\">extends</span> ElasticsearchRepository<Article, Long> {</li><li class=\"\"></li><li class=\"alt\"> Page<Article> findDistinctByTitleContainingOrContentContaining(String title, String content, Pageable pageable);</li><li class=\"\">}</li></ol></div><p> </p><p> </p><h2>五、创建测试类</h2><p>ArticleRepositoryTest.java</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">package</span> com.liuyanzhao.chuyun.repository.es;</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">import</span> com.liuyanzhao.chuyun.domain.es.Article;</li><li class=\"\"><span class=\"keyword\">import</span> com.liuyanzhao.chuyun.entity.User;</li><li class=\"alt\"><span class=\"keyword\">import</span> org.junit.Before;</li><li class=\"\"><span class=\"keyword\">import</span> org.junit.Test;</li><li class=\"alt\"><span class=\"keyword\">import</span> org.junit.runner.RunWith;</li><li class=\"\"><span class=\"keyword\">import</span> org.springframework.beans.factory.annotation.Autowired;</li><li class=\"alt\"><span class=\"keyword\">import</span> org.springframework.boot.test.context.<a href=\"https://liuyanzhao.com/tag/springboot/\" title=\"查看与 SpringBoot 相关的文章\" target=\"_blank\">SpringBoot</a>Test;</li><li class=\"\"><span class=\"keyword\">import</span> org.springframework.data.domain.Page;</li><li class=\"alt\"><span class=\"keyword\">import</span> org.springframework.data.domain.PageRequest;</li><li class=\"\"><span class=\"keyword\">import</span> org.springframework.data.domain.Pageable;</li><li class=\"alt\"><span class=\"keyword\">import</span> org.springframework.test.context.junit4.SpringRunner;</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">import</span> java.util.Date;</li><li class=\"\"></li><li class=\"alt\"><span class=\"comment\">/**</span></li><li class=\"\"><span class=\"comment\"> * @author 言曌</span></li><li class=\"alt\"><span class=\"comment\"> * @date 2018/1/21 下午5:03</span></li><li class=\"\"><span class=\"comment\"> */</span></li><li class=\"alt\"></li><li class=\"\"></li><li class=\"alt\"><span class=\"annotation\">@RunWith</span>(SpringRunner.<span class=\"keyword\">class</span>)</li><li class=\"\"><span class=\"annotation\">@<a href=\"https://liuyanzhao.com/tag/springboot/\" title=\"查看与 SpringBoot 相关的文章\" target=\"_blank\">SpringBoot</a>Test</span></li><li class=\"alt\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> ArticleRepositoryTest {</li><li class=\"\"></li><li class=\"alt\"> <span class=\"annotation\">@Autowired</span></li><li class=\"\"> <span class=\"keyword\">private</span> ArticleRepository articleRepository;</li><li class=\"alt\"></li><li class=\"\"> <span class=\"annotation\">@Before</span></li><li class=\"alt\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> initRepositoryData() {</li><li class=\"\"> <span class=\"comment\">//清除所有数据</span></li><li class=\"alt\"> articleRepository.deleteAll();</li><li class=\"\"> Article article = <span class=\"keyword\">new</span> Article();</li><li class=\"alt\"> article.setId((<span class=\"keyword\">long</span>) <span class=\"number\">1</span>);</li><li class=\"\"> article.setTitle(<span class=\"string\">\"《蝶恋花》\"</span>);</li><li class=\"alt\"> article.setContent(<span class=\"string\">\"槛菊愁烟兰泣露,罗幕轻寒,燕子双飞去。明月不谙离恨苦,斜光到晓穿朱户。昨夜西风凋碧树,独上高楼,望尽天涯路。欲寄彩笺兼尺素,山长水阔知何处?\"</span>);</li><li class=\"\"> article.setCreateTime(<span class=\"keyword\">new</span> Date());</li><li class=\"alt\"> article.setUpdateTime(<span class=\"keyword\">new</span> Date());</li><li class=\"\"> article.setViewCount(<span class=\"number\">678</span>);</li><li class=\"alt\"> articleRepository.save(article);</li><li class=\"\"></li><li class=\"alt\"> Article article2 = <span class=\"keyword\">new</span> Article();</li><li class=\"\"> article2.setId((<span class=\"keyword\">long</span>) <span class=\"number\">2</span>);</li><li class=\"alt\"> article2.setTitle(<span class=\"string\">\"《蝶恋花》\"</span>);</li><li class=\"\"> article2.setContent(<span class=\"string\">\"伫倚危楼风细细,望极春愁,黯黯生天际。草色烟光残照里,无言谁会凭阑意。拟把疏狂图一醉,对酒当歌,强乐还无味。衣带渐宽终不悔,为伊消得人憔悴。\"</span>);</li><li class=\"alt\"> article2.setCreateTime(<span class=\"keyword\">new</span> Date());</li><li class=\"\"> article2.setUpdateTime(<span class=\"keyword\">new</span> Date());</li><li class=\"alt\"> article.setViewCount(<span class=\"number\">367</span>);</li><li class=\"\"> articleRepository.save(article2);</li><li class=\"alt\"></li><li class=\"\"> Article article3 = <span class=\"keyword\">new</span> Article();</li><li class=\"alt\"> article3.setId((<span class=\"keyword\">long</span>) <span class=\"number\">3</span>);</li><li class=\"\"> article3.setTitle(<span class=\"string\">\"《青玉案·元夕》\"</span>);</li><li class=\"alt\"> article3.setContent(<span class=\"string\">\"东风夜放花千树,更吹落,星如雨。宝马雕车香满路。凤箫声动,玉壶光转,一夜鱼龙舞。蛾儿雪柳黄金缕,笑语盈盈暗香去。众里寻他千百度,蓦然回首,那人却在,灯火阑珊处。\"</span>);</li><li class=\"\"> article3.setCreateTime(<span class=\"keyword\">new</span> Date());</li><li class=\"alt\"> article3.setUpdateTime(<span class=\"keyword\">new</span> Date());</li><li class=\"\"> article3.setViewCount(<span class=\"number\">786</span>);</li><li class=\"alt\"> articleRepository.save(article3);</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"annotation\">@Test</span></li><li class=\"alt\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> findDistinctByTitleContainingOrContentContainingTest() <span class=\"keyword\">throws</span> Exception {</li><li class=\"\"> Pageable pageable = <span class=\"keyword\">new</span> PageRequest(<span class=\"number\">0</span>,<span class=\"number\">20</span>);</li><li class=\"alt\"> String title = <span class=\"string\">\"我爱罗琪\"</span>;</li><li class=\"\"> String content = <span class=\"string\">\"花千树\"</span>;</li><li class=\"alt\"> Page<Article> page = articleRepository.findDistinctByTitleContainingOrContentContaining(title, content, pageable);</li><li class=\"\"> System.out.println(page);</li><li class=\"alt\"> System.out.println(<span class=\"string\">\"---start---\"</span>);</li><li class=\"\"> <span class=\"keyword\">for</span>(Article article : page.getContent()) {</li><li class=\"alt\"> System.out.println(article.toString());</li><li class=\"\"> }</li><li class=\"alt\"> System.out.println(<span class=\"string\">\"---end---\"</span>);</li><li class=\"\"></li><li class=\"alt\"> }</li><li class=\"\"></li><li class=\"alt\">}</li></ol></div><p> </p><p>运行 @Test 注解的方法</p><p>根据 title 和 content 内容查到一条数据</p><p><img class=\"wp-image-7176 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/01/es9-1024x243.jpg\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/01/es9-1024x243.jpg\" alt=\"SpringBoot 整合 elasticsearch 实例\" width=\"652\" height=\"155\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/01/es9-1024x243.jpg 1024w, https://media.liuyanzhao.com/wp-content/uploads/2018/01/es9-300x71.jpg 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/01/es9-768x182.jpg 768w\" sizes=\"(max-width: 652px) 100vw, 652px\"></p><p> </p><p>修改 title 和 content</p><pre>String title = \"蝶恋\";\r\nString content = \"东风\";</pre><p>查到三条数据</p><p><img class=\"wp-image-7177 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/01/es10-1024x135.jpg\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/01/es10-1024x135.jpg\" alt=\"SpringBoot 整合 elasticsearch 实例\" width=\"654\" height=\"86\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/01/es10-1024x135.jpg 1024w, https://media.liuyanzhao.com/wp-content/uploads/2018/01/es10-300x40.jpg 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/01/es10-768x102.jpg 768w\" sizes=\"(max-width: 654px) 100vw, 654px\"></p><p> </p><h2>六、访问 http://localhost:9200/_plugin/head/</h2><p>因为上一篇文章中,我们讲了装一个 head 插件,现在我们就能看到里面的数据了(多余的数据是之前测试的)</p><p><img class=\"wp-image-7182 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/01/es12-1024x363.jpg\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/01/es12-1024x363.jpg\" alt=\"SpringBoot 整合 elasticsearch 实例\" width=\"667\" height=\"236\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/01/es12-1024x363.jpg 1024w, https://media.liuyanzhao.com/wp-content/uploads/2018/01/es12-300x106.jpg 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/01/es12-768x272.jpg 768w\" sizes=\"(max-width: 667px) 100vw, 667px\"></p><h2></h2><h2>七、新建 Controller 类</h2><p>ArticleController.java</p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">package</span> com.liuyanzhao.chuyun.controller;</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">import</span> com.liuyanzhao.chuyun.domain.es.Article;</li><li class=\"\"><span class=\"keyword\">import</span> com.liuyanzhao.chuyun.repository.es.ArticleRepository;</li><li class=\"alt\"><span class=\"keyword\">import</span> org.springframework.beans.factory.annotation.Autowired;</li><li class=\"\"><span class=\"keyword\">import</span> org.springframework.data.domain.Page;</li><li class=\"alt\"><span class=\"keyword\">import</span> org.springframework.data.domain.PageRequest;</li><li class=\"\"><span class=\"keyword\">import</span> org.springframework.data.domain.Pageable;</li><li class=\"alt\"><span class=\"keyword\">import</span> org.springframework.web.bind.annotation.RequestMapping;</li><li class=\"\"><span class=\"keyword\">import</span> org.springframework.web.bind.annotation.RequestParam;</li><li class=\"alt\"><span class=\"keyword\">import</span> org.springframework.web.bind.annotation.RestController;</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">import</span> java.util.List;</li><li class=\"\"></li><li class=\"alt\"><span class=\"comment\">/**</span></li><li class=\"\"><span class=\"comment\"> * @author 言曌</span></li><li class=\"alt\"><span class=\"comment\"> * @date 2018/1/22 下午9:07</span></li><li class=\"\"><span class=\"comment\"> */</span></li><li class=\"alt\"></li><li class=\"\"></li><li class=\"alt\"><span class=\"annotation\">@RestController</span></li><li class=\"\"><span class=\"annotation\">@RequestMapping</span>(<span class=\"string\">\"/article\"</span>)</li><li class=\"alt\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> ArticleController {</li><li class=\"\"></li><li class=\"alt\"> <span class=\"annotation\">@Autowired</span></li><li class=\"\"> <span class=\"keyword\">private</span> ArticleRepository ArticleRepository;</li><li class=\"alt\"></li><li class=\"\"> <span class=\"annotation\">@RequestMapping</span>(<span class=\"string\">\"\"</span>)</li><li class=\"alt\"> <span class=\"keyword\">public</span> List<Article> list(<span class=\"annotation\">@RequestParam</span>(value = <span class=\"string\">\"title\"</span>, required = <span class=\"keyword\">false</span>) String title,</li><li class=\"\"> <span class=\"annotation\">@RequestParam</span>(value = <span class=\"string\">\"content\"</span>, required = <span class=\"keyword\">false</span>) String content,</li><li class=\"alt\"> <span class=\"annotation\">@RequestParam</span>(value = <span class=\"string\">\"pageIndex\"</span>, defaultValue = <span class=\"string\">\"0\"</span>) <span class=\"keyword\">int</span> pageIndex,</li><li class=\"\"> <span class=\"annotation\">@RequestParam</span>(value = <span class=\"string\">\"pageSize\"</span>, defaultValue = <span class=\"string\">\"10\"</span>) <span class=\"keyword\">int</span> pageSize) {</li><li class=\"alt\"> Pageable pageable = <span class=\"keyword\">new</span> PageRequest(pageIndex, pageSize);</li><li class=\"\"> Page<Article> page = ArticleRepository.findDistinctByTitleContainingOrContentContaining(title, content, pageable);</li><li class=\"alt\"> <span class=\"keyword\">return</span> page.getContent();</li><li class=\"\"> }</li><li class=\"alt\">}</li></ol></div><p> </p><p>因为之前在 测试类 里已经给 elasticsearch 添加了数据</p><p>所有现在可以在浏览器上访问:</p><p>http://localhost:8080/chuyun/article?title=浪淘沙&content=伊人</p><p><img class=\"wp-image-7181 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/01/es11-1024x372.jpg\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/01/es11-1024x372.jpg\" alt=\"SpringBoot 整合 elasticsearch 实例\" width=\"646\" height=\"235\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/01/es11-1024x372.jpg 1024w, https://media.liuyanzhao.com/wp-content/uploads/2018/01/es11-300x109.jpg 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/01/es11-768x279.jpg 768w\" sizes=\"(max-width: 646px) 100vw, 646px\"></p><p> </p><p> </p><p> </p><p>本文地址:<a href=\"https://liuyanzhao.com/7170.html\" target=\"_blank\" rel=\"noopener noreferrer\">https://liuyanzhao.com/7170.html</a></p>', '', '', '', '', '', '', '2018-11-25 20:42:58', '2018-11-25 20:42:58', null);
INSERT INTO `article` VALUES ('', '', 'SpringCloud 中使用 Eureka 和 Feign 实现增删改查', '<p>在没有接触微服务或者分布式项目之前,我们的项目时没有分层的,我们的服务也是没有分层的。</p><p>比如,我们有5台服务器使用 Nginx 做负载均衡,上面都是安装了我们的 Tomcat 服务器,部署的一样的项目。当用户访问某个请求的时候,按照负载均衡的机制(随机、轮询之类)来转发到对应的服务器上,然后在该服务器上先是到 Controller,然后 Service,再到 DAO,然后返回给 Service,返回给 Controller,最后返回给前台。似乎这样也行。</p><p>但是当我们网站访问量不断飙升的时候,似乎有点撑不住了,只能加服务器,成本也明显增加。我们能不能再分一分呢 ?</p><p>同时,我们发现真正比较耗费时间的是一些 IO 操作,比如数据库操作、上传文件之类的。为了方便开发,我们可以把 DAO 和 Service 层放在一起,暂且称为业务层。业务层耗费的远远大于 Controller 层的。我们可以在 5 台服务器中拿 1 台部署 Controller 层,另外 4 台部署业务层代码。</p><p>如果将他们连接起来呢?</p><p>Eureka + Feign!</p><p>Eureka 做注册中心,业务层和控制器层每一个实例可以作为一个服务,比如 UserService 是一个服务,UserController 也可以是一个服务,一个服务可以有多个状态(可以有多台机器注册到该服务)。</p><p>下面通过一个例子来讲解</p><p> </p><p>源码地址:<a href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=https://github.com/saysky/spring-cloud-demo\">https://github.com/saysky/spring-cloud-demo</a></p><p> </p><h2>一、下载源码</h2><p>请上 Github 上下载本例子源码</p><p>源码地址:<a href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=https://github.com/saysky/spring-cloud-demo\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/saysky/spring-cloud-demo</a></p><p> </p><h2>二、项目结构</h2><p>注意这是三个项目(不是一个项目),用 IDEA 打开分别打开三个项目,最终如图</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/springcloud40.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8631 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/springcloud40-1024x385.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/springcloud40-1024x385.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"595\" height=\"224\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/springcloud40-1024x385.png 1024w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/springcloud40-300x113.png 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/springcloud40-768x289.png 768w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/springcloud40-530x200.png 530w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/springcloud40.png 1334w\" sizes=\"(max-width: 595px) 100vw, 595px\"></a></p><p> </p><p>1、eureka-server(一个 SpringBoot 项目)</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/eureka-server.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8633 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/eureka-server.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/eureka-server.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"420\" height=\"262\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/eureka-server.png 772w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/eureka-server-300x187.png 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/eureka-server-768x480.png 768w\" sizes=\"(max-width: 420px) 100vw, 420px\"></a></p><p> </p><p> </p><p>2、cloud (一个多模块项目,API + 业务层) <a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud2.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8632 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud2.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud2.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"418\" height=\"121\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud2.png 574w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud2-300x87.png 300w\" sizes=\"(max-width: 418px) 100vw, 418px\"></a></p><p> </p><p> </p><p>(1)其中 cloud-api 是 api,里面有 model 和 service接口,cloud-core 和 cloud-web 将依赖它</p><p>到时候需要首先将该模块 install,即打包到本地仓库中</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/api.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8634 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/api.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/api.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"463\" height=\"414\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/api.png 672w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/api-300x269.png 300w\" sizes=\"(max-width: 463px) 100vw, 463px\"></a></p><p> </p><p>(2)其中 cloud-core,里面 dao 和 serviceImpl,作为生产者的存在</p><p>启动类在这里面</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/core.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8635 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/core.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/core.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"439\" height=\"460\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/core.png 800w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/core-286x300.png 286w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/core-768x804.png 768w\" sizes=\"(max-width: 439px) 100vw, 439px\"></a></p><p> </p><p>3、cloud-web( 一个 SpringBoot 项目)</p><p>只有控制器层,需要依赖 cloud-api ,作为消费者的存在(可以是其他人调用)</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-web.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8636 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-web.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-web.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"439\" height=\"359\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-web.png 746w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-web-300x245.png 300w\" sizes=\"(max-width: 439px) 100vw, 439px\"></a></p><p> </p><h2>三、如何启动</h2><p>1、新建数据库 summer,导入 user.sql</p><p>2、导入新项目,先后顺序随便(如eureka-server、cloud、cloud-web)</p><p>3、启动 eureka-server 项目,端口为 8761</p><p>4、对 cloud 项目中的 cloud-api 模块进行install,maven命令是 mvn clean install</p><p>5、启动 cloud-core 项目,端口为 8090</p><p>6、(可选)其实这时候我们可以在地址栏访问一下 localhost:8090/user 其实是可以访问的,如果你不行,说明有问题</p><p>7、启动 cloud-web 项目,端口为 8080</p><p>8、在地址栏访问 localhost:8080/user,如果可以看到数据,说明成功</p><p> </p><h2>四、补充</h2><p>1、我这里模拟了四台注册了业务层服务(cloud-producer) 和 一台控制器层服务(cloud-consumer)</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/server.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8637 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/server-1024x655.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/server-1024x655.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"644\" height=\"412\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/server-1024x655.png 1024w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/server-300x192.png 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/server-768x491.png 768w\" sizes=\"(max-width: 644px) 100vw, 644px\"></a></p><p>ps:因为我是用本机测试,所以开了不同的端口表示不同的服务器</p><p> </p><p>2、访问控制器层,然后 Feign 会帮我们在注册中心中找到对应的服务名和对应的方法</p><p>我们访问 localhost:8080/user/1 首先到的是控制器层,控制器层调用 userService,userService 是 Cloud-API 里的,添加了 @FeignClient(name=\"cloud-producer\") 注解,然后找到在注册中心中寻找 cloud-producer 的服务,从一台(负载均衡)服务器中找到 userService 的实现类,即 userServiceImpl,也就是进入了 cloud-core,然后完成一系列 DAO 和 Service 操作,最后返回给 Controller。</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/8080.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8638 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/8080-1024x680.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/8080-1024x680.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"625\" height=\"415\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/8080-1024x680.png 1024w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/8080-300x199.png 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/8080-768x510.png 768w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/8080.png 1148w\" sizes=\"(max-width: 625px) 100vw, 625px\"></a></p><p>其中 Service 和 Service 的实现都需要加 RequestMapping,Service 实现需要加 @RestController,否则会出现 404</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8639 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api-1024x373.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api-1024x373.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"627\" height=\"228\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api-1024x373.png 1024w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api-300x109.png 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api-768x280.png 768w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api.png 1538w\" sizes=\"(max-width: 627px) 100vw, 627px\"></a></p><p>所以,其实我们可以直接访问 cloud-core 这一层,在地址栏访问 localhost:8090/user/1</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api2.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8640 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api2.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api2.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"497\" height=\"383\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api2.png 784w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api2-300x231.png 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud-api2-768x592.png 768w\" sizes=\"(max-width: 497px) 100vw, 497px\"></a></p><p> </p><p>事实上,我们访问 控制器的时候,当需要某个 Service 的时候,Feign 帮我们向 Service 又发了一个同样的请求。我们可以直接访问这个 Service 请求</p><p>但是,通常我们会在生产环境中,不让别人直接访问到 Service,可以通过一些机制实现,比如内网啦。</p><p> </p><h2>六、学习过程中遇到的问题</h2><p>1、cloud-web 中注入 userService,一直注入失败,无法扫描到 userServiceImpl</p><p>解决方案:在 cloud-web 启动类上添加</p><p>@EnableFeignClients(basePackages = \"com.liuyanzhao.cloud\")</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud22.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8641 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud22-1024x288.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud22-1024x288.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"566\" height=\"159\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud22-1024x288.png 1024w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud22-300x84.png 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud22-768x216.png 768w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/cloud22.png 1572w\" sizes=\"(max-width: 566px) 100vw, 566px\"></a></p><p> </p><p>2、出现 404 错误</p><p>cloud-core 里的 userServiceImpl 也需要加 @RequestMapping,并且需要将 @Service 改成 @RestController</p><p> </p><p>UserService(cloud-api)</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/user23.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8643 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/user23-1024x605.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/user23-1024x605.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"615\" height=\"363\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/user23-1024x605.png 1024w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/user23-300x177.png 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/user23-768x454.png 768w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/user23.png 1334w\" sizes=\"(max-width: 615px) 100vw, 615px\"></a></p><p> </p><p>UserServiceImpl(cloud-core)</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/user22.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8642 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/user22-1024x457.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/user22-1024x457.png\" alt=\"SpringCloud 中使用 Eureka 和 Feign 实现增删改查\" width=\"623\" height=\"278\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/07/user22-1024x457.png 1024w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/user22-300x134.png 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/07/user22-768x343.png 768w\" sizes=\"(max-width: 623px) 100vw, 623px\"></a></p><p> </p>', '', '', '', '', '', '', '2019-04-25 21:43:11', '2018-11-25 20:58:22', '在没有接触微服务或者分布式项目之前,我们的项目时没有分层的,我们的服务也是没有分层的。比如,我们有5台服务器使用 Nginx 做负载均衡,上面都是安装了我们的 Tomcat 服务器,部署的一样的项目。当用户访问某个请求的时候,按照负载均衡的机制(随机、轮询之类)来转发到对应的服务器上,然后在该服务器');
INSERT INTO `article` VALUES ('', '', 'IDEA启动EDAS项目', '<h2>一、下载EDAS配置中心</h2><p>这个有点像我们SpringCloud里的Eurake注册中心</p><p>下载地址:<a href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=https://edas-public.oss-cn-hangzhou.aliyuncs.com/install_package/LCC/2018-11-01/edas-lite-configcenter.tar.gz?spm=a2c4g.11186623.2.14.1c1d4423eoGWQQ&file=edas-lite-configcenter.tar.gz\" target=\"_blank\" rel=\"noopener noreferrer\">点此</a></p><p>官网地址:<a href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=https://help.aliyun.com/document_detail/44163.html?spm=a2c4g.11186623.6.675.71d822b8fWyaNi\" target=\"_blank\" rel=\"noopener noreferrer\">点此</a></p><p>启动 edas配置中心:windows下点击 edas-lite-configcenter/bin 中的 startup.bat</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/11/edas-startup.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8903 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/11/edas-startup.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/11/edas-startup.png\" alt=\"IDEA启动EDAS项目\" width=\"333\" height=\"90\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/11/edas-startup.png 551w, https://media.liuyanzhao.com/wp-content/uploads/2018/11/edas-startup-300x81.png 300w\" sizes=\"(max-width: 333px) 100vw, 333px\"></a></p><p>默认启动在 8080 端口</p><p> </p><h2>二、安装 Ali-Tomcat 和 Pandora</h2><p>1、下载 <a title=\"Ali-Tomcat\" href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=http://edas-public.oss-cn-hangzhou.aliyuncs.com/install_package/tomcat/taobao-tomcat-7.0.59.tgz\">Ali-Tomcat</a>,保存后解压至相应的目录(如:d:\\work\\tomcat\\)。</p><p> </p><p>2、下载 <a title=\"Pandora 容器\" href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=http://edas-public.oss-cn-hangzhou.aliyuncs.com/install_package/pandora/unauth/taobao-hsf.tgz\">Pandora 容器</a>,保存后将内容解压至上述保存的 Ali-Tomcat 的 deploy 目录(d:\\work\\tomcat\\deploy)下。</p><p>下载后是两个文件夹</p><p><img class=\"size-full wp-image-8902 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/11/QQ截图20181106100740.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/11/QQ%E6%88%AA%E5%9B%BE20181106100740.png\" alt=\"IDEA启动EDAS项目\" width=\"229\" height=\"60\"></p><p> </p><p> </p><p>解压后,将 taobao-haf 放到 taobao-tomcat-7.0.59/deploy 里面</p><p> </p><p>比如我们有三个服务,分别是A、B、C</p><p>我们建立三个文件夹,分别是A、B 和 C,每个文件夹里都拷贝一份上面的 taobao-tomcat-7.0.59</p><p> </p><h2>三、IDEA 配置 Tomcat</h2><p>1、创建三个Tomcat</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/11/tomcat-server.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8905 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/11/tomcat-server.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/11/tomcat-server.png\" alt=\"IDEA启动EDAS项目\" width=\"651\" height=\"473\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/11/tomcat-server.png 824w, https://media.liuyanzhao.com/wp-content/uploads/2018/11/tomcat-server-300x218.png 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/11/tomcat-server-768x557.png 768w\" sizes=\"(max-width: 651px) 100vw, 651px\"></a></p><p>指定启动的端口,VM options里填写对应的 Tomcat 的启动参数</p><p>D:\\EDAS\\A\\taobao-tomcat-7.0.59\\deploy\\taobao-hsf.sar</p><p> </p><p>2、在Deployment里选择对应的项目</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/11/tomcat-server2.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8906 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/11/tomcat-server2.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/11/tomcat-server2.png\" alt=\"IDEA启动EDAS项目\" width=\"641\" height=\"481\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/11/tomcat-server2.png 801w, https://media.liuyanzhao.com/wp-content/uploads/2018/11/tomcat-server2-300x225.png 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/11/tomcat-server2-768x576.png 768w, https://media.liuyanzhao.com/wp-content/uploads/2018/11/tomcat-server2-280x210.png 280w\" sizes=\"(max-width: 641px) 100vw, 641px\"></a></p><p> </p><p>然后依次启动几个项目就行了</p><p> </p><p>官方文档:<a href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=https://help.aliyun.com/product/29500.html?spm=a2c4g.11186623.6.540.102bb8e7ETwPNH\" target=\"_blank\" rel=\"noopener noreferrer\">https://help.aliyun.com/product/29500.html?spm=a2c4g.11186623.6.540.102bb8e7ETwPNH</a></p><p> </p>', '', '', '', '', '', '', '2019-04-25 21:43:08', '2018-11-25 21:00:24', '一、下载EDAS配置中心这个有点像我们SpringCloud里的Eurake注册中心下载地址:点此官网地址:点此启动 edas配置中心:windows下点击 edas-lite-configcenter/bin 中的 startup.bat默认启动在 8080 端口 二、安装 Ali-To');
INSERT INTO `article` VALUES ('', '', 'SpringBoot + mongodb 整合, 记录网站操作日志,常用查询操作', '<div class=\"single-content\"><p>mongodb 是一种文档型数据库。跟 Redis 一样是非关系型数据库,Redis 属于那种小而快的数据库,常常用作缓存。</p><p>而如果我们需要存一些类似于日志的那种,可以尝试用 mongodb (当然也有人用 MySQL,就是有点慢)。我们尝试用 mongodb 来存储博客的日志信息。</p><p>本文主要介绍 <a href=\"https://liuyanzhao.com/tag/springboot/\" title=\"查看与 SpringBoot 相关的文章\" target=\"_blank\">SpringBoot</a> 和 mongodb 整合,和基本的查询操作。</p><p> </p><h2>一、依赖和配置</h2><p>创建 springboot 项目,引入 web 和 lombok,然后再添加 mongodb 依赖</p><p><strong>1、pom.xml</strong></p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-xml\" start=\"1\"><li class=\"alt\"><span class=\"comments\"><!--mongodb--></span></li><li class=\"\"><span class=\"tag\"><</span><span class=\"tag-name\">dependency</span><span class=\"tag\">></span></li><li class=\"alt\"> <span class=\"tag\"><</span><span class=\"tag-name\">groupId</span><span class=\"tag\">></span>org.springframework.boot<span class=\"tag\"><!--</span--><span class=\"tag-name\">groupId</span><span class=\"tag\">></span></span></li><li class=\"\"> <span class=\"tag\"><</span><span class=\"tag-name\">artifactId</span><span class=\"tag\">></span>spring-boot-starter-data-mongodb<span class=\"tag\"><!--</span--><span class=\"tag-name\">artifactId</span><span class=\"tag\">></span></span></li><li class=\"alt\"><span class=\"tag\"><!--</span--><span class=\"tag-name\">dependency</span><span class=\"tag\">></span></span></li></ol></div><p>类似 Spring Data JPA</p><p> </p><p><strong>2、application.properties</strong></p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\">spring.data.mongodb.uri=mongodb:<span class=\"comment\">//localhost:27017/saysky</span></li></ol></div><p>saysky是数据库名称,确保你本地启动了 mongodb</p><p> </p><h2>二、代码实例</h2><p><strong>1、日志实体</strong></p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">package</span> com.liuyanzhao.mongodb.model;</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">import</span> lombok.Data;</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">import</span> java.util.Date;</li><li class=\"\"></li><li class=\"alt\"><span class=\"comment\">/**</span></li><li class=\"\"><span class=\"comment\"> * 日志</span></li><li class=\"alt\"><span class=\"comment\"> * @author 言曌</span></li><li class=\"\"><span class=\"comment\"> * @date 2018/9/3 20:00</span></li><li class=\"alt\"><span class=\"comment\"> */</span></li><li class=\"\"><span class=\"annotation\">@Data</span></li><li class=\"alt\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> Log {</li><li class=\"\"></li><li class=\"alt\"> <span class=\"keyword\">private</span> Long id;</li><li class=\"\"></li><li class=\"alt\"> <span class=\"keyword\">private</span> Long userId;</li><li class=\"\"></li><li class=\"alt\"> <span class=\"keyword\">private</span> Integer type;</li><li class=\"\"></li><li class=\"alt\"> <span class=\"keyword\">private</span> String url;</li><li class=\"\"></li><li class=\"alt\"> <span class=\"keyword\">private</span> String desc;</li><li class=\"\"></li><li class=\"alt\"> <span class=\"keyword\">private</span> Date createTime;</li><li class=\"\">}</li></ol></div><p> </p><p><strong>2、LogRepository</strong></p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">package</span> com.liuyanzhao.mongodb.dao;</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">import</span> com.liuyanzhao.mongodb.model.Log;</li><li class=\"\"><span class=\"keyword\">import</span> org.springframework.data.domain.Page;</li><li class=\"alt\"><span class=\"keyword\">import</span> org.springframework.data.domain.Pageable;</li><li class=\"\"><span class=\"keyword\">import</span> org.springframework.data.mongodb.repository.MongoRepository;</li><li class=\"alt\"><span class=\"keyword\">import</span> org.springframework.stereotype.Repository;</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">import</span> java.util.Date;</li><li class=\"\"><span class=\"keyword\">import</span> java.util.List;</li><li class=\"alt\"></li><li class=\"\"></li><li class=\"alt\"><span class=\"comment\">/**</span></li><li class=\"\"><span class=\"comment\"> * @author 言曌</span></li><li class=\"alt\"><span class=\"comment\"> * @date 2018/9/3 14:17</span></li><li class=\"\"><span class=\"comment\"> */</span></li><li class=\"alt\"><span class=\"annotation\">@Repository</span></li><li class=\"\"><span class=\"keyword\">public</span> <span class=\"keyword\">interface</span> LogRepository <span class=\"keyword\">extends</span> <log, long> {</log, long></li><li class=\"alt\"></li><li class=\"\"> <span class=\"comment\">/**</span></li><li class=\"alt\"><span class=\"comment\"> * 根据用户ID查询</span></li><li class=\"\"><span class=\"comment\"> * @param userId</span></li><li class=\"alt\"><span class=\"comment\"> * @return</span></li><li class=\"\"><span class=\"comment\"> */</span></li><li class=\"alt\"> List<log> findByUserId(Long userId);</log></li><li class=\"\"></li><li class=\"alt\"> <span class=\"comment\">/**</span></li><li class=\"\"><span class=\"comment\"> * 根据描述查询</span></li><li class=\"alt\"><span class=\"comment\"> * @param desc</span></li><li class=\"\"><span class=\"comment\"> * @return</span></li><li class=\"alt\"><span class=\"comment\"> */</span></li><li class=\"\"> List<log> findByDesc(String desc);</log></li><li class=\"alt\"></li><li class=\"\"> <span class=\"comment\">/**</span></li><li class=\"alt\"><span class=\"comment\"> * 根据创建日期范围查询</span></li><li class=\"\"><span class=\"comment\"> * @param startTime</span></li><li class=\"alt\"><span class=\"comment\"> * @param endTime</span></li><li class=\"\"><span class=\"comment\"> * @return</span></li><li class=\"alt\"><span class=\"comment\"> */</span></li><li class=\"\"> List<log> findByCreateTimeBetween(Date startTime, Date endTime);</log></li><li class=\"alt\"></li><li class=\"\"></li><li class=\"alt\"> <span class=\"comment\">/**</span></li><li class=\"\"><span class=\"comment\"> * 根据描述查询</span></li><li class=\"alt\"><span class=\"comment\"> * 分页查询</span></li><li class=\"\"><span class=\"comment\"> * @param desc</span></li><li class=\"alt\"><span class=\"comment\"> * @return</span></li><li class=\"\"><span class=\"comment\"> */</span></li><li class=\"alt\"> Page<log> findByDesc(String desc, Pageable pageable);</log></li><li class=\"\"></li><li class=\"alt\"> <span class=\"comment\">/**</span></li><li class=\"\"><span class=\"comment\"> * 根据创建日期范围查询</span></li><li class=\"alt\"><span class=\"comment\"> * 分页查询</span></li><li class=\"\"><span class=\"comment\"> * @param startTime</span></li><li class=\"alt\"><span class=\"comment\"> * @param endTime</span></li><li class=\"\"><span class=\"comment\"> * @return</span></li><li class=\"alt\"><span class=\"comment\"> */</span></li><li class=\"\"> Page<log> findByCreateTimeBetween(Date startTime, Date endTime,Pageable pageable);</log></li><li class=\"alt\"></li><li class=\"\">}</li></ol></div><p> </p><p><strong>3、测试类</strong></p><div class=\"dp-highlighter\"><div class=\"bar\"></div><ol class=\"dp-j\" start=\"1\"><li class=\"alt\"><span class=\"keyword\">package</span> com.liuyanzhao.mongodb.dao;</li><li class=\"\"></li><li class=\"alt\"><span class=\"keyword\">import</span> com.liuyanzhao.mongodb.model.Log;</li><li class=\"\"><span class=\"keyword\">import</span> org.junit.Test;</li><li class=\"alt\"><span class=\"keyword\">import</span> org.junit.runner.RunWith;</li><li class=\"\"><span class=\"keyword\">import</span> org.springframework.beans.factory.annotation.Autowired;</li><li class=\"alt\"><span class=\"keyword\">import</span> org.springframework.boot.test.context.<a href=\"https://liuyanzhao.com/tag/springboot/\" title=\"查看与 SpringBoot 相关的文章\" target=\"_blank\">SpringBoot</a>Test;</li><li class=\"\"><span class=\"keyword\">import</span> org.springframework.data.domain.Page;</li><li class=\"alt\"><span class=\"keyword\">import</span> org.springframework.data.domain.PageRequest;</li><li class=\"\"><span class=\"keyword\">import</span> org.springframework.test.context.junit4.SpringRunner;</li><li class=\"alt\"></li><li class=\"\"><span class=\"keyword\">import</span> java.util.Date;</li><li class=\"alt\"></li><li class=\"\"></li><li class=\"alt\"><span class=\"comment\">/**</span></li><li class=\"\"><span class=\"comment\"> * @author 言曌</span></li><li class=\"alt\"><span class=\"comment\"> * @date 2018/9/3 14:21</span></li><li class=\"\"><span class=\"comment\"> */</span></li><li class=\"alt\"><span class=\"annotation\">@<a href=\"https://liuyanzhao.com/tag/springboot/\" title=\"查看与 SpringBoot 相关的文章\" target=\"_blank\">SpringBoot</a>Test</span></li><li class=\"\"><span class=\"annotation\">@RunWith</span>(SpringRunner.<span class=\"keyword\">class</span>)</li><li class=\"alt\"><span class=\"keyword\">public</span> <span class=\"keyword\">class</span> LogRepositoryTest {</li><li class=\"\"></li><li class=\"alt\"> <span class=\"annotation\">@Autowired</span></li><li class=\"\"> <span class=\"keyword\">private</span> LogRepository logRepository;</li><li class=\"alt\"></li><li class=\"\"> <span class=\"annotation\">@Test</span></li><li class=\"alt\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> save() {</li><li class=\"\"> Log log = <span class=\"keyword\">new</span> Log();</li><li class=\"alt\"> log.setId(7L);</li><li class=\"\"> log.setType(<span class=\"number\">1</span>);</li><li class=\"alt\"> log.setDesc(<span class=\"string\">\"更新用户\"</span>);</li><li class=\"\"> log.setUserId(10002L);</li><li class=\"alt\"> log.setUrl(<span class=\"string\">\"/user/update\"</span>);</li><li class=\"\"> log.setCreateTime(<span class=\"keyword\">new</span> Date());</li><li class=\"alt\"> logRepository.save(log);</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"> <span class=\"annotation\">@Test</span></li><li class=\"alt\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> findById() {</li><li class=\"\"> Log Log = logRepository.findById(1L).get();</li><li class=\"alt\"> System.out.println(Log);</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"></li><li class=\"alt\"> <span class=\"annotation\">@Test</span></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> findByDes() {</li><li class=\"alt\"> <span class=\"comment\">//不分页</span></li><li class=\"\"><span class=\"comment\">// List<log> LogList = logRepository.findByDesc(\"添加用户\");</log></span></li><li class=\"alt\"><span class=\"comment\">// System.out.println(LogList);</span></li><li class=\"\"></li><li class=\"alt\"> <span class=\"comment\">//分页查询</span></li><li class=\"\"> <span class=\"comment\">//查询第1页,每页显示2条</span></li><li class=\"alt\"> PageRequest pageRequest = <span class=\"keyword\">new</span> PageRequest(<span class=\"number\">0</span>,<span class=\"number\">2</span>);</li><li class=\"\"> Page<log> logPage = logRepository.findByDesc(<span class=\"string\">\"添加用户\"</span>,pageRequest);</log></li><li class=\"alt\"> System.out.println(logPage);</li><li class=\"\"> }</li><li class=\"alt\"></li><li class=\"\"></li><li class=\"alt\"> <span class=\"annotation\">@Test</span></li><li class=\"\"> <span class=\"keyword\">public</span> <span class=\"keyword\">void</span> findByCreateTimeBetween() {</li><li class=\"alt\"> <span class=\"comment\">//根据时间区间查询</span></li><li class=\"\"> <span class=\"comment\">//不分页</span></li><li class=\"alt\"> Date createdAtStart = <span class=\"keyword\">new</span> Date(1535974057016L);</li><li class=\"\"> Date createdAtEnd = <span class=\"keyword\">new</span> Date(1535974145009L);</li><li class=\"alt\"><span class=\"comment\">// List<log> LogList = logRepository.findByCreateTimeBetween(createdAtStart,createdAtEnd);</log></span></li><li class=\"\"><span class=\"comment\">// System.out.println(LogList);</span></li><li class=\"alt\"></li><li class=\"\"> <span class=\"comment\">//分页查询</span></li><li class=\"alt\"> <span class=\"comment\">//查询第1页,每页显示2条</span></li><li class=\"\"> PageRequest pageRequest = <span class=\"keyword\">new</span> PageRequest(<span class=\"number\">0</span>,<span class=\"number\">2</span>);</li><li class=\"alt\"> Page<log> logPage = logRepository.findByCreateTimeBetween(createdAtStart,createdAtEnd,pageRequest);</log></li><li class=\"\"> System.out.println(logPage);</li><li class=\"alt\"> }</li><li class=\"\"></li><li class=\"alt\">}</li></ol></div><p> </p><p>我们通过mongodb可视化工具可以看到数据</p><p><a href=\"https://media.liuyanzhao.com/wp-content/uploads/2018/09/mongodb.png\" class=\"fancybox\" data-fancybox-group=\"button\"><img class=\"wp-image-8733 aligncenter\" data-original=\"https://media.liuyanzhao.com/wp-content/uploads/2018/09/mongodb.png\" src=\"https://media.liuyanzhao.com/wp-content/uploads/2018/09/mongodb.png\" alt=\"SpringBoot + mongodb 整合, 记录网站操作日志,常用查询操作\" width=\"618\" height=\"320\" srcset=\"https://media.liuyanzhao.com/wp-content/uploads/2018/09/mongodb.png 858w, https://media.liuyanzhao.com/wp-content/uploads/2018/09/mongodb-300x155.png 300w, https://media.liuyanzhao.com/wp-content/uploads/2018/09/mongodb-768x397.png 768w\" sizes=\"(max-width: 618px) 100vw, 618px\"></a></p><p> </p><h2>三、更多查询</h2><p>因为引入的 MongoRepository 是一种 JPA 框架,所以增删改查都很容易。</p><p>可以参考 Spring Data JPA 方法命名规范</p><p></p><p></p><p></p><p></p></div><div class=\"s-weixin\"><ul class=\"weimg1\" style=\"text-align: center;\"></ul></div>', '', '', '', '', '', '', '2019-04-25 21:43:06', '2018-11-25 21:01:24', 'mongodb 是一种文档型数据库。跟 Redis 一样是非关系型数据库,Redis 属于那种小而快的数据库,常常用作缓存。而如果我们需要存一些类似于日志的那种,可以尝试用 mongodb (当然也有人用 MySQL,就是有点慢)。我们尝试用 mongodb 来存储博客的日志信息。本文主要介绍&nb');
INSERT INTO `article` VALUES ('', '', 'RocketMQ 实战之快速入门', '<div><p>最近 RocketMQ 刚刚上生产环境,闲暇之时在这里做一些分享,主要目的是让初学者能快速上手RocketMQ。</p>\r\n<h2>RocketMQ 是什么</h2>\r\n<p>Github 上关于 RocketMQ 的介绍:<br>\r\nRcoketMQ 是一款低延迟、高可靠、可伸缩、易于使用的消息中间件。具有以下特性:</p>\r\n<ol>\r\n<li>支持发布/订阅(Pub/Sub)和点对点(P2P)消息模型</li>\r\n<li>在一个队列中可靠的先进先出(FIFO)和严格的顺序传递</li>\r\n<li>支持拉(pull)和推(push)两种消息模式</li>\r\n<li>单一队列百万消息的堆积能力</li>\r\n<li>支持多种消息协议,如 JMS、MQTT 等</li>\r\n<li>分布式高可用的部署架构,满足至少一次消息传递语义</li>\r\n<li>提供 docker 镜像用于隔离测试和云集群部署</li>\r\n<li>提供配置、指标和监控等功能丰富的 Dashboard</li>\r\n</ol>\r\n<p>对于这些特性描述,大家简单过一眼就即可,深入学习之后自然就明白了。</p>\r\n<h2>专业术语</h2>\r\n<h3>Producer</h3>\r\n<p>消息生产者,生产者的作用就是将消息发送到 MQ,生产者本身既可以产生消息,如读取文本信息等。也可以对外提供接口,由外部应用来调用接口,再由生产者将收到的消息发送到 MQ。</p>\r\n<h3>Producer Group</h3>\r\n<p>生产者组,简单来说就是多个发送同一类消息的生产者称之为一个生产者组。在这里可以不用关心,只要知道有这么一个概念即可。</p>\r\n<h3>Consumer</h3>\r\n<p>消息消费者,简单来说,消费 MQ 上的消息的应用程序就是消费者,至于消息是否进行逻辑处理,还是直接存储到数据库等取决于业务需要。</p>\r\n<h3>Consumer Group</h3>\r\n<p>消费者组,和生产者类似,消费同一类消息的多个 consumer 实例组成一个消费者组。</p>\r\n<h3>Topic</h3>\r\n<p>Topic 是一种消息的逻辑分类,比如说你有订单类的消息,也有库存类的消息,那么就需要进行分类,一个是订单 Topic 存放订单相关的消息,一个是库存 Topic 存储库存相关的消息。</p>\r\n<h3>Message</h3>\r\n<p>Message 是消息的载体。一个 Message 必须指定 topic,相当于寄信的地址。Message 还有一个可选的 tag 设置,以便消费端可以基于 tag 进行过滤消息。也可以添加额外的键值对,例如你需要一个业务 key 来查找 broker 上的消息,方便在开发过程中诊断问题。</p>\r\n<h3>Tag</h3>\r\n<p>标签可以被认为是对 Topic 进一步细化。一般在相同业务模块中通过引入标签来标记不同用途的消息。</p>\r\n<h3>Broker</h3>\r\n<p>Broker 是 RocketMQ 系统的主要角色,其实就是前面一直说的 MQ。Broker 接收来自生产者的消息,储存以及为消费者拉取消息的请求做好准备。</p>\r\n<h3>Name Server</h3>\r\n<p>Name Server 为 producer 和 consumer 提供路由信息。</p>\r\n<h2>RocketMQ 架构</h2>\r\n<div class=\"image-package\">\r\n<div class=\"image-container\">\r\n<div class=\"image-container-fill\"></div>\r\n<div class=\"image-view\" data-width=\"975\" data-height=\"434\"><img data-original-src=\"//upload-images.jianshu.io/upload_images/6332814-7885601f065a3556.png\" data-original-width=\"975\" data-original-height=\"434\" data-original-format=\"image/png\" data-original-filesize=\"69606\" class=\"\" src=\"//upload-images.jianshu.io/upload_images/6332814-7885601f065a3556.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/975/format/webp\"></div>\r\n</div>\r\n<div class=\"image-caption\">RocketMQ 架构</div>\r\n</div>\r\n<p>由这张图可以看到有四个集群,分别是 NameServer 集群、Broker 集群、Producer 集群和 Consumer 集群:</p>\r\n<ol>\r\n<li>NameServer: 提供轻量级的服务发现和路由。 每个 NameServer 记录完整的路由信息,提供等效的读写服务,并支持快速存储扩展。</li>\r\n<li>Broker: 通过提供轻量级的 Topic 和 Queue 机制来处理消息存储,同时支持推(push)和拉(pull)模式以及主从结构的容错机制。</li>\r\n<li>Producer:生产者,产生消息的实例,拥有相同 Producer Group 的 Producer 组成一个集群。</li>\r\n<li>Consumer:消费者,接收消息进行消费的实例,拥有相同 Consumer Group 的<br>\r\nConsumer 组成一个集群。</li>\r\n</ol>\r\n<p>简单说明一下图中箭头含义,从 Broker 开始,Broker Master1 和 Broker Slave1 是主从结构,它们之间会进行数据同步,即 Date Sync。同时每个 Broker 与<br>\r\nNameServer 集群中的所有节<br>\r\n点建立长连接,定时注册 Topic 信息到所有 NameServer 中。</p>\r\n<p>Producer 与 NameServer 集群中的其中一个节点(随机选择)建立长连接,定期从 NameServer 获取 Topic 路由信息,并向提供 Topic 服务的 Broker Master 建立长连接,且定时向 Broker 发送心跳。Producer 只能将消息发送到 Broker master,但是 Consumer 则不一样,它同时和提供 Topic 服务的 Master 和 Slave<br>\r\n建立长连接,既可以从 Broker Master 订阅消息,也可以从 Broker Slave 订阅消息。</p>\r\n<h2>RocketMQ 集群部署模式</h2>\r\n<ol>\r\n<li>单 master 模式<br>\r\n也就是只有一个 master 节点,称不上是集群,一旦这个 master 节点宕机,那么整个服务就不可用,适合个人学习使用。</li>\r\n<li>多 master 模式<br>\r\n多个 master 节点组成集群,单个 master 节点宕机或者重启对应用没有影响。<br>\r\n优点:所有模式中性能最高<br>\r\n缺点:单个 master 节点宕机期间,未被消费的消息在节点恢复之前不可用,消息的实时性就受到影响。<br>\r\n<strong>注意</strong>:使用同步刷盘可以保证消息不丢失,同时 Topic 相对应的 queue 应该分布在集群中各个节点,而不是只在某各节点上,否则,该节点宕机会对订阅该 topic 的应用造成影响。</li>\r\n<li>多 master 多 slave 异步复制模式<br>\r\n在多 master 模式的基础上,每个 master 节点都有至少一个对应的 slave。master<br>\r\n节点可读可写,但是 slave 只能读不能写,类似于 mysql 的主备模式。<br>\r\n优点: 在 master 宕机时,消费者可以从 slave 读取消息,消息的实时性不会受影响,性能几乎和多 master 一样。<br>\r\n缺点:使用异步复制的同步方式有可能会有消息丢失的问题。</li>\r\n<li>多 master 多 slave 同步双写模式<br>\r\n同多 master 多 slave 异步复制模式类似,区别在于 master 和 slave 之间的数据同步方式。<br>\r\n优点:同步双写的同步模式能保证数据不丢失。<br>\r\n缺点:发送单个消息 RT 会略长,性能相比异步复制低10%左右。<br>\r\n刷盘策略:同步刷盘和异步刷盘(指的是节点自身数据是同步还是异步存储)<br>\r\n同步方式:同步双写和异步复制(指的一组 master 和 slave 之间数据的同步)<br>\r\n<strong>注意</strong>:要保证数据可靠,需采用同步刷盘和同步双写的方式,但性能会较其他方式低。</li>\r\n</ol>\r\n<h2>RocketMQ 单主部署</h2>\r\n<p>鉴于是快速入门,我选择的是第一种单 master 的部署模式。先说明一下我的安装环境:</p>\r\n<ol>\r\n<li>Centos 7.2</li>\r\n<li>jdk 1.8</li>\r\n<li>Maven 3.2.x</li>\r\n<li>Git</li>\r\n</ol>\r\n<p>这里 git 可用可不用,主要是用来直接下载 github 上的源码。也可以选择自己到<br>\r\ngithub 上下载,然后上传到服务器上。以git操作为示例。</p>\r\n<ol>\r\n<li>clone 源码并用 maven 编译</li>\r\n</ol>\r\n<pre class=\"hljs bash\"><code class=\"bash\">> git <span class=\"hljs-built_in\">clone</span> https://github.com/alibaba/RocketMQ.git /opt/RocketMQ\r\n> <span class=\"hljs-built_in\">cd</span> /opt/RocketMQ && mvn -Dmaven.test.skip=<span class=\"hljs-literal\">true</span> clean package install assembly:assembly -U\r\n> <span class=\"hljs-built_in\">cd</span> target/alibaba-rocketmq-broker/alibaba-rocketmq\r\n</code></pre>\r\n<p><strong>此处可能遇到的问题</strong><br>\r\n一、执行\"git clone <a href=\"https://link.jianshu.com?t=https%3A%2F%2Fgithub.com%2Falibaba%2FRocketMQ.git\" target=\"_blank\" rel=\"nofollow\">https://github.com/alibaba/RocketMQ.git</a> /home/inspkgs/RocketMQ\"时出现以下提示:</p>\r\n<pre class=\"hljs python\"><code class=\"python\">fatal: unable to access <span class=\"hljs-string\">\'https://github.com/alibaba/RocketMQ.git/\'</span>: Could <span class=\"hljs-keyword\">not</span> resolve host: github.com; Unknown error\r\n</code></pre>\r\n<p>解决办法:一般是由于网络原因造成的,执行以下命令</p>\r\n<pre class=\"hljs css\"><code class=\"css\">> <span class=\"hljs-selector-tag\">ping</span> <span class=\"hljs-selector-tag\">github</span><span class=\"hljs-selector-class\">.com</span>\r\n</code></pre>\r\n<p>确定可以 ping 通之后,再重新执行 git clone 命令。<br>\r\n二、执行\"mvn -Dmaven.test.skip=true clean package install assembly:assembly -U\"编译时,可能出现下载相关jar很慢的情况。<br>\r\n这也是由于默认 maven 中央仓库在国外的原因,可以根据需要在 /home/maven/conf/setting.xml 中的 <mirrors></mirrors> 添加以下内容后重新编译:</p>\r\n<pre class=\"hljs xml\"><code class=\"xml\"><span class=\"hljs-tag\"><<span class=\"hljs-name\">mirror</span>></span>\r\n <span class=\"hljs-tag\"><<span class=\"hljs-name\">id</span>></span>aliyun<span class=\"hljs-tag\"><!--<span class=\"hljs-name\"-->id</span>>\r\n <span class=\"hljs-tag\"><<span class=\"hljs-name\">mirrorOf</span>></span>central<span class=\"hljs-tag\"><!--<span class=\"hljs-name\"-->mirrorOf</span>>\r\n <span class=\"hljs-tag\"><<span class=\"hljs-name\">name</span>></span>aliyun maven<span class=\"hljs-tag\"><!--<span class=\"hljs-name\"-->name</span>>\r\n <span class=\"hljs-tag\"><<span class=\"hljs-name\">url</span>></span>http://maven.aliyun.com/nexus/content/groups/public/<span class=\"hljs-tag\"><!--<span class=\"hljs-name\"-->url</span>>\r\n<span class=\"hljs-tag\"><!--<span class=\"hljs-name\"-->mirror</span>>\r\n</code></pre>\r\n<ol start=\"2\">\r\n<li>启动 Name Server</li>\r\n</ol>\r\n<pre class=\"hljs cpp\"><code class=\"cpp\">> nohup sh /opt/RocketMQ/bin/mqnamesrv &\r\n<span class=\"hljs-comment\">//执行 jps 查看进程</span>\r\n> jps\r\n<span class=\"hljs-number\">25913</span> NamesrvStartup\r\n<span class=\"hljs-comment\">//查看日志确保服务已正常启动</span>\r\n> tail -f ~/logs/rocketmqlogs/namesrv.<span class=\"hljs-built_in\">log</span>\r\nThe Name Server boot success...\r\n</code></pre>\r\n<ol start=\"3\">\r\n<li>启动 broker</li>\r\n</ol>\r\n<pre class=\"hljs cpp\"><code class=\"cpp\">> nohup sh /opt/RocketMQ/bin/mqbroker -n localhost:<span class=\"hljs-number\">9876</span> &\r\n<span class=\"hljs-comment\">//执行 jps 查看进程</span>\r\n> jps\r\n<span class=\"hljs-number\">25954</span> BrokerStartup\r\n<span class=\"hljs-comment\">//查看日志确保服务已正常启动</span>\r\n> tail -f ~/logs/rocketmqlogs/broker.<span class=\"hljs-built_in\">log</span> \r\nThe broker[broker-a, <span class=\"hljs-number\">10.1</span><span class=\"hljs-number\">.54</span><span class=\"hljs-number\">.121</span>:<span class=\"hljs-number\">10911</span>] boot success...\r\n</code></pre>\r\n<ol start=\"4\">\r\n<li>发送和接收消息<br>\r\n发送/接收消息之前,我们需要告诉客户端 NameServer 地址。RocketMQ 提供了多种方式来实现这一目标。为简单起见,我们使用环境变量 NAMESRV_ADDR。</li>\r\n</ol>\r\n<pre class=\"hljs bash\"><code class=\"bash\">> <span class=\"hljs-built_in\">export</span> NAMESRV_ADDR=localhost:9876\r\n> sh /opt/RocketMQ/bin/tools.sh com.alibaba.rocketmq.example.quickstart.Producer\r\nSendResult [sendStatus=SEND_OK, msgId= ...\r\n> sh /opt/RocketMQ/bin/tools.sh com.alibaba.rocketmq.example.quickstart.Consumer\r\nConsumeMessageThread_%d Receive New Messages: [MessageExt...\r\n</code></pre>\r\n<ol start=\"5\">\r\n<li>关闭服务</li>\r\n</ol>\r\n<pre class=\"hljs python\"><code class=\"python\">> sh /opt/RocketMQ/bin/mqshutdown broker\r\nThe mqbroker(<span class=\"hljs-number\">36695</span>) <span class=\"hljs-keyword\">is</span> running...\r\nSend shutdown request to mqbroker(<span class=\"hljs-number\">36695</span>) OK\r\n> sh /opt/RocketMQ/bin/mqshutdown namesrv\r\nThe mqnamesrv(<span class=\"hljs-number\">36664</span>) <span class=\"hljs-keyword\">is</span> running...\r\nSend shutdown request to mqnamesrv(<span class=\"hljs-number\">36664</span>) OK\r\n</code></pre>\r\n<h2>生产者、消费者 Demo</h2>\r\n<ol>\r\n<li>生产者</li>\r\n</ol>\r\n<pre class=\"hljs java\"><code class=\"java\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">Producer</span> </span>{\r\n <span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">static</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">main</span><span class=\"hljs-params\">(String[] args)</span> <span class=\"hljs-keyword\">throws</span> MQClientException, InterruptedException </span>{\r\n\r\n <span class=\"hljs-comment\">//声明并初始化一个producer</span>\r\n <span class=\"hljs-comment\">//需要一个producer group名字作为构造方法的参数,这里为producer1</span>\r\n DefaultMQProducer producer = <span class=\"hljs-keyword\">new</span> DefaultMQProducer(<span class=\"hljs-string\">\"producer1\"</span>);\r\n \r\n <span class=\"hljs-comment\">//设置NameServer地址,此处应改为实际NameServer地址,多个地址之间用;分隔</span>\r\n <span class=\"hljs-comment\">//NameServer的地址必须有,但是也可以通过环境变量的方式设置,不一定非得写死在代码里</span>\r\n producer.setNamesrvAddr(<span class=\"hljs-string\">\"10.1.54.121:9876;10.1.54.122:9876\"</span>);\r\n \r\n <span class=\"hljs-comment\">//调用start()方法启动一个producer实例</span>\r\n producer.start();\r\n\r\n <span class=\"hljs-comment\">//发送10条消息到Topic为TopicTest,tag为TagA,消息内容为“Hello RocketMQ”拼接上i的值</span>\r\n <span class=\"hljs-keyword\">for</span> (<span class=\"hljs-keyword\">int</span> i = <span class=\"hljs-number\">0</span>; i < <span class=\"hljs-number\">10</span>; i++) {\r\n <span class=\"hljs-keyword\">try</span> {\r\n Message msg = <span class=\"hljs-keyword\">new</span> Message(<span class=\"hljs-string\">\"TopicTest\"</span>,<span class=\"hljs-comment\">// topic</span>\r\n <span class=\"hljs-string\">\"TagA\"</span>,<span class=\"hljs-comment\">// tag</span>\r\n (<span class=\"hljs-string\">\"Hello RocketMQ \"</span> + i).getBytes(RemotingHelper.DEFAULT_CHARSET)<span class=\"hljs-comment\">// body</span>\r\n );\r\n \r\n <span class=\"hljs-comment\">//调用producer的send()方法发送消息</span>\r\n <span class=\"hljs-comment\">//这里调用的是同步的方式,所以会有返回结果</span>\r\n SendResult sendResult = producer.send(msg);\r\n \r\n <span class=\"hljs-comment\">//打印返回结果,可以看到消息发送的状态以及一些相关信息</span>\r\n System.out.println(sendResult);\r\n } <span class=\"hljs-keyword\">catch</span> (Exception e) {\r\n e.printStackTrace();\r\n Thread.sleep(<span class=\"hljs-number\">1000</span>);\r\n }\r\n }\r\n\r\n <span class=\"hljs-comment\">//发送完消息之后,调用shutdown()方法关闭producer</span>\r\n producer.shutdown();\r\n }\r\n}\r\n</code></pre>\r\n<ol start=\"2\">\r\n<li>消费者</li>\r\n</ol>\r\n<pre class=\"hljs java\"><code class=\"java\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">Consumer</span> </span>{\r\n\r\n <span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">static</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">main</span><span class=\"hljs-params\">(String[] args)</span> <span class=\"hljs-keyword\">throws</span> InterruptedException, MQClientException </span>{\r\n \r\n <span class=\"hljs-comment\">//声明并初始化一个consumer</span>\r\n <span class=\"hljs-comment\">//需要一个consumer group名字作为构造方法的参数,这里为consumer1</span>\r\n DefaultMQPushConsumer consumer = <span class=\"hljs-keyword\">new</span> DefaultMQPushConsumer(<span class=\"hljs-string\">\"consumer1\"</span>);\r\n\r\n <span class=\"hljs-comment\">//同样也要设置NameServer地址</span>\r\n consumer.setNamesrvAddr(<span class=\"hljs-string\">\"10.1.54.121:9876;10.1.54.122:9876\"</span>);\r\n\r\n <span class=\"hljs-comment\">//这里设置的是一个consumer的消费策略</span>\r\n <span class=\"hljs-comment\">//CONSUME_FROM_LAST_OFFSET 默认策略,从该队列最尾开始消费,即跳过历史消息</span>\r\n <span class=\"hljs-comment\">//CONSUME_FROM_FIRST_OFFSET 从队列最开始开始消费,即历史消息(还储存在broker的)全部消费一遍</span>\r\n <span class=\"hljs-comment\">//CONSUME_FROM_TIMESTAMP 从某个时间点开始消费,和setConsumeTimestamp()配合使用,默认是半个小时以前</span>\r\n consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);\r\n\r\n <span class=\"hljs-comment\">//设置consumer所订阅的Topic和Tag,*代表全部的Tag</span>\r\n consumer.subscribe(<span class=\"hljs-string\">\"TopicTest\"</span>, <span class=\"hljs-string\">\"*\"</span>);\r\n\r\n <span class=\"hljs-comment\">//设置一个Listener,主要进行消息的逻辑处理</span>\r\n consumer.registerMessageListener(<span class=\"hljs-keyword\">new</span> MessageListenerConcurrently() {\r\n\r\n <span class=\"hljs-meta\">@Override</span>\r\n <span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> ConsumeConcurrentlyStatus <span class=\"hljs-title\">consumeMessage</span><span class=\"hljs-params\">(List<messageext> msgs,\r\n ConsumeConcurrentlyContext context)</messageext></span> </span>{\r\n\r\n System.out.println(Thread.currentThread().getName() + <span class=\"hljs-string\">\" Receive New Messages: \"</span> + msgs);\r\n \r\n <span class=\"hljs-comment\">//返回消费状态</span>\r\n <span class=\"hljs-comment\">//CONSUME_SUCCESS 消费成功</span>\r\n <span class=\"hljs-comment\">//RECONSUME_LATER 消费失败,需要稍后重新消费</span>\r\n <span class=\"hljs-keyword\">return</span> ConsumeConcurrentlyStatus.CONSUME_SUCCESS;\r\n }\r\n });\r\n\r\n <span class=\"hljs-comment\">//调用start()方法启动consumer</span>\r\n consumer.start();\r\n\r\n System.out.println(<span class=\"hljs-string\">\"Consumer Started.\"</span>);\r\n }\r\n}\r\n</code></pre>\r\n<p></p></div><br><br>', '', '', '', '', '', '', '2019-04-25 21:43:02', '2018-11-25 21:02:40', '最近 RocketMQ 刚刚上生产环境,闲暇之时在这里做一些分享,主要目的是让初学者能快速上手RocketMQ。\r\nRocketMQ 是什么\r\nGithub 上关于 RocketMQ 的介绍:\r\nRcoketMQ 是一款低延迟、高可靠、可伸缩、易于使用的消息中间件。具有以下特性:\r\n\r\n支持发布/订');
INSERT INTO `article` VALUES ('', '', 'Docker_入门?只要这篇就够了!(纯干货适合0基础小白)', '<p>原文地址:<a href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=https://blog.csdn.net/S_gy_Zetrov/article/details/78161154\" target=\"_blank\" rel=\"noopener noreferrer\">https://blog.csdn.net/S_gy_Zetrov/article/details/78161154</a></p><h2 id=\"写在前面\">写在前面</h2><ul><li>这篇博客适合谁?<ul><li>对于Docker并不了解,只是有一点模糊的感觉,觉得Docker可以当成虚拟机用之类的</li><li>只是下载了Docker软件,对于怎么配置,怎么玩,第一步干什么,完全一无所知</li><li>本文适用于mac,PC用户不保证文章的效果,现在离开来得及</li></ul></li><li>网上Docker相关的教程都泛滥了,为啥还要写,为啥我要看你的文章<ul><li>首先欢迎你,能搜索到我的博客就是缘分</li><li>其次,确实,现在Docker相关的文章真的太多了,那我为什么还要写呢?原因有三:<ul><li>其一,文章是很多,各种1小时入门什么的,相信你已经在知乎看过很多了,我也看过,<br>因为我就是从知乎那几篇教程为起点,自己抠出来的,现在把自己的心得和经验贴出来,我觉得既是对自己负责也能服务他人。</li><li>其二,我从对Docker只有及其模糊(真的是模糊,我除了知道Docker是跟虚拟系统有关其他啥都不知道),到现在起码一<br>问一不知(对应一问三不知)的状态,大概花了8个小时。这期间全靠我从网络上搜索到的资料。现在用我的话写出来,我觉得对于跟我相同开始情况的小白用户,能省不少时间,所以我要写。</li><li>其三,因为我自己是纯靠搜索到的教程和技术文章学习的Docker,我自然是懂得干涩的语言对学习的拖慢。所以我自己写的时候,自然会考虑到这个问题。本文尽量不用技术用语,尽量使用通俗易懂的文字,为进一步拉低Docker的入门门槛作出我自己的贡献。</li></ul></li></ul></li><li>这篇文章主要是哪方面的Docker文章,纯入门?你做了哪些工作?<ul><li>很高兴你问了我这个问题。本文主要:<ul><li>首先对于Docker的概念作出我的解释,尽量通俗易懂</li><li>接着针对我用Docker的目的,从最开始的软件下载,到最后push一个自己的镜像到hub,整个过程以白话的形式描述出来,降低入门门槛,节省你的学习时间</li></ul></li><li>我做了哪些工作?这是个好问题!<ul><li>首先,我一直都很好奇Docker这个东西,但从来没有机会去使用它。但是,这学期OS课的lab,纯Linux系统编程,虽然使用双系统或者虚拟机都是纯小学生的难易程度了,也不麻烦,但我想,为什么不用Docker呢?用双系统或者虚拟机也太过小儿科!所以,我就这样接触了Docker</li><li>那么我使用Docker实现了什么?在lab中,我们需要用到的是纯终端下的gcc工具链还有vim等,那么这就是我需要的全部软件了!于是我在Docker中实现了「gcc+gdb<br>+vim」环境,并push到了hub中。</li></ul></li></ul></li><li>你这篇博客有没有抄别人的?<ul><li>额,我学习Docker时确实看了很多很多(真的是很多)文章,现在写这篇博客,除了自己的经验心得,自然还得去看那些文章。但我能保证的是,我写的都是取精华去糟粕,不然这篇文章就不必存在了你说是不是?</li><li>参考资料会在文后统一附上,谢谢!</li></ul></li></ul><p>好了,闲话说完,我们开始吧!</p><p> </p><h2 id=\"入门docker你要下载什么注册什么\">入门Docker,你要下载什么?注册什么?</h2><p id=\"dockerapp你肯定是要下载的\"><strong><span>+ Docker.app你肯定是要下载的!</span></strong></p><p>Docker for mac,这个你肯定要下载:<a href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=https://www.docker.com/community-edition\" target=\"_blank\" rel=\"noopener noreferrer\">点我下载Docker for mac的社区免费版本</a></p><p> </p><p id=\"注册docker官方账号你需要它\"><strong><span>+ 注册Docker官方账号,你需要它!</span></strong></p><p>注册一个Docker的官方账号,有利而无害,相信我!<a href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=https://cloud.docker.com/swarm/sgyzetrov/dashboard/onboarding/cloud-registry\" target=\"_blank\" rel=\"noopener noreferrer\">点我注册Docker Cloud官方账号</a></p><p>Docker安装好,账号也注册后,点击桌面顶栏的Docker图标,点击sign in,登陆你的Docker账号。</p><p> </p><h2 id=\"注册daocloud账号获取加速服务\"><span>+ 注册DaoCloud账号,获取加速服务!</span></h2><p>不可否认,有时直接从Docker官方往本地pull镜像会十分缓慢。。。这时我们可以通过国内的Docker服务提供商免费获取加速pull镜像服务,阿里网易好像都有这种服务,我选择的是DaoCloud:<a href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=https://account.daocloud.io/signup\" target=\"_blank\" rel=\"noopener noreferrer\">点我注册DaoCloud账号</a></p><p>注册后登陆DaoCloud,找到这个按钮:</p><p><img title=\"\" data-original=\"https://img-blog.csdn.net/20171005113053850?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvU19neV9aZXRyb3Y=/font/5a6L5L2T/fontsize/1400/fill/I0ZGMDAwMA==/dissolve/90/gravity/Center\" src=\"https://media.liuyanzhao.com/wp-content/themes/begin/img/blank.gif\" alt=\"docker1.png\"></p><p>接着,按照它的步骤,为自己添加Docker加速服务</p><p><img title=\"\" data-original=\"https://img-blog.csdn.net/20171005113639144?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvU19neV9aZXRyb3Y=/font/5a6L5L2T/fontsize/1400/fill/I0ZGMDAwMA==/dissolve/70/gravity/Center\" src=\"https://media.liuyanzhao.com/wp-content/themes/begin/img/blank.gif\" alt=\"docker2\"></p><h4></h4><p id=\"到此准备工作结束进入下一部分\"><strong><span>+ 到此准备工作结束,进入下一部分!</span></strong></p><p> </p><h2 id=\"要想入门docker首先你需要理解docker\">要想入门Docker,首先你需要理解Docker!</h2><p>我也不扯淡了,直接上我对Docker的理解:(Copyright © <a href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=http://blog.csdn.net/s_gy_zetrov\" target=\"_blank\" rel=\"noopener noreferrer\">http://blog.csdn.net/s_gy_zetrov</a>. All Rights Reserved)</p><p>Docker,可以说是一个终端命令行的虚拟机,但更准确的说法,其实应该是一个虚拟环境。比如,你想要在PC上无缝使用Linux么?那么虚拟机并不是你唯一的出路,你还有Docker!我更愿意称Docker为一个容器,当然这只是Docker的一个狭义解释,Docker不止是一个容器。Docker包含3个重要概念:</p><ul><li>一个,是镜像(Image),镜像是静态的、可以被用户互相分享的文件。我们玩过双系统和虚拟机的人都知道,首先你需要一个.iso镜像,才能安装系统。Docker中的镜像也是这个东西,<strong><span>镜像是静态的,你不能对他操作,只能pull别人的镜像或者push自己的镜像</span></strong>。</li><li>还有一个,是容器(Container),前面说过,镜像是静态不可操作的,只能被分享和下载,那什么是能被操作的呢?就是容器里!容器可以理解为镜像的动态状态,也就是我们虚拟机中系统装好后的状态,其实这么说是不对的,容器最符合的描述应该是Linux的iso文件的<code>Live CD</code>模式,比如我们玩双系统时都进入过<code>Live CD</code>模式,不安装系统而直接进入系统,很神奇是吧,Docker的容器就是这个概念,只不过更加轻量更加迅速便捷。但是<code>Live CD</code>的害处就是你关机后作出的修改安装的软件全部gg,<strong><span>容器也是一样,一旦被直接推出,之前安装的gcc啊vim啊啥的就会全部gg掉。如果要保存修改,就需要将当前容器封装成一个新的镜像,这样下次启动这个新的镜像后之前作出的修改还都在</span></strong>。</li><li>最后,是仓库(Repository)。各位在前面看到我写的pull和push什么的,有没有晕?不知道各位对于git熟悉不熟悉,Docker中的仓库很像git的代码仓库,你可以pull自己之前push到自己仓库的镜像到本地,也可以pull别人push到公共仓库的镜像到自己本地。说白了就是百度云盘,你可以上传(push)自己做好环境的Docker上去,也可以下载(pull)自己云端的镜像到本地。同时,我们知道百度云最大的特点就是分享(你懂的嘿嘿嘿),类比Docker,如果你得到百度云分享链接(别人的镜像名字、标签和别人的用户名),你还可以下载(pull)别人分享的镜像到自己的本地,别人也可以下载(pull)你的镜像,因为Docker仓库都是公共的。当然,每个免费用户有一个名额把自己的一个镜像设为私有,也就是禁止被分享给别人,类比百度云上你自己保存的而没有被生成分享链接的小姐姐。</li></ul><p>接下来来张高大上的概念图,各位看个热闹2333</p><p><img title=\"\" data-original=\"https://img-blog.csdn.net/20171005120538429?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvU19neV9aZXRyb3Y=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast\" src=\"https://media.liuyanzhao.com/wp-content/themes/begin/img/blank.gif\" alt=\"docker3.png\"></p><h3 id=\"接下来就是实战了打开你的iterm2我是iterm2党2333打开普通terminal也行这个不影响\">接下来就是实战了!打开你的iTerm2!(我是iTerm2党2333,打开普通terminal也行,这个不影响)</h3><p> </p><p id=\"查看docker版本信息\"><strong><span>+ 查看Docker版本信息</span></strong></p><pre class=\"prettyprint\"><code class=\"hljs livecodeserver has-numbering\">终端输入:\r\n<strong><span>docker <span class=\"hljs-built_in\">version</span></span></strong></code></pre><p> </p><p>显示的我的版本信息</p><pre class=\"prettyprint\"><code class=\"hljs r has-numbering\"> ~$ docker version\r\nClient:\r\n Version: <span class=\"hljs-number\">17.06</span><span class=\"hljs-number\">.2</span>-ce\r\n API version: <span class=\"hljs-number\">1.30</span>\r\n Go version: go1.8.3\r\n Git commit: cec0b72\r\n Built: Tue Sep <span class=\"hljs-number\">5</span> <span class=\"hljs-number\">20</span>:<span class=\"hljs-number\">12</span>:<span class=\"hljs-number\">06</span> <span class=\"hljs-number\">2017</span>\r\n OS/Arch: darwin/amd64\r\n <span class=\"hljs-keyword\">...</span>\r\n //omitted by sgy(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)\r\n <span class=\"hljs-keyword\">...</span></code></pre><p>不过我一般不需要那么多信息,所以一直用的是<code>docker -v</code>命令</p><h4></h4><p id=\"是时候pull你的第一个镜像下来的\"><strong><span>+ 是时候pull你的第一个镜像下来的!</span></strong></p><p>Docker安装好后是不会自带镜像的,你需要从仓库自己pull一个镜像下来,自己制作自己的镜像也是一个道理,你可以通过在已有的镜像基础上生成自己的镜像或者看一下这篇博客: <a href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=http://blog.csdn.net/shiqiangdexin/article/details/52472195\" target=\"_blank\" rel=\"noopener noreferrer\">随便百度的如何创建自己Docker镜像得到的教程</a></p><p>Docker镜像官方好像只提供Linux,这个很容易想,windows和mac是要交钱的吧大概?(否)</p><p>搜索ubuntu的Docker镜像<strong><span><code>docker search ubuntu</code></span></strong>,这个如果你想要什么centos,直接改就行,不区分写法,我的返回结果:</p><pre class=\"prettyprint\"><code class=\"hljs r has-numbering\">~$ docker search ubuntu\r\nNAME DESCRIPTION STARS OFFICIAL AUTOMATED\r\nubuntu Ubuntu is a Debian-based Linux operating s... <span class=\"hljs-number\">6636</span> [OK]\r\ndorowu/ubuntu-desktop-lxde-vnc Ubuntu with openssh-server and NoVNC <span class=\"hljs-number\">131</span> [OK]\r\nrastasheep/ubuntu-sshd Dockerized SSH service, built on top of of... <span class=\"hljs-number\">105</span> [OK]\r\nansible/ubuntu14.04-ansible Ubuntu <span class=\"hljs-number\">14.04</span> LTS with ansible <span class=\"hljs-number\">86</span> [OK]\r\nubuntu-upstart Upstart is an event-based replacement <span class=\"hljs-keyword\">for</span> <span class=\"hljs-keyword\">...</span> <span class=\"hljs-number\">80</span> [OK]\r\nneurodebian NeuroDebian provides neuroscience research... <span class=\"hljs-number\">40</span> [OK]\r\nubuntu-debootstrap debootstrap --variant=minbase --components... <span class=\"hljs-number\">31</span> [OK]\r\n<span class=\"hljs-keyword\">...</span>\r\n//omitted by sgy(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)\r\n<span class=\"hljs-keyword\">...</span></code></pre><p> </p><p>拉取官方最新版的ubuntu镜像:<strong><span><code>docker pull ubuntu:latest</code></span></strong>,其中的latest是一个标签(tag),表示是当前最新版本。你应该得到的信息,类似这样的</p><pre class=\"prettyprint\"><code class=\"hljs r has-numbering\">~$ docker pull ubuntu:latest\r\nTrying to pull repository docker.io/<span class=\"hljs-keyword\">library</span>/ubuntu <span class=\"hljs-keyword\">...</span> \r\nlatest: Pulling from docker.io/<span class=\"hljs-keyword\">library</span>/ubuntu\r\naed158d74952: Pull complete \r\n773ae8273d14: Pull complete \r\nd1d487w88782: Pull complete \r\ncd3d6cd6c0cf: Pull complete \r\n8d73bu79120c: Pull complete \r\nDigest: sha256:35bc48a1ca97c3f74rhf378hj92hd82j29i4hf4hf84nf0dhnsid232de8d8\r\nStatus: Downloaded newer image <span class=\"hljs-keyword\">for</span> docker.io/ubuntu:latest</code></pre><p><strong><span>你输入的命令实际上相当于<code>docker pull registry.hub.docker.com/ubuntu:latest</code>命令,即从注册服务器<code>registry.hub.docker.com</code>中的名为<code>ubuntu</code>的仓库中下载标签为<code>latest</code>的镜像</span></strong>。</p><p> </p><p>由于我的lab统一要求,ubuntu需要14.04版本,所以我在hub里面搜了搜,<strong><span>找到了一个用户分享的14.04 64位纯净镜像(base image)</span></strong>,下面将他的镜像扒下来</p><pre class=\"prettyprint\"><code class=\"hljs ruby has-numbering\">~<span class=\"hljs-variable\">$ </span>docker pull chug/ubuntu14.<span class=\"hljs-number\">04</span>x64</code></pre><p>这个用户还有很多其他版本的ubuntu系统,12 13 14的32位64位都有,全是纯净镜像。</p><h4></h4><p id=\"查看你本地的镜像仓库\"><strong><span>+ 查看你本地的镜像仓库!</span></strong></p><p>把初始镜像拉下来后,就可以启动它了,不过,可以先使用<code>docker images</code>命令查看你自己的本地镜像,我随便找了个例子,你的也应该是类似这样的:</p><pre class=\"prettyprint\"><code class=\"hljs lua has-numbering\">~$ <span><strong>docker images</strong></span>\r\nREPOSITORY TAG IMAGE ID CREATED SIZE\r\ndocker.<span class=\"hljs-built_in\">io</span>/ubuntu <span class=\"hljs-number\">16.04</span> e4415b714b62 <span class=\"hljs-number\">11</span> days ago <span class=\"hljs-number\">128.1</span> MB\r\ndocker.<span class=\"hljs-built_in\">io</span>/ubuntu latest e4415b714b62 <span class=\"hljs-number\">11</span> days ago <span class=\"hljs-number\">128.1</span> MB\r\ndocker.<span class=\"hljs-built_in\">io</span>/ubuntu <span class=\"hljs-number\">12.04</span> aefa163f7a7e <span class=\"hljs-number\">11</span> days ago <span class=\"hljs-number\">103.5</span> MB\r\ndocker.<span class=\"hljs-built_in\">io</span>/centos latest <span class=\"hljs-number\">0584</span>b3d2cf6d <span class=\"hljs-number\">3</span> weeks ago <span class=\"hljs-number\">196.5</span> MB</code></pre><p> </p><p>从网上一个教程中找到以下说法,比我说的好,那就看这个吧!</p><blockquote><p>在列出信息中,可以看到几个字段信息:</p><ul><li>来自于哪个仓库,比如 ubuntu</li><li>镜像的标记,比如 16.04</li><li>它的 ID 号(唯一),比如e4415b714b62</li><li>创建时间</li><li>镜像大小</li></ul><p>其中镜像的 ID 唯一标识了镜像,注意到 ubuntu:16.04 和 ubuntu:latest具有相同的镜像 ID ,说明它们实际上是同一镜像。 TAG 信息用来标记来自同一个仓库的不同镜像。例如 ubuntu 仓库中有多个镜像,通过 TAG 信息来区分发行版本,例如10.04 、 12.04 、 12.10 、 13.04 、 14.04 等。例如可以使用<code>docker run -t -i ubuntu:16.04 /bin/bash</code>命令指定使用镜像<code>ubuntu:16.04</code>来启动一个容器。如果不指定具体的标记,则默认使用<code>latest</code>标记信息。</p></blockquote><h4></h4><p id=\"启动你的镜像并尽情安装软件吧\"><strong><span>+ 启动你的镜像并尽情安装软件吧!</span></strong></p><p>以下内容均以我自己pull下来的chug的初始镜像为例:</p><p>现在你已经有一个初始的镜像了,注意这个里面是什么都没有的,连vim都没装,是精简到不能再精简的镜像了。</p><p>首先启动它:</p><pre class=\"prettyprint\"><code class=\"hljs ruby has-numbering\">~<span class=\"hljs-variable\">$ </span>docker run -it chug/ubuntu14.<span class=\"hljs-number\">04</span>x64 /bin/bash\r\nroot<span class=\"hljs-variable\">@aa97ba3292ce</span><span class=\"hljs-symbol\">:/</span><span class=\"hljs-comment\">#</span></code></pre><p><strong><span>-it 表示运行在交互模式,是-i -t的缩写,即-it是两个参数:-i和-t。前者表示打开并保持stdout,后者表示分配一个终端(pseudo-tty)一般这个模式就是可以启动bash,然后和容器有命令行的交互</span></strong></p><p>可以看到我们终端的字头变成<code>root@aa97ba3292ce:/#</code>了,这就意味着我们的镜像已经创建了一个容器实例。现在我们可以对这个“ubuntu系统”进行操作了</p><p> </p><p>比如安装vim:</p><pre class=\"prettyprint\"><code class=\"hljs applescript has-numbering\">root@aa97ba3292ce:/<span class=\"hljs-comment\"># <strong><span>apt-get install vim</span></strong></span>\r\nReading package lists... Done\r\nBuilding dependency tree... Done\r\nThe following extra packages will be installed:\r\n <span class=\"hljs-type\">file</span> libexpat1 libffi6 libgpm2 libmagic1 libpython2<span class=\"hljs-number\">.7</span> libpython2<span class=\"hljs-number\">.7</span>-minimal libpython2<span class=\"hljs-number\">.7</span>-stdlib libsqlite3-<span class=\"hljs-number\">0</span> libssl1<span class=\"hljs-number\">.0</span><span class=\"hljs-number\">.0</span> mime-support vim-common vim-runtime\r\nSuggested packages:\r\n gpm ctags vim-doc vim-scripts\r\nThe following NEW packages will be installed:\r\n <span class=\"hljs-type\">file</span> libexpat1 libffi6 libgpm2 libmagic1 libpython2<span class=\"hljs-number\">.7</span> libpython2<span class=\"hljs-number\">.7</span>-minimal libpython2<span class=\"hljs-number\">.7</span>-stdlib libsqlite3-<span class=\"hljs-number\">0</span> libssl1<span class=\"hljs-number\">.0</span><span class=\"hljs-number\">.0</span> mime-support vim vim-common vim-runtime\r\n<span class=\"hljs-number\">0</span> upgraded, <span class=\"hljs-number\">14</span> newly installed, <span class=\"hljs-number\">0</span> <span class=\"hljs-keyword\">to</span> remove <span class=\"hljs-keyword\">and</span> <span class=\"hljs-number\">0</span> <span class=\"hljs-keyword\">not</span> upgraded.\r\nNeed <span class=\"hljs-keyword\">to</span> <span class=\"hljs-keyword\">get</span> <span class=\"hljs-number\">10.7</span> MB <span class=\"hljs-keyword\">of</span> archives.\r\nAfter this operation, <span class=\"hljs-number\">50.7</span> MB <span class=\"hljs-keyword\">of</span> additional disk <span class=\"hljs-constant\">space</span> will be used.\r\nDo you want <span class=\"hljs-keyword\">to</span> <span class=\"hljs-keyword\">continue</span>? [Y/n]</code></pre><p>看到我没有用sudo,是因为本来就已经是超级用户(root)状态了。</p><p>同理按照我自己的需求,我安装了gcc和gdb</p><h4></h4><p id=\"想退出容器很简单\"><span><strong>+ 想退出容器?很简单!</strong></span></p><p>如果使用exit,命令退出,则容器的状态处于Exit,而不是后台运行。如果想让容器一直运行,而不是停止,可以使用快捷键 <strong><span>ctrl+p ctrl+q</span></strong> 退出,此时容器的状态为Up。</p><p>查看当前正在运行的容器:</p><pre class=\"prettyprint\"><code class=\"hljs ruby has-numbering\">~<span class=\"hljs-variable\">$ </span><strong><span>docker ps</span></strong>\r\n<span class=\"hljs-constant\">CONTAINER</span> <span class=\"hljs-constant\">ID</span> <span class=\"hljs-constant\">IMAGE</span> <span class=\"hljs-constant\">COMMAND</span> <span class=\"hljs-constant\">CREATED</span> <span class=\"hljs-constant\">STATUS</span> <span class=\"hljs-constant\">PORTS</span> <span class=\"hljs-constant\">NAMES</span>\r\naa97ba3292ce chug/ubuntu14.<span class=\"hljs-number\">04</span>x64 <span class=\"hljs-string\">\"/bin/bash\"</span> <span class=\"hljs-number\">7</span> minutes ago <span class=\"hljs-constant\">Up</span> <span class=\"hljs-number\">7</span> minutes relaxed_hoover</code></pre><p> </p><p>看到当前有一个ID为aa97ba3292ce的容器(Copyright © <a href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=http://blog.csdn.net/s_gy_zetrov\" target=\"_blank\" rel=\"noopener noreferrer\">http://blog.csdn.net/s_gy_zetrov</a>. All Rights Reserved)</p><p>启动、停止、重启容器aa97ba3292ce的命令:</p><pre class=\"prettyprint\"><code class=\"hljs ruby has-numbering\">~<span class=\"hljs-variable\">$ </span><strong><span>docker start aa97ba3292ce</span></strong>\r\n~<span class=\"hljs-variable\">$ </span><span><strong>docker stop aa97ba3292ce</strong></span>\r\n~<span class=\"hljs-variable\">$ </span><span><strong>docker restart aa97ba3292ce</strong></span></code></pre><p> </p><p><strong><span>后台启动一个容器后,如果想进入到这个容器,可以使用attach命令</span></strong>:</p><pre class=\"prettyprint\"><code class=\"hljs ruby has-numbering\">~<span class=\"hljs-variable\">$ </span><strong><span>docker attach aa97ba3292ce</span></strong></code></pre><h4></h4><h4></h4><p id=\"软件装完想保存环境commit帮你\"><span><strong>+ 软件装完,想保存环境?commit帮你!</strong></span></p><p>将容器转化为一个镜像,即执行commit操作,完成后可使用<strong><span><code>docker images</code></span></strong>查看</p><pre class=\"prettyprint\"><code class=\"hljs ruby has-numbering\">root<span class=\"hljs-variable\">@aa97ba3292ce</span><span class=\"hljs-symbol\">:/</span><span class=\"hljs-comment\">#exit //先退出容器</span>\r\n~<span class=\"hljs-variable\">$ </span><strong><span>docker commit -m <span class=\"hljs-string\">\"ubuntu with vim\"</span> -a <span class=\"hljs-string\">\"sgy\"</span> aa97ba3292ce sgy/<span class=\"hljs-symbol\">ubuntu:</span>vim</span></strong>\r\n~<span class=\"hljs-variable\">$ </span>docker images\r\n<span class=\"hljs-constant\">REPOSITORY</span> <span class=\"hljs-constant\">TAG</span> <span class=\"hljs-constant\">IMAGE</span> <span class=\"hljs-constant\">ID</span> <span class=\"hljs-constant\">CREATED</span> <span class=\"hljs-constant\">SIZE</span>\r\nsgy/ubuntu vim <span class=\"hljs-number\">52166</span>e4475ed <span class=\"hljs-number\">5</span> seconds ago <span class=\"hljs-number\">358.1</span> <span class=\"hljs-constant\">MB</span>\r\nchug/ubuntu14.<span class=\"hljs-number\">04</span>x64 latest <span class=\"hljs-number\">05</span>84b3d2cf6d <span class=\"hljs-number\">9</span> days ago <span class=\"hljs-number\">196.5</span> <span class=\"hljs-constant\">MB</span></code></pre><p> </p><p>其中,-m指定说明信息;-a指定用户信息;aa97ba3292ce代表容器的id;sgy/ubuntu:vim指定目标镜像的用户名、仓库名和 tag 信息。我这里都是为了博客瞎编的用户名,我自己的用户名也不是sgy,你运行命令的时候使用自己注册Docker时的用户名。</p><p>此时Docker中就有了我们新建的镜像sgy/ubuntu:vim,此镜像和原有的ubuntu镜像区别在于多了个vim工具。此时我们利用新镜像创建的容器,本身就自带vim了。</p><p>启动新创建的镜像,可以看到vim已经自带了。</p><pre class=\"prettyprint\"><code class=\"hljs r has-numbering\">~$ <strong><span>docker run -it sgy/ubuntu:vim /bin/bash</span></strong>\r\nroot@520afc596c51:/<span class=\"hljs-comment\"># vim --version</span>\r\nVIM - Vi IMproved <span class=\"hljs-number\">7.4</span> (<span class=\"hljs-number\">2013</span> Aug <span class=\"hljs-number\">10</span>, compiled Apr <span class=\"hljs-number\">4</span> <span class=\"hljs-number\">2017</span> <span class=\"hljs-number\">18</span>:<span class=\"hljs-number\">14</span>:<span class=\"hljs-number\">54</span>)\r\n<span class=\"hljs-keyword\">...</span>\r\n//omitted by sgy(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)\r\n<span class=\"hljs-keyword\">...</span></code></pre><p>利用exit退出容器。此时Docker引擎中就有了两个容器,可使用<code>docker ps -a</code>查看。</p><h4></h4><h4>+ 想要删除容器或者镜像?用这个!</h4><p>如果想删除容器或者镜像,可以使用rm命令,注意:<span><strong>删除镜像前必须先删除以此镜像为基础的容器(哪怕是已经停止的容器),否则无法删除该镜像,会报错<code>Failed to remove image (e4415b714b62): Error response from daemon: conflict: unable to delete e4415b714b62 (cannot be forced) - image has dependent child images</code>类似这种</strong></span>。</p><pre class=\"prettyprint\"><code class=\"hljs ruby has-numbering\">~<span class=\"hljs-variable\">$ </span><strong><span>docker rm container_id</span></strong>\r\n~<span class=\"hljs-variable\">$ </span><span><strong>docker rmi image_id</strong></span></code></pre><p> </p><p>有的时候尽管删除了全部容器,镜像还是无法删除,这时点击mac顶栏中的docker logo,选择restart,然后再试一次rmi,应该就没问题了。(Copyright © <a href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=http://blog.csdn.net/s_gy_zetrov\" target=\"_blank\" rel=\"noopener noreferrer\">http://blog.csdn.net/s_gy_zetrov</a>. All Rights Reserved)</p><h4 id=\"附上一张高大上的docker命令图\">+ 附上一张高大上的Docker命令图</h4><p><img title=\"\" data-original=\"https://img-blog.csdn.net/20171005132826220\" src=\"https://media.liuyanzhao.com/wp-content/themes/begin/img/blank.gif\" alt=\"docker4.png\"></p><h4></h4><p id=\"一次配置到处使用那就push到hub上吧\"><span><strong>+ 一次配置,到处使用?那就push到hub上吧!</strong></span></p><p>因为之前已经在Docker.app中登陆了Docker账号,所以现在直接</p><pre class=\"prettyprint\"><code class=\"hljs ruby has-numbering\">~<span class=\"hljs-variable\">$ </span><strong><span>docker push sgy/<span class=\"hljs-symbol\">ubuntu:</span>vim</span></strong></code></pre><p>就可以了!</p><p>下次到了机房,在ubuntu系统中安装Docker,配置好加速器,启动Docker,<strong><span><code>docker login</code></span></strong>登陆自己的账号,然后直接<code>~$ <span><strong>docker pull sgy/ubuntu:vim</strong></span></code>就可以把你push到hub的已经配置好的环境的镜像给扒下来。做完实验,再push上去就ok了!</p><h2></h2><h2>Docker中安装gcc、gdb时遇到的问题</h2><p id=\"add-apt-repository-command-not-found\"><span><strong>+ add-apt-repository: command not found</strong></span></p><p>14.04系统解决办法:</p><pre class=\"prettyprint\"><code class=\"hljs lasso has-numbering\">apt<span class=\"hljs-attribute\">-get</span> install software<span class=\"hljs-attribute\">-properties</span><span class=\"hljs-attribute\">-common</span></code></pre><p> </p><p>older版本的系统:</p><pre class=\"prettyprint\"><code class=\"hljs lasso has-numbering\">apt<span class=\"hljs-attribute\">-get</span> install python<span class=\"hljs-attribute\">-software</span><span class=\"hljs-attribute\">-properties</span></code></pre><h4></h4><p id=\"docker中使用gdb无法进入断点无法调试\"><strong><span>+ Docker中使用gdb无法进入断点,无法调试</span></strong></p><p>加上<code>--privileged</code>参数</p><pre class=\"prettyprint\"><code class=\"hljs applescript has-numbering\">~$ docker <span class=\"hljs-command\">run</span> -<span class=\"hljs-keyword\">it</span> <span class=\"hljs-comment\">--privileged sgy/ubuntu:vim /bin/bash</span></code></pre>', '', '', '', '', '', '', '2019-04-25 21:42:58', '2018-11-25 21:05:05', '原文地址:https://blog.csdn.net/S_gy_Zetrov/article/details/78161154写在前面这篇博客适合谁?对于Docker并不了解,只是有一点模糊的感觉,觉得Docker可以当成虚拟机用之类的只是下载了Docker软件,对于怎么配置,怎么玩,第一步干什么,');
INSERT INTO `article` VALUES ('', '', 'MySQL常用命令语句', '<p><strong>常用的语句</strong></p><p>查询 select * from bbs where id=1;</p><p>增加 insert into bbs (name,data_year) values (\"jack\",\"1993-10-01\");</p><p>修改 update bbs set name=\"tom\",sex=1,age=18 where name=\"jack\";</p><p>删除 delete form bbs where id=2;</p><p>字符串替换: UPDATE `category` SET guid = REPLACE ( guid, \'articles\', \'article\' );</p><p> </p><p><strong>更多</strong></p><p>1.进入数据库:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"wp_keywordlink_affiliate\">mysql</span> -u root -p</li><li><span class=\"wp_keywordlink_affiliate\">mysql</span> -h localhost -u root -p database_name</li></ol></div><p>2.列出数据库:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"wp_keywordlink_affiliate\">show</span> databases;</li></ol></div><p>3.选择数据库:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\">use databases_name;</li></ol></div><p>4.列出数据表:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"wp_keywordlink_affiliate\">show</span> tables;</li></ol></div><p>5.显示表格列的属性:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"wp_keywordlink_affiliate\">show</span> columns <span class=\"keyword\">from</span> table_name;</li><li>describe table_name;</li></ol></div><p>6.导出整个数据库:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"wp_keywordlink_affiliate\">mysql</span>dump -u user_name -p database_name > /tmp/file_name</li></ol></div><p>例如:my<span class=\"wp_keywordlink_affiliate\">sql</span>dump -u root -p test_db > d:/test_db.<span class=\"wp_keywordlink_affiliate\">sql</span></p><p>7.导出一个表:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\">my<span class=\"wp_keywordlink_affiliate\">sql</span>dump -u user_name -p database_name table_name > /tmp/file_name</li></ol></div><p>例如:mysqldump -u root -p test_db table1 > d:/table1.sql</p><p>8.导出一个数据库结构:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\">mysqldump -u user_name -p -d <span class=\"comment\">--add--table database_name > file_name</span></li></ol></div><p>例如:mysqldump -u root -p -d --add-<span class=\"wp_keywordlink_affiliate\">drop</span>-table test_db > test_db.sql</p><p>9.导入数据库:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\">source file_name;</li><li>或</li><li class=\"alt\">mysql -u user_name -p database_name < file_name</li></ol></div><p>例如:</p><p>source /tmp/bbs.sql;</p><p>source d:/bbs.sql;</p><p>mysql -u root -p bbs < \"d:/bbs.sql\"</p><p>mysql -u root -p bbs < \"/tmp/bbs.sql\"</p><p>10.将文本文件导入数据表中(excel与之相同)</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">load</span> data infile <span class=\"string\">\"tables.txt\"</span> <span class=\"keyword\">into</span> <span class=\"keyword\">table</span> table_name;</li></ol></div><p>例如:</p><p>load data infile \"/tmp/bbs.txt\" into table bbs;</p><p>load data infile \"/tmp/bbs.xls\" into table bbs;</p><p>load data infile \"d:/bbs.txt\" into table bbs;</p><p>load data infile \"d:/bbs.xls\" into table bbs;</p><p>11.将数据表导出为文本文件(excel与之相同)</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\"><span class=\"wp_keywordlink_affiliate\">select</span></span> * <span class=\"keyword\">into</span> outfile <span class=\"string\">\"path_file_name\"</span> <span class=\"keyword\">from</span> table_name;</li></ol></div><p>例如:</p><p><span class=\"wp_keywordlink_affiliate\">select</span> * into outfile \"/tmp/bbs.txt\" from bbs;</p><p>select * into outfile \"/tmp/bbs.xls\" from bbs where id=1;</p><p>select * into outfile \"d:/bbs.txt\" from bbs;</p><p>select * into outfile \"d:/bbs.xls\" from bbs where id=1;</p><p>12.创建数据库时先判断数据库是否存在:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">create</span> <span class=\"keyword\">database</span> if <span class=\"op\">not</span> exists database_name;</li></ol></div><p>例如:create database if not exists bbs</p><p>13.创建数据库:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">create</span> <span class=\"keyword\">database</span> database_name;</li></ol></div><p>例如:create database bbs;</p><p>14.删除数据库:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">drop</span> <span class=\"keyword\">database</span> database_name;</li></ol></div><p>例如:drop database bbs;</p><p>15.创建数据表:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\">mysql> <span class=\"keyword\">create</span> <span class=\"keyword\">table</span> <table_name> ( <<span class=\"keyword\">column</span> 1 <span class=\"keyword\">name</span>> <col. 1 type> <col. 1 details>,<<span class=\"keyword\">column</span> 2 <span class=\"keyword\">name</span>> <col. 2 type> <col. 2 details>, ...);</col. 2 details></col. 2 type></col. 1 details></col. 1 type></table_name></li></ol></div><p>例如:create table (id int not null auto_increment primary key,name char(16) not null default \"jack\",date_year date not null);</p><p><span>16.删除数据表中数据:</span></p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">delete</span> <span class=\"keyword\">from</span> table_name;</li></ol></div><p>例如:</p><p>delete from bbs;</p><p>delete from bbs where id=2;</p><p>17.删除数据库中的数据表:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">drop</span> <span class=\"keyword\">table</span> table_name;</li></ol></div><p>例如:</p><p>drop table test_db;</p><p>rm -f database_name/table_name.* (linux下)</p><p>例如:</p><p>rm -rf bbs/accp.*</p><p><span>18.向数据库中添加数据:</span></p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\"><span class=\"wp_keywordlink_affiliate\">insert</span></span> <span class=\"keyword\">into</span> table_name <span class=\"keyword\">set</span> column_name1=value1,column_name2=value2;</li></ol></div><p>例如:<span class=\"wp_keywordlink_affiliate\">insert</span> into bbs set name=\"jack\",date_year=\"1993-10-01\";</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\"><span class=\"wp_keywordlink_affiliate\">insert</span></span> <span class=\"keyword\">into</span> table_name <span class=\"keyword\">values</span> (column1,column2,...);</li></ol></div><p>例如:insert into bbs (\"2\",\"jack\",\"1993-10-02\")</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">insert</span> <span class=\"keyword\">into</span> table_name (column_name1,column_name2,...) <span class=\"keyword\">values</span> (value1,value2);</li></ol></div><p>例如:insert into bbs (name,data_year) values (\"jack\",\"1993-10-01\");</p><p><span>19.查询数据表中的数据:</span></p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">select</span> * <span class=\"keyword\">from</span> table_name;</li></ol></div><p>例如:select * from bbs where id=1;</p><p><span>20.修改数据表中的数据:</span></p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">update</span> table_name <span class=\"keyword\">set</span> col_name=new_value <span class=\"keyword\">where</span> id=1;</li></ol></div><p>例如:update bbs set name=\"tom\",age=18 where name=\"jack\";</p><p>21.增加一个字段:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">alter</span> <span class=\"keyword\">table</span> table_name <span class=\"keyword\">add</span> <span class=\"keyword\">column</span> field_name datatype <span class=\"op\">not</span> <span class=\"op\">null</span> <span class=\"keyword\">default</span> <span class=\"string\">\"1\"</span>;</li></ol></div><p>例如:alter table bbs add column tel char(16) not null;</p><p>22.增加多个字段:(column可省略不写)</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">alter</span> <span class=\"keyword\">table</span> table_name <span class=\"keyword\">add</span> <span class=\"keyword\">column</span> filed_name1 datatype,<span class=\"keyword\">add</span> <span class=\"keyword\">column</span> filed_name2 datatype;</li></ol></div><p>例如:alter table bbs add column tel char(16) not null,add column address text;</p><p>23.删除一个字段:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">alter</span> <span class=\"keyword\">table</span> table_name <span class=\"keyword\">drop</span> field_name;</li></ol></div><p>例如:alter table bbs drop tel;</p><p>24.修改字段的数据类型:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">alter</span> <span class=\"keyword\">table</span> table_name <span class=\"keyword\">modify</span> id <span class=\"keyword\">int</span> unsigned;//修改列id的类型为<span class=\"keyword\">int</span> unsigned</li><li><span class=\"keyword\">alter</span> <span class=\"keyword\">table</span> table_name change id sid <span class=\"keyword\">int</span> unsigned;//修改列id的名字为sid,而且把属性修改为<span class=\"keyword\">int</span> unsigned</li></ol></div><p>25.修改一个字段的默认值:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">alter</span> <span class=\"keyword\">table</span> table_name <span class=\"keyword\">modify</span> column_name datatype <span class=\"op\">not</span> <span class=\"op\">null</span> <span class=\"keyword\">default</span> <span class=\"string\">\"\"</span>;</li></ol></div><p>例如:alter table test_db modify name char(16) default not null \"yourname\";</p><p>26.对表重新命名:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">alter</span> <span class=\"keyword\">table</span> table_name rename <span class=\"keyword\">as</span> new_table_name;</li></ol></div><p>例如:alter table bbs rename as bbs_table;</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\">rename <span class=\"keyword\">table</span> old_table_name <span class=\"keyword\">to</span> new_table_name;</li></ol></div><p>例如:rename table test_db to accp;</p><p>27.从已经有的表中复制表的结构:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">create</span> <span class=\"keyword\">table</span> table2 <span class=\"keyword\">select</span> * <span class=\"keyword\">from</span> table1 <span class=\"keyword\">where</span> 1<>1;</li></ol></div><p>例如:create table test_db select * from accp where 1<>1;</p><p>28.查询时间:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">select</span> now();</li></ol></div><p>29.查询当前用户:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">select</span> <span class=\"func\">user</span>();</li></ol></div><p>30.查询数据库版本:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">select</span> version();</li></ol></div><p>31.创建索引:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">alter</span> <span class=\"keyword\">table</span> table1 <span class=\"keyword\">add</span> <span class=\"keyword\">index</span> ind_id(id);</li><li><span class=\"keyword\">create</span> <span class=\"keyword\">index</span> ind_id <span class=\"keyword\">on</span> table1(id);</li><li class=\"alt\"><span class=\"keyword\">create</span> <span class=\"keyword\">unique</span> <span class=\"keyword\">index</span> ind_id <span class=\"keyword\">on</span> table1(id);//建立唯一性索引</li></ol></div><p>32.删除索引:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">drop</span> <span class=\"keyword\">index</span> idx_id <span class=\"keyword\">on</span> table1;</li><li><span class=\"keyword\">alter</span> <span class=\"keyword\">table</span> table1 <span class=\"keyword\">drop</span> <span class=\"keyword\">index</span> ind_id;</li></ol></div><p>33.联合字符或者多个列(将id与\":\"和列name和\"=\"连接)</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">select</span> concat(id,\':\',<span class=\"keyword\">name</span>,\'=\') <span class=\"keyword\">from</span> <span class=\"keyword\">table</span>;</li></ol></div><p>34.limit(选出10到20条)</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">select</span> * <span class=\"keyword\">from</span> bbs <span class=\"keyword\">order</span> <span class=\"keyword\">by</span> id limit 9,10;</li></ol></div><p>(从查询结果中列出第几到几条的记录)</p><p>35.增加一个管理员账号:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\"><span class=\"wp_keywordlink_affiliate\">grant</span></span> <span class=\"op\">all</span> <span class=\"keyword\">on</span> *.* <span class=\"keyword\">to</span> <span class=\"func\">user</span>@localhost identified <span class=\"keyword\">by</span> <span class=\"string\">\"password\"</span>;</li></ol></div><p>36.创建表是先判断表是否存在</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">create</span> <span class=\"keyword\">table</span> if <span class=\"op\">not</span> exists students(……);</li></ol></div><p>37.复制表:</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\">create</span> <span class=\"keyword\">table</span> table2 <span class=\"keyword\">select</span> * <span class=\"keyword\">from</span> table1;</li></ol></div><p>例如:create table test_db select * from accp;</p><p>38.授于用户远程访问mysql的权限</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\"><span class=\"keyword\"><span class=\"wp_keywordlink_affiliate\">grant</span></span> <span class=\"op\">all</span> <span class=\"keyword\">privileges</span> <span class=\"keyword\">on</span> *.* <span class=\"keyword\">to</span> <span class=\"string\">\"root\"</span>@<span class=\"string\">\"%\"</span> identified <span class=\"keyword\">by</span> <span class=\"string\">\"password\"</span> <span class=\"keyword\">with</span> <span class=\"keyword\">grant</span> <span class=\"keyword\">option</span>;</li></ol></div><p>或者是修改mysql数据库中的user表中的host字段</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\">use mysql;</li><li><span class=\"keyword\">select</span> <span class=\"func\">user</span>,host <span class=\"keyword\">from</span> <span class=\"func\">user</span>;</li><li class=\"alt\"><span class=\"keyword\">update</span> <span class=\"func\">user</span> <span class=\"keyword\">set</span> host=<span class=\"string\">\"%\"</span> <span class=\"keyword\">where</span> <span class=\"func\">user</span>=<span class=\"string\">\"user_name\"</span>;</li></ol></div><p>39.查看当前状态</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\">show status;</li></ol></div><p>40.查看当前连接的用户</p><div class=\"dp-highlighter\"><ol class=\"dp-sql\"><li class=\"alt\">show processlist;</li></ol></div><p>(如果是root用户,则查看全部的线程,得到的用户连接数同show status;里的 Threads_connected值是相同的)</p><p>参考:<a href=\"https://liuyanzhao.com/wp-content/themes/begin/inc/go.php?url=http://www.92csz.com/56/992.html\" target=\"_blank\" rel=\"noopener noreferrer\">http://www.92csz.com/56/992.html</a></p><p>本文地址:<a href=\"https://liuyanzhao.com/5306.html\" target=\"_blank\" rel=\"noopener noreferrer\">https://liuyanzhao.com/5306.html</a></p>', '', '', '', '', '', '', '2019-04-25 21:42:41', '2018-11-25 21:06:52', '常用的语句查询 select * from bbs where id=1;增加 insert into bbs (name,data_year) values (\"jack\",\"1993-10-01\");修改 update bbs set name=\"tom\",sex=1,age=18 where ');
INSERT INTO `article` VALUES ('', '', '测试数据', '', '', '', '', '', '', '', '2019-04-25 22:32:23', '2019-04-25 22:32:23', '测试数据');
category表:表中存放分类的基础信息
-- ----------------------------
-- Table structure for category
-- ----------------------------
DROP TABLE IF EXISTS `category`;
CREATE TABLE `category` (
`category_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`category_pid` int(11) DEFAULT NULL,
`category_name` varchar(50) DEFAULT NULL,
`category_description` varchar(255) DEFAULT NULL,
`category_order` int(11) unsigned DEFAULT '',
`category_icon` varchar(20) DEFAULT NULL,
PRIMARY KEY (`category_id`),
UNIQUE KEY `category_name` (`category_name`)
) ENGINE=InnoDB AUTO_INCREMENT=100000007 DEFAULT CHARSET=utf8;
表中的数据如下:
-- ----------------------------
-- Records of category
-- ----------------------------
INSERT INTO `category` VALUES ('', '', 'Java', 'Java语言', '', 'fa fa-coffee');
INSERT INTO `category` VALUES ('', '', 'Java基础', '', '', '');
INSERT INTO `category` VALUES ('', '', 'Core Java', '', '', '');
INSERT INTO `category` VALUES ('', '', '多线程并发编程', '', '', '');
INSERT INTO `category` VALUES ('', '', 'Sockets和IO', '', '', '');
INSERT INTO `category` VALUES ('', '', '设计模式和反射', '', '', '');
INSERT INTO `category` VALUES ('', '', 'JVM', '', '', '');
INSERT INTO `category` VALUES ('', '', 'JavaWeb', '', '', '');
INSERT INTO `category` VALUES ('', '', 'Java框架', '', '', '');
INSERT INTO `category` VALUES ('', '', '计算机科学', '', '', 'fa fa-cubes');
INSERT INTO `category` VALUES ('', '', '数据结构和算法', '', '', '');
INSERT INTO `category` VALUES ('', '', '操作系统', '', '', '');
INSERT INTO `category` VALUES ('', '', '数据库', '', '', '');
INSERT INTO `category` VALUES ('', '', '计算机网络', '', '', '');
INSERT INTO `category` VALUES ('', '', '其他技术', '', '', 'fa-snowflake-o fa');
INSERT INTO `category` VALUES ('', '', '消息服务', '', '', '');
INSERT INTO `category` VALUES ('', '', '缓存服务', '', '', '');
INSERT INTO `category` VALUES ('', '', 'Hello', '', '', '');
INSERT INTO `category` VALUES ('', '', '微服务', '', null, '');
INSERT INTO `category` VALUES ('', '', '搜索引擎', '', null, '');
INSERT INTO `category` VALUES ('', '', '权限框架', '', null, '');
INSERT INTO `category` VALUES ('', '', '开发利器', '', null, '');
Article类:article表对应的实体对象
package wbl_ssm_blog.entity; import lombok.Data; import java.io.Serializable;
import java.util.Date;
import java.util.List; /**
* @author liuyanzhao
*/
@Data
public class Article implements Serializable{ private static final long serialVersionUID = 5207865247400761539L; private Integer articleId; public Integer articleUserId; private String articleTitle; private Integer articleViewCount; private Integer articleCommentCount; private Integer articleLikeCount; private Date articleCreateTime; private Date articleUpdateTime; private Integer articleIsComment; private Integer articleStatus; private Integer articleOrder; private String articleContent; private String articleSummary; private User user; private List<Tag> tagList; private List<Category> categoryList; }
一、开启驼峰命名适配:
我们发现,数据库字段的名称使用驼峰命名规则,即user_name这种命名,而mybatis对应的java实体类使用userName这种命名方式,无法完成映射。所以,需要在mybatis-config配置文件中开启驼峰命名自动匹配。
<settings>
<!--允许 JDBC 支持自动生成主键-->
<setting name="useGeneratedKeys" value="false"/>
<!--是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典
Java 属性名 aColumn 的类似映射。 -->
<setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
这样配置之后就能把数据库中的user_name和实体类中的userName映射上了。
二.查询参数的传递
1.单个简单类型的查询参数,可以在ArticleMapper.xml中使用value来获取ArticleMapper.java中传递的参数。也可以使用@Param这种方法。
/**
* 我自己编写的一个类 用来测试mybatis语句的写法
*
* @return
*/
List<Article> findArticleBySome(int articleViewCount);
<!-- 测试 @Param语法 这种情况下不需要设置parameterType这个参数-->
<!-- 数据库字段使用驼峰命名article_user_id 实体的使用userName命名 mybatis-config.xml中配置了驼峰匹配原则 -->
<select id="findArticleBySome" resultType="Article">
select * from
<include refid="tb"/>
where article_view_count > #{value}
</select>
2.@Param的使用
在很多时候,我们需要传入多个查询参数,那mybatis怎么获取多个查询参数呢?在Article.mapper文件中,我们使用@Param(别名)为参数设置别名,在ArticleMapper.xml使用#{别名}来获取参数。
/**
* 我自己编写的一个类 用来测试mybatis语句的写法
* @param articleTitle 当查询需要多个参数的时候 就使用@Param语法 或者传入一个对象进去1
* @return
*/
List<Article> findArticleBySome(@Param("articleTitle") String articleTitle,@Param("articleViewCount") Integer articleViewCount);
!-- 测试 @Param语法 这种情况下不需要设置parameterType这个参数-->
<!-- 数据库字段使用驼峰命名article_user_id 实体的使用userName命名 mybatis-config.xml中配置了驼峰匹配原则 -->
<select id="findArticleBySome" resultType="Article">
select * from
<include refid="tb"/>
where article_title like '%${articleTitle}%' and article_view_count > #{articleViewCount}
</select>
3.使用对象的方式传递参数。在ArticleMapper.java的查询方法中,传入一个对象,在ArticleMapper.xml中,使用对象的属性来获取参数。
/**
* 需要传入多个参数时 新建一个查询对象 把查询参数赋值给这个查询对象 然后在sql语句中直接获取查询参数的值
* @param article
* @return
*/
List<Article> findArticleBySome01(Article article);
<!--测试使用对象的形式为查询语句赋值-->
<select id="findArticleBySome01" parameterType="Article" resultType="Article">
select
<include refid="Base_Column_List"/>
from
<include refid="tb"/>
where
article_title like '%${articleTitle}%' and article_view_count > #{articleViewCount}
</select>
4.查询时传入HashMap类型的参数。在查询时,我们会把查询条件拼成一个HashMap传到mybatis中。在mybatis中可以直接使用HashMap的key可以来获取对应value的值。
/**
* 根据查询条件查找文章列表
* @param criteria
* @return
*/
List<Article> findAll(HashMap<String,Object> criteria);
@Test
public void findAll()
{
HashMap<String,Object> cateria = new HashMap<String,Object>();
cateria.put("status",1);
cateria.put("userId",1);
// cateria.put("keywords","操作");
// cateria.put("categoryId",1);
cateria.put("tagId",1);
List<Article> articleList = articleMapper.findAll(cateria);
System.out.println();
// order by `article`.`article_order` DESC ,`article`.`article_id` DESC
}
<!--这里根据查询条件对文章进行查询-->
<select id="findAll" resultType="Article">
select
article.*
from
<include refid="tb"/>
<where>
<!--取HashMap里面的东西时 直接取key就行-->
<if test="status != null">article.article_status = #{status} AND </if>
<!--mybatis做模糊查询时 使用concat方法拼接%keywords%-->
<if test="keywords != null">article.article_title like concat(concat('%',#{keywords}),'%') AND </if>
<!--这里忘了加AND差点被恶心死-->
<if test="userId != null">article.article_user_id = #{userId} AND </if>
<!--分类-->
<if test="categoryId != null">
article.article_id in (
select article_category_ref.article_id from article_category_ref where
article_category_ref.category_id = #{category_id}
) AND
</if>
<!--标签-->
<if test="tagId != null">
article.article_id in (
select article_tag_ref.article_id from article_tag_ref where
article_tag_ref.tag_id = #{tagId}
) AND
</if>
</where>
1 = 1
order by article.article_order DESC ,article.article_id DESC
</select>
三、添加数据。
在添加数据时,如果需要把插入数据的主键返回,我们要设置useGeneratedKeys="true" 同时设置 keyProperty="articleId"。useGeneratedKeys="true"表示要获取插入记录的主键,keyProperty="articleId"表示把返回的主键赋值给articleId这个属性。
/**
* 添加文章
* @param article
* @return
*/
Integer insert(Article article);
@Test
public void insert()
{
Article article = new Article();
article.setArticleViewCount(10);
article.setArticleTitle("你好,这是测试文章的标题");
article.setArticleCommentCount(20);
article.setArticleContent("你好,这是测试文章的内容");
article.setArticleCreateTime(new Date());
article.setArticleUpdateTime(new Date());
article.setArticleIsComment(1);
article.setArticleLikeCount(20);
article.setArticleOrder(1);
article.setArticleStatus(1);
article.setArticleSummary("你好,这是测试文章的总结");
article.setArticleUserId(1); int result = articleMapper.insert(article);
System.out.println(article.getArticleId()); }
<!--这里要注意两个问题 第一个是获取插入的数据的主键 第二个是要在数据后面添加jdbcType 这样可以在插入空数据的时候自动转为空字符串-->
<insert id="insert" parameterType="Article" useGeneratedKeys="true" keyProperty="articleId">
insert into
<include refid="tb"/>
(article_user_id, article_title, article_content,article_summary, article_view_count, article_comment_count,
article_like_count, article_create_time,article_update_time, article_is_comment, article_status, article_order)
values
(
#{articleUserId,jdbcType=INTEGER },#{articleTitle,jdbcType=VARCHAR},#{articleContent,jdbcType=VARCHAR},
#{articleSummary,jdbcType=VARCHAR},#{articleViewCount,jdbcType=INTEGER},#{articleCommentCount,jdbcType=INTEGER},
#{articleLikeCount,jdbcType=INTEGER },#{articleCreateTime,jdbcType=TIMESTAMP },#{articleUpdateTime,jdbcType=TIMESTAMP},
#{articleIsComment,jdbcType=LONGVARCHAR},#{articleStatus,jdbcType=INTEGER },#{articleOrder,jdbcType=INTEGER }
)
</insert>
mybatis在插入数据后会返回一个数,这个数表示受影响的行数,通常是1.在执行SQL时MyBatis会自动通过对象中的属性给SQL中参数赋值,它会自动将Java类型转换成数据库的类型。而一旦传入的是null 程序就无法准确判断这个类型应该是什么(是Integer?是VARCHAR?还是别的?),就有可能将类型转换错误,从而报错。加入jdbcType正是为了解决这样的报错,需要针对这些可能为空的字段,手动指定其转换时用到的类型。
在添加了jdbcType的情况下,我们向数据库中插入一条空记录,运行成功。
数据库中多了一条空记录
如果我们不加jdbcType呢?插入依然成功,并没有像网上面说的那样会报错。这是个问题,待会儿解决一下。
四、删除数据。
删除数据时,mybatis会返回被删除数据的行数。
/**
* 根据articleId删除文章
* @param articleId
* @return 影响函数 是被删除的记录条数
*/
Integer deleteById(@Param(value = "articleId") Integer articleId);
<!-- mybatis删除数据时 会返回被删除的行数 -->
<delete id="deleteById" parameterType="Integer">
delete from
<include refid="tb"/>
where
article_id = #{articleId}
</delete>
五、更新数据
/**
* 更新文章
* @param article
* @return
*/
Integer update(Article article);
<!--<if>标签前面要加<set>标签 更新的字段后面要加jdbcType-->
<update id="update" parameterType="Article">
update
<include refid="tb"/>
<set>
<!--if的test条件里面取对象的值 不用带#{} 直接拿对象的属性值就可以-->
<if test="articleUserId != null and articleUserId != '' ">article_user_id = #{articleUserId,jdbcType=INTEGER },</if>
<if test="articleTitle != null and articleTitle != '' ">article_title = #{articleTitle,jdbcType=VARCHAR},</if>
<if test="articleContent != null and articleContent != '' ">article_content = #{articleContent,jdbcType=VARCHAR},</if>
<if test="articleSummary != null and articleSummary != '' ">article_summary = #{articleSummary,jdbcType=VARCHAR},</if>
<if test="articleViewCount != null and articleViewCount != '' ">article_view_count = #{articleViewCount,jdbcType=INTEGER},</if>
<if test="articleCommentCount != null and articleCommentCount != '' ">article_comment_count = #{articleCommentCount,jdbcType=INTEGER},</if>
<if test="articleLikeCount != null and articleLikeCount != '' ">article_like_count = #{articleLikeCount,jdbcType=INTEGER },</if>
<if test="articleCreateTime != null and articleCreateTime != '' ">article_create_time = #{articleCreateTime,jdbcType=TIMESTAMP },</if>
<if test="articleUpdateTime != null and articleUpdateTime != '' ">article_update_time = #{articleUpdateTime,jdbcType=TIMESTAMP},</if>
<if test="articleIsComment != null and articleIsComment != '' ">article_is_comment = #{articleIsComment,jdbcType=LONGVARCHAR},</if>
<if test="articleStatus != null and articleStatus != '' ">article_status = #{articleStatus,jdbcType=INTEGER },</if>
<if test="articleOrder != null and articleOrder != '' ">article_order = #{articleOrder,jdbcType=INTEGER },</if>
</set>
where article_id = #{articleId,jdbcType = INTEGER}
</update>
六、其他知识点
1.使用count()、max()、sum()等方法时,方法名和括号之间不能留空格。
<!--统计所有已经启用点文章数量-->
<select id="countArticle" parameterType="Integer" resultType="Integer">
select count(*) from
<include refid="tb"/>
where
article_status = #{status}
</select>
2.mybatis中的`>`要用 `>`来代替,`<`要用`<`来代替。
<!-- 获取下一篇文章-->
<select id="getAfterArticle" parameterType="Integer" resultType="Article">
select
<include refid="Base_Column_List"/>
from
<include refid="tb"/>
where
article_status = 1 and article_id > #{id}
order by article_id ASC
limit 1
</select>
<!--获取上一篇文章 这里居然不能写< 要写<-->
<!--
Mybatis中的sql语句中的“<”浩和“>”号要用转义字符“<”和”>“,否则会报错!
-->
<select id="getPreArticle" parameterType="Integer" resultType="Article">
select
<include refid="Base_Column_List"/>
from
<include refid="tb"/>
where
article_id < #{id} and article_status = 1
order by article_id DESC
limit 1
</select>
3.foreach标签的使用。
<!--获取多个分类的文章-->
<select id="findArticleByCategoryIds" resultType="Article">
select
*
from
article,article_category_ref
<where>
article_category_ref.article_id = article.article_id
and article_category_ref.category_id in
<foreach collection="ids" item="id" open="(" separator="," close=")" >
#{id}
</foreach>
and
article.article_status = 1
</where>
limit #{limit}
</select>