diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index 817acb6e..ebaf883b --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ !.mvn/wrapper/maven-wrapper.jar -target/ + ### STS ### .apt_generated .classpath @@ -13,6 +13,8 @@ target/ *.iws *.iml *.ipr +*.log +*.gz ### NetBeans ### nbproject/private/ @@ -22,5 +24,12 @@ dist/ nbdist/ .nb-gradle/ -#Mac -.DS_Store \ No newline at end of file + +.DS_Store + +target/ +.mvn/ +mvnw.cmd +mvnw +sens-log/ +/templates \ No newline at end of file diff --git a/ForestBlog/pom.xml b/ForestBlog/pom.xml index 19337842..aa830ec6 100755 --- a/ForestBlog/pom.xml +++ b/ForestBlog/pom.xml @@ -7,6 +7,17 @@ 1.0.0-SNAPSHOT ForestBlog Maven Webapp http://maven.apache.org + + + + 1.8 + 1.8 + UTF-8 + 4.3.19.RELEASE + 2.10.1 + + + @@ -44,63 +55,63 @@ org.springframework spring-core - 4.2.0.RELEASE + ${spring.version} org.springframework spring-beans - 4.2.0.RELEASE + ${spring.version} org.springframework spring-context - 4.2.0.RELEASE + ${spring.version} org.springframework spring-context-support - 4.2.0.RELEASE + ${spring.version} org.springframework spring-web - 4.2.0.RELEASE + ${spring.version} org.springframework spring-test - 4.2.0.RELEASE + ${spring.version} org.springframework spring-webmvc - 4.2.0.RELEASE + ${spring.version} org.springframework spring-tx - 4.2.0.RELEASE + ${spring.version} org.springframework spring-jdbc - 4.2.0.RELEASE + ${spring.version} org.springframework spring-aop - 4.2.0.RELEASE + ${spring.version} org.springframework spring-aspects - 4.2.0.RELEASE + ${spring.version} @@ -117,7 +128,7 @@ mysql mysql-connector-java - 5.1.30 + 8.0.11 @@ -125,23 +136,23 @@ druid 1.0.16 - + - log4j - log4j - 1.2.17 + ch.qos.logback + logback-classic + 1.1.2 - org.slf4j - slf4j-api - 1.7.21 + ch.qos.logback + logback-core + 1.1.2 - - commons-logging - commons-logging - 1.2 + org.logback-extensions + logback-ext-spring + 0.1.1 + junit @@ -154,7 +165,7 @@ commons-fileupload commons-fileupload - 1.2.2 + 1.3.3 commons-io @@ -196,7 +207,6 @@ 26.0-jre - com.github.pagehelper pagehelper @@ -205,7 +215,7 @@ com.alibaba fastjson - 1.2.47 + 1.2.72 @@ -215,13 +225,23 @@ 4.1.13 - + + com.fasterxml.jackson.core + jackson-databind + ${jackson-databind-version} + + + com.fasterxml.jackson.core + jackson-core + ${jackson-databind-version} + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson-databind-version} + - - 1.8 - 1.8 - UTF-8 - + diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/admin/AdminController.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/admin/AdminController.java index 560b30a8..90882f82 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/admin/AdminController.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/admin/AdminController.java @@ -1,8 +1,10 @@ package com.liuyanzhao.ssm.blog.controller.admin; +import com.liuyanzhao.ssm.blog.dto.JsonResult; import com.liuyanzhao.ssm.blog.entity.Article; import com.liuyanzhao.ssm.blog.entity.Comment; import com.liuyanzhao.ssm.blog.entity.User; +import com.liuyanzhao.ssm.blog.enums.UserRole; import com.liuyanzhao.ssm.blog.service.ArticleService; import com.liuyanzhao.ssm.blog.service.CommentService; import com.liuyanzhao.ssm.blog.service.UserService; @@ -10,9 +12,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; @@ -45,13 +49,20 @@ public class AdminController { * @return */ @RequestMapping("/admin") - public String index(Model model) { + public String index(Model model, HttpSession session) { + User user = (User) session.getAttribute("user"); + Integer userId = null; + if (!UserRole.ADMIN.getValue().equals(user.getUserRole())) { + // 用户查询自己的文章, 管理员查询所有的 + userId = user.getUserId(); + } //文章列表 - List
articleList = articleService.listRecentArticle(5); - model.addAttribute("articleList",articleList); + List
articleList = articleService.listRecentArticle(userId, 5); + model.addAttribute("articleList", articleList); + //评论列表 - List commentList = commentService.listRecentComment(5); - model.addAttribute("commentList",commentList); + List commentList = commentService.listRecentComment(userId, 5); + model.addAttribute("commentList", commentList); return "Admin/index"; } @@ -61,10 +72,22 @@ public String index(Model model) { * @return */ @RequestMapping("/login") - public String loginPage() { + public String + loginPage() { return "Admin/login"; } + + /** + * 登录页面显示 + * + * @return + */ + @RequestMapping("/register") + public String registerPage() { + return "Admin/register"; + } + /** * 登录验证 * @@ -72,29 +95,32 @@ public String loginPage() { * @param response * @return */ - @RequestMapping(value = "/loginVerify",method = RequestMethod.POST) + @RequestMapping(value = "/loginVerify", method = RequestMethod.POST, produces = {"text/plain;charset=UTF-8"}) @ResponseBody - public String loginVerify(HttpServletRequest request, HttpServletResponse response) { + public String loginVerify(HttpServletRequest request, HttpServletResponse response) { Map map = new HashMap(); String username = request.getParameter("username"); String password = request.getParameter("password"); String rememberme = request.getParameter("rememberme"); User user = userService.getUserByNameOrEmail(username); - if(user==null) { - map.put("code",0); - map.put("msg","用户名无效!"); - } else if(!user.getUserPass().equals(password)) { - map.put("code",0); - map.put("msg","密码错误!"); + if (user == null) { + map.put("code", 0); + map.put("msg", "用户名无效!"); + } else if (!user.getUserPass().equals(password)) { + map.put("code", 0); + map.put("msg", "密码错误!"); + } else if (user.getUserStatus() == 0) { + map.put("code", 0); + map.put("msg", "账号已禁用!"); } else { //登录成功 - map.put("code",1); - map.put("msg",""); + map.put("code", 1); + map.put("msg", ""); //添加session request.getSession().setAttribute("user", user); //添加cookie - if(rememberme!=null) { + if (rememberme != null) { //创建两个Cookie对象 Cookie nameCookie = new Cookie("username", username); //设置Cookie的有效期为3天 @@ -113,6 +139,47 @@ public String loginVerify(HttpServletRequest request, HttpServletResponse respon return result; } + /** + * 登录验证 + * + * @param request + * @return + */ + @RequestMapping(value = "/registerSubmit", method = RequestMethod.POST) + @ResponseBody + public JsonResult registerSubmit(HttpServletRequest request) { + String username = request.getParameter("username"); + String nickname = request.getParameter("nickname"); + String email = request.getParameter("email"); + String password = request.getParameter("password"); + User checkUserName = userService.getUserByName(username); + if (checkUserName != null) { + return new JsonResult().fail("用户名已存在"); + } + User checkEmail = userService.getUserByEmail(username); + if (checkEmail != null) { + return new JsonResult().fail("电子邮箱已存在"); + } + + // 添加用户 + User user = new User(); + user.setUserAvatar("/img/avatar/avatar.png"); + user.setUserName(username); + user.setUserNickname(nickname); + user.setUserPass(password); + user.setUserEmail(email); + user.setUserStatus(1); + user.setArticleCount(0); + user.setUserRole(UserRole.USER.getValue()); + try { + userService.insertUser(user); + } catch (Exception e) { + e.printStackTrace(); + return new JsonResult().fail("系统异常"); + } + return new JsonResult().ok("注册成功"); + } + /** * 退出登录 * @@ -120,11 +187,62 @@ public String loginVerify(HttpServletRequest request, HttpServletResponse respon * @return */ @RequestMapping(value = "/admin/logout") - public String logout(HttpSession session) { + public String logout(HttpSession session) { session.removeAttribute("user"); session.invalidate(); return "redirect:/login"; } + /** + * 基本信息页面显示 + * + * @return + */ + @RequestMapping(value = "/admin/profile") + public ModelAndView userProfileView(HttpSession session) { + + ModelAndView modelAndView = new ModelAndView(); + User sessionUser = (User) session.getAttribute("user"); + User user = userService.getUserById(sessionUser.getUserId()); + modelAndView.addObject("user", user); + + modelAndView.setViewName("Admin/User/profile"); + return modelAndView; + } + + /** + * 编辑个人信息页面显示 + * + * @param session + * @return + */ + @RequestMapping(value = "/admin/profile/edit") + public ModelAndView editUserView(HttpSession session) { + ModelAndView modelAndView = new ModelAndView(); + User loginUser = (User) session.getAttribute("user"); + User user = userService.getUserById(loginUser.getUserId()); + modelAndView.addObject("user", user); + + modelAndView.setViewName("Admin/User/editProfile"); + return modelAndView; + } + + + /** + * 编辑用户提交 + * + * @param user + * @return + */ + @RequestMapping(value = "/admin/profile/save", method = RequestMethod.POST) + public String saveProfile(User user, HttpSession session) { + User dbUser = (User) session.getAttribute("user"); + + user.setUserId(dbUser.getUserId()); + userService.updateUser(user); + return "redirect:/admin/profile"; + } + + } diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/admin/BackArticleController.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/admin/BackArticleController.java index 86941d64..55d82cca 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/admin/BackArticleController.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/admin/BackArticleController.java @@ -3,7 +3,9 @@ import cn.hutool.http.HtmlUtil; import com.github.pagehelper.PageInfo; import com.liuyanzhao.ssm.blog.dto.ArticleParam; +import com.liuyanzhao.ssm.blog.dto.JsonResult; import com.liuyanzhao.ssm.blog.entity.Article; +import com.liuyanzhao.ssm.blog.enums.UserRole; import com.liuyanzhao.ssm.blog.service.ArticleService; import com.liuyanzhao.ssm.blog.service.CategoryService; import com.liuyanzhao.ssm.blog.service.TagService; @@ -14,16 +16,14 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpSession; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Objects; /** @@ -49,7 +49,8 @@ public class BackArticleController { @RequestMapping(value = "") public String index(@RequestParam(required = false, defaultValue = "1") Integer pageIndex, @RequestParam(required = false, defaultValue = "10") Integer pageSize, - @RequestParam(required = false) String status, Model model) { + @RequestParam(required = false) String status, Model model, + HttpSession session) { HashMap criteria = new HashMap<>(1); if (status == null) { model.addAttribute("pageUrlPrefix", "/admin/article?pageIndex"); @@ -57,6 +58,12 @@ public String index(@RequestParam(required = false, defaultValue = "1") Integer criteria.put("status", status); model.addAttribute("pageUrlPrefix", "/admin/article?status=" + status + "&pageIndex"); } + + User user = (User) session.getAttribute("user"); + if (!UserRole.ADMIN.getValue().equals(user.getUserRole())) { + // 用户查询自己的文章, 管理员查询所有的 + criteria.put("userId", user.getUserId()); + } PageInfo
articlePageInfo = articleService.pageArticle(pageIndex, pageSize, criteria); model.addAttribute("pageInfo", articlePageInfo); return "Admin/Article/index"; @@ -101,6 +108,7 @@ public String insertArticleSubmit(HttpSession session, ArticleParam articleParam } else { article.setArticleSummary(summaryText); } + article.setArticleThumbnail(articleParam.getArticleThumbnail()); article.setArticleContent(articleParam.getArticleContent()); article.setArticleStatus(articleParam.getArticleStatus()); //填充分类 @@ -132,8 +140,17 @@ public String insertArticleSubmit(HttpSession session, ArticleParam articleParam * * @param id 文章ID */ - @RequestMapping(value = "/delete/{id}") - public void deleteArticle(@PathVariable("id") Integer id) { + @RequestMapping(value = "/delete/{id}", method = RequestMethod.POST) + public void deleteArticle(@PathVariable("id") Integer id, HttpSession session) { + Article dbArticle = articleService.getArticleByStatusAndId(null, id); + if (dbArticle == null) { + return; + } + User user = (User) session.getAttribute("user"); + // 如果不是管理员,访问其他用户的数据,则跳转403 + if (!Objects.equals(dbArticle.getArticleUserId(), user.getUserId()) && !Objects.equals(user.getUserRole(), UserRole.ADMIN.getValue())) { + return; + } articleService.deleteArticle(id); } @@ -145,22 +162,27 @@ public void deleteArticle(@PathVariable("id") Integer id) { * @return */ @RequestMapping(value = "/edit/{id}") - public ModelAndView editArticleView(@PathVariable("id") Integer id) { - ModelAndView modelAndView = new ModelAndView(); + public String editArticleView(@PathVariable("id") Integer id, Model model, HttpSession session) { Article article = articleService.getArticleByStatusAndId(null, id); - modelAndView.addObject("article", article); + if (article == null) { + return "redirect:/404"; + } + User user = (User) session.getAttribute("user"); + // 如果不是管理员,访问其他用户的数据,则跳转403 + if (!Objects.equals(article.getArticleUserId(), user.getUserId()) && !Objects.equals(user.getUserRole(), UserRole.ADMIN.getValue())) { + return "redirect:/403"; + } + model.addAttribute("article", article); List categoryList = categoryService.listCategory(); - modelAndView.addObject("categoryList", categoryList); + model.addAttribute("categoryList", categoryList); List tagList = tagService.listTag(); - modelAndView.addObject("tagList", tagList); - + model.addAttribute("tagList", tagList); - modelAndView.setViewName("Admin/Article/edit"); - return modelAndView; + return "Admin/Article/edit"; } @@ -171,8 +193,18 @@ public ModelAndView editArticleView(@PathVariable("id") Integer id) { * @return */ @RequestMapping(value = "/editSubmit", method = RequestMethod.POST) - public String editArticleSubmit(ArticleParam articleParam) { + public String editArticleSubmit(ArticleParam articleParam, HttpSession session) { + Article dbArticle = articleService.getArticleByStatusAndId(null, articleParam.getArticleId()); + if (dbArticle == null) { + return "redirect:/404"; + } + User user = (User) session.getAttribute("user"); + // 如果不是管理员,访问其他用户的数据,则跳转403 + if (!Objects.equals(dbArticle.getArticleUserId(), user.getUserId()) && !Objects.equals(user.getUserRole(), UserRole.ADMIN.getValue())) { + return "redirect:/403"; + } Article article = new Article(); + article.setArticleThumbnail(articleParam.getArticleThumbnail()); article.setArticleId(articleParam.getArticleId()); article.setArticleTitle(articleParam.getArticleTitle()); article.setArticleContent(articleParam.getArticleContent()); @@ -208,6 +240,55 @@ public String editArticleSubmit(ArticleParam articleParam) { return "redirect:/admin/article"; } + /** + * 后台添加文章提交操作 + * + * @param articleParam + * @return + */ + @RequestMapping(value = "/insertDraftSubmit", method = RequestMethod.POST) + public String insertDraftSubmit(HttpSession session, ArticleParam articleParam) { + Article article = new Article(); + //用户ID + User user = (User) session.getAttribute("user"); + if (user != null) { + article.setArticleUserId(user.getUserId()); + } + article.setArticleTitle(articleParam.getArticleTitle()); + //文章摘要 + int summaryLength = 150; + String summaryText = HtmlUtil.cleanHtmlTag(articleParam.getArticleContent()); + if (summaryText.length() > summaryLength) { + String summary = summaryText.substring(0, summaryLength); + article.setArticleSummary(summary); + } else { + article.setArticleSummary(summaryText); + } + article.setArticleThumbnail(articleParam.getArticleThumbnail()); + article.setArticleContent(articleParam.getArticleContent()); + article.setArticleStatus(articleParam.getArticleStatus()); + //填充分类 + List categoryList = new ArrayList<>(); + if (articleParam.getArticleChildCategoryId() != null) { + categoryList.add(new Category(articleParam.getArticleParentCategoryId())); + } + if (articleParam.getArticleChildCategoryId() != null) { + categoryList.add(new Category(articleParam.getArticleChildCategoryId())); + } + article.setCategoryList(categoryList); + //填充标签 + List tagList = new ArrayList<>(); + if (articleParam.getArticleTagIds() != null) { + for (int i = 0; i < articleParam.getArticleTagIds().size(); i++) { + Tag tag = new Tag(articleParam.getArticleTagIds().get(i)); + tagList.add(tag); + } + } + article.setTagList(tagList); + + articleService.insertArticle(article); + return "redirect:/admin"; + } } diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/admin/BackCommentController.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/admin/BackCommentController.java index b0a05111..ebf5e59a 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/admin/BackCommentController.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/admin/BackCommentController.java @@ -1,10 +1,15 @@ package com.liuyanzhao.ssm.blog.controller.admin; +import cn.hutool.http.HtmlUtil; import com.github.pagehelper.PageInfo; +import com.liuyanzhao.ssm.blog.dto.JsonResult; import com.liuyanzhao.ssm.blog.entity.Article; import com.liuyanzhao.ssm.blog.entity.Comment; +import com.liuyanzhao.ssm.blog.entity.User; import com.liuyanzhao.ssm.blog.enums.ArticleStatus; +import com.liuyanzhao.ssm.blog.enums.Role; +import com.liuyanzhao.ssm.blog.enums.UserRole; import com.liuyanzhao.ssm.blog.util.MyUtils; import com.liuyanzhao.ssm.blog.service.ArticleService; import com.liuyanzhao.ssm.blog.service.CommentService; @@ -14,8 +19,8 @@ import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; -import java.util.Date; -import java.util.List; +import javax.servlet.http.HttpSession; +import java.util.*; /** @@ -33,18 +38,47 @@ public class BackCommentController { /** * 评论页面 + * 我发送的评论 * * @param pageIndex 页码 * @param pageSize 页大小 * @return modelAndView */ @RequestMapping(value = "") - public String commentListView(@RequestParam(required = false, defaultValue = "1") Integer pageIndex, - @RequestParam(required = false, defaultValue = "10") Integer pageSize, - Model model) { - PageInfo commentPageInfo = commentService.listCommentByPage(pageIndex, pageSize); + public String commentList(@RequestParam(required = false, defaultValue = "1") Integer pageIndex, + @RequestParam(required = false, defaultValue = "10") Integer pageSize, + HttpSession session, + Model model) { + User user = (User) session.getAttribute("user"); + HashMap criteria = new HashMap<>(); + if (!UserRole.ADMIN.getValue().equals(user.getUserRole())) { + // 用户查询自己的文章, 管理员查询所有的 + criteria.put("userId", user.getUserId()); + } + PageInfo commentPageInfo = commentService.listCommentByPage(pageIndex, pageSize, criteria); + model.addAttribute("pageInfo", commentPageInfo); + model.addAttribute("pageUrlPrefix", "/admin/comment?pageIndex"); + return "Admin/Comment/index"; + } + + + /** + * 评论页面 + * 我收到的评论 + * + * @param pageIndex 页码 + * @param pageSize 页大小 + * @return modelAndView + */ + @RequestMapping(value = "/receive") + public String myReceiveComment(@RequestParam(required = false, defaultValue = "1") Integer pageIndex, + @RequestParam(required = false, defaultValue = "10") Integer pageSize, + HttpSession session, + Model model) { + User user = (User) session.getAttribute("user"); + PageInfo commentPageInfo = commentService.listReceiveCommentByPage(pageIndex, pageSize, user.getUserId()); model.addAttribute("pageInfo", commentPageInfo); - model.addAttribute("pageUrlPrefix","/admin/comment?pageIndex"); + model.addAttribute("pageUrlPrefix", "/admin/comment?pageIndex"); return "Admin/Comment/index"; } @@ -55,15 +89,21 @@ public String commentListView(@RequestParam(required = false, defaultValue = "1" * @param request * @param comment */ - @RequestMapping(value = "/insert", method = {RequestMethod.POST}) + @RequestMapping(value = "/insert", method = {RequestMethod.POST}, produces = {"text/plain;charset=UTF-8"}) @ResponseBody - public void insertComment(HttpServletRequest request, Comment comment) { + public void insertComment(HttpServletRequest request, Comment comment, HttpSession session) { + User user = (User) session.getAttribute("user"); + Article article = articleService.getArticleByStatusAndId(null, comment.getCommentArticleId()); + if (article == null) { + return; + } + //添加评论 + comment.setCommentUserId(user.getUserId()); comment.setCommentIp(MyUtils.getIpAddr(request)); comment.setCommentCreateTime(new Date()); commentService.insertComment(comment); //更新文章的评论数 - Article article = articleService.getArticleByStatusAndId(null, comment.getCommentArticleId()); articleService.updateCommentCount(article.getArticleId()); } @@ -73,8 +113,13 @@ public void insertComment(HttpServletRequest request, Comment comment) { * @param id 批量ID */ @RequestMapping(value = "/delete/{id}") - public void deleteComment(@PathVariable("id") Integer id) { + public void deleteComment(@PathVariable("id") Integer id, HttpSession session) { Comment comment = commentService.getCommentById(id); + User user = (User) session.getAttribute("user"); + // 如果不是管理员,访问其他用户的数据,没有权限 + if (!Objects.equals(user.getUserRole(), UserRole.ADMIN.getValue()) && !Objects.equals(comment.getCommentUserId(), user.getUserId())) { + return; + } //删除评论 commentService.deleteComment(id); //删除其子评论 @@ -94,7 +139,12 @@ public void deleteComment(@PathVariable("id") Integer id) { * @return */ @RequestMapping(value = "/edit/{id}") - public String editCommentView(@PathVariable("id") Integer id, Model model) { + public String editCommentView(@PathVariable("id") Integer id, Model model, HttpSession session) { + // 没有权限操作,只有管理员可以操作 + User user = (User) session.getAttribute("user"); + if (!Objects.equals(user.getUserRole(), UserRole.ADMIN.getValue())) { + return "redirect:/403"; + } Comment comment = commentService.getCommentById(id); model.addAttribute("comment", comment); return "Admin/Comment/edit"; @@ -108,7 +158,12 @@ public String editCommentView(@PathVariable("id") Integer id, Model model) { * @return */ @RequestMapping(value = "/editSubmit", method = RequestMethod.POST) - public String editCommentSubmit(Comment comment) { + public String editCommentSubmit(Comment comment, HttpSession session) { + User user = (User) session.getAttribute("user"); + // 没有权限操作,只有管理员可以操作 + if (!Objects.equals(user.getUserRole(), UserRole.ADMIN.getValue())) { + return "redirect:/403"; + } commentService.updateComment(comment); return "redirect:/admin/comment"; } @@ -135,14 +190,27 @@ public String replyCommentView(@PathVariable("id") Integer id, Model model) { * @return */ @RequestMapping(value = "/replySubmit", method = RequestMethod.POST) - public String replyCommentSubmit(HttpServletRequest request, Comment comment) { + public String replyCommentSubmit(HttpServletRequest request, Comment comment, HttpSession session) { //文章评论数+1 Article article = articleService.getArticleByStatusAndId(ArticleStatus.PUBLISH.getValue(), comment.getCommentArticleId()); + if (article == null) { + return "redirect:/404"; + } + User user = (User) session.getAttribute("user"); + comment.setCommentContent(HtmlUtil.escape(comment.getCommentContent())); + comment.setCommentAuthorName(user.getUserNickname()); + comment.setCommentAuthorEmail(user.getUserEmail()); + comment.setCommentAuthorUrl(user.getUserUrl()); article.setArticleCommentCount(article.getArticleCommentCount() + 1); articleService.updateArticle(article); //添加评论 comment.setCommentCreateTime(new Date()); comment.setCommentIp(MyUtils.getIpAddr(request)); + if (Objects.equals(user.getUserId(), article.getArticleUserId())) { + comment.setCommentRole(Role.OWNER.getValue()); + } else { + comment.setCommentRole(Role.VISITOR.getValue()); + } commentService.insertComment(comment); return "redirect:/admin/comment"; } diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/admin/BackUserController.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/admin/BackUserController.java index cb35ec6e..852b438a 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/admin/BackUserController.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/admin/BackUserController.java @@ -2,6 +2,7 @@ import com.liuyanzhao.ssm.blog.entity.User; +import com.liuyanzhao.ssm.blog.enums.UserRole; import com.liuyanzhao.ssm.blog.service.UserService; import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; @@ -33,11 +34,11 @@ public class BackUserController { * @return */ @RequestMapping(value = "") - public ModelAndView userList() { + public ModelAndView userList() { ModelAndView modelandview = new ModelAndView(); List userList = userService.listUser(); - modelandview.addObject("userList",userList); + modelandview.addObject("userList", userList); modelandview.setViewName("Admin/User/index"); return modelandview; @@ -50,7 +51,7 @@ public ModelAndView userList() { * @return */ @RequestMapping(value = "/insert") - public ModelAndView insertUserView() { + public ModelAndView insertUserView() { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("Admin/User/insert"); return modelAndView; @@ -62,22 +63,22 @@ public ModelAndView insertUserView() { * @param request * @return */ - @RequestMapping(value = "/checkUserName",method = RequestMethod.POST) + @RequestMapping(value = "/checkUserName", method = RequestMethod.POST, produces = {"text/plain;charset=UTF-8"}) @ResponseBody - public String checkUserName(HttpServletRequest request) { + public String checkUserName(HttpServletRequest request) { Map map = new HashMap(); String username = request.getParameter("username"); User user = userService.getUserByName(username); int id = Integer.valueOf(request.getParameter("id")); //用户名已存在,但不是当前用户(编辑用户的时候,不提示) - if(user!=null) { - if(user.getUserId()!=id) { + if (user != null) { + if (user.getUserId() != id) { map.put("code", 1); map.put("msg", "用户名已存在!"); } } else { - map.put("code",0); - map.put("msg",""); + map.put("code", 0); + map.put("msg", ""); } String result = new JSONObject(map).toString(); return result; @@ -89,22 +90,22 @@ public String checkUserName(HttpServletRequest request) { * @param request * @return */ - @RequestMapping(value = "/checkUserEmail",method = RequestMethod.POST) + @RequestMapping(value = "/checkUserEmail", method = RequestMethod.POST, produces = {"text/plain;charset=UTF-8"}) @ResponseBody - public String checkUserEmail(HttpServletRequest request) { + public String checkUserEmail(HttpServletRequest request) { Map map = new HashMap(); String email = request.getParameter("email"); User user = userService.getUserByEmail(email); int id = Integer.valueOf(request.getParameter("id")); //用户名已存在,但不是当前用户(编辑用户的时候,不提示) - if(user!=null) { - if(user.getUserId()!=id) { + if (user != null) { + if (user.getUserId() != id) { map.put("code", 1); map.put("msg", "电子邮箱已存在!"); } } else { - map.put("code",0); - map.put("msg",""); + map.put("code", 0); + map.put("msg", ""); } String result = new JSONObject(map).toString(); return result; @@ -117,13 +118,14 @@ public String checkUserEmail(HttpServletRequest request) { * @param user * @return */ - @RequestMapping(value = "/insertSubmit",method = RequestMethod.POST) - public String insertUserSubmit(User user) { + @RequestMapping(value = "/insertSubmit", method = RequestMethod.POST) + public String insertUserSubmit(User user) { User user2 = userService.getUserByName(user.getUserName()); User user3 = userService.getUserByEmail(user.getUserEmail()); - if(user2==null&&user3==null) { + if (user2 == null && user3 == null) { user.setUserRegisterTime(new Date()); user.setUserStatus(1); + user.setUserRole(UserRole.USER.getValue()); userService.insertUser(user); } return "redirect:/admin/user"; @@ -136,7 +138,7 @@ public String insertUserSubmit(User user) { * @return */ @RequestMapping(value = "/delete/{id}") - public String deleteUser(@PathVariable("id") Integer id) { + public String deleteUser(@PathVariable("id") Integer id) { userService.deleteUser(id); return "redirect:/admin/user"; } @@ -148,11 +150,11 @@ public String deleteUser(@PathVariable("id") Integer id) { * @return */ @RequestMapping(value = "/edit/{id}") - public ModelAndView editUserView(@PathVariable("id") Integer id) { + public ModelAndView editUserView(@PathVariable("id") Integer id) { ModelAndView modelAndView = new ModelAndView(); - User user = userService.getUserById(id); - modelAndView.addObject("user",user); + User user = userService.getUserById(id); + modelAndView.addObject("user", user); modelAndView.setViewName("Admin/User/edit"); return modelAndView; @@ -165,26 +167,10 @@ public ModelAndView editUserView(@PathVariable("id") Integer id) { * @param user * @return */ - @RequestMapping(value = "/editSubmit",method = RequestMethod.POST) - public String editUserSubmit(User user) { + @RequestMapping(value = "/editSubmit", method = RequestMethod.POST) + public String editUserSubmit(User user) { userService.updateUser(user); return "redirect:/admin/user"; } - /** - * 基本信息页面显示 - * - * @return - */ - @RequestMapping(value = "/profile") - public ModelAndView userProfileView(HttpSession session) { - - ModelAndView modelAndView = new ModelAndView(); - User sessionUser = (User) session.getAttribute("user"); - User user = userService.getUserById(sessionUser.getUserId()); - modelAndView.addObject("user",user); - - modelAndView.setViewName("Admin/User/profile"); - return modelAndView; - } } diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/admin/UploadFileController.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/admin/UploadFileController.java index 28d046fa..7431f47f 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/admin/UploadFileController.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/admin/UploadFileController.java @@ -22,7 +22,8 @@ public class UploadFileController { /** * 文件保存目录,物理路径 */ - public final String rootPath = "/Users/liuyanzhao/Documents/uploads"; +// public final String rootPath = "/Users/liuyanzhao/Documents/uploads"; + public final String rootPath = "D:\\uploads"; public final String allowSuffix = ".bmp.jpg.jpeg.png.gif.pdf.doc.zip.rar.gz"; diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/ArticleController.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/ArticleController.java index 1b9c8bbf..38c78fff 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/ArticleController.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/ArticleController.java @@ -104,7 +104,7 @@ public String getArticleDetailPage(@PathVariable("articleId") Integer articleId, * @param id 文章ID * @return 点赞量数量 */ - @RequestMapping(value = "/article/like/{id}", method = {RequestMethod.POST}) + @RequestMapping(value = "/article/like/{id}", method = {RequestMethod.POST}, produces = {"text/plain;charset=UTF-8"}) @ResponseBody public String increaseLikeCount(@PathVariable("id") Integer id) { Article article = articleService.getArticleByStatusAndId(ArticleStatus.PUBLISH.getValue(), id); @@ -120,7 +120,7 @@ public String increaseLikeCount(@PathVariable("id") Integer id) { * @param id 文章ID * @return 访问量数量 */ - @RequestMapping(value = "/article/view/{id}", method = {RequestMethod.POST}) + @RequestMapping(value = "/article/view/{id}", method = {RequestMethod.POST}, produces = {"text/plain;charset=UTF-8"}) @ResponseBody public String increaseViewCount(@PathVariable("id") Integer id) { Article article = articleService.getArticleByStatusAndId(ArticleStatus.PUBLISH.getValue(), id); diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/CategoryController.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/CategoryController.java index 06d598d0..310c361e 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/CategoryController.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/CategoryController.java @@ -75,7 +75,7 @@ public String getArticleListByCategory(@PathVariable("cateId") Integer cateId, //获得热评文章 List
mostCommentArticleList = articleService.listArticleByCommentCount(8); model.addAttribute("mostCommentArticleList", mostCommentArticleList); - model.addAttribute("pageUrlPrefix", "/category/"+pageIndex+"?pageIndex"); + model.addAttribute("pageUrlPrefix", "/category/"+cateId+"?pageIndex"); return "Home/Page/articleListByCategory"; } diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/CommentController.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/CommentController.java index 53cd0b4f..9e76d921 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/CommentController.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/CommentController.java @@ -4,6 +4,7 @@ import com.liuyanzhao.ssm.blog.dto.JsonResult; import com.liuyanzhao.ssm.blog.entity.Article; import com.liuyanzhao.ssm.blog.entity.Comment; +import com.liuyanzhao.ssm.blog.entity.User; import com.liuyanzhao.ssm.blog.enums.ArticleStatus; import com.liuyanzhao.ssm.blog.enums.Role; import com.liuyanzhao.ssm.blog.service.ArticleService; @@ -13,11 +14,12 @@ import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; import java.util.Date; +import java.util.Objects; /** * @author 言曌 @@ -34,32 +36,43 @@ public class CommentController { private ArticleService articleService; /** - * '添加评论 + * 添加评论 * * @param request * @param comment */ @RequestMapping(value = "/comment", method = {RequestMethod.POST}) - public JsonResult insertComment(HttpServletRequest request, Comment comment) { + public JsonResult insertComment(HttpServletRequest request, Comment comment, HttpSession session) { + User user = (User) session.getAttribute("user"); + if (user == null) { + return new JsonResult().fail("请先登录"); + } + Article article = articleService.getArticleByStatusAndId(ArticleStatus.PUBLISH.getValue(), comment.getCommentArticleId()); + if (article == null) { + return new JsonResult().fail("文章不存在"); + } + + //添加评论 + comment.setCommentUserId(user.getUserId()); comment.setCommentCreateTime(new Date()); comment.setCommentIp(MyUtils.getIpAddr(request)); - if (request.getSession().getAttribute("user") != null) { - comment.setCommentRole(Role.ADMIN.getValue()); + if (Objects.equals(user.getUserId(), article.getArticleUserId())) { + comment.setCommentRole(Role.OWNER.getValue()); } else { comment.setCommentRole(Role.VISITOR.getValue()); } - comment.setCommentAuthorAvatar(MyUtils.getGravatar(comment.getCommentAuthorEmail())); + comment.setCommentAuthorAvatar(user.getUserAvatar()); //过滤字符,防止XSS攻击 comment.setCommentContent(HtmlUtil.escape(comment.getCommentContent())); - comment.setCommentAuthorName(HtmlUtil.escape(comment.getCommentAuthorName())); - comment.setCommentAuthorEmail(HtmlUtil.escape(comment.getCommentAuthorEmail())); - comment.setCommentAuthorUrl(HtmlUtil.escape(comment.getCommentAuthorUrl())); + + comment.setCommentAuthorName(user.getUserNickname()); + comment.setCommentAuthorEmail(user.getUserEmail()); + comment.setCommentAuthorUrl(user.getUserUrl()); try { commentService.insertComment(comment); //更新文章的评论数 - Article article = articleService.getArticleByStatusAndId(ArticleStatus.PUBLISH.getValue(), comment.getCommentArticleId()); articleService.updateCommentCount(article.getArticleId()); } catch (Exception e) { e.printStackTrace(); diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/IndexController.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/IndexController.java index 474ab333..aec1455c 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/IndexController.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/IndexController.java @@ -62,7 +62,7 @@ public String index(@RequestParam(required = false, defaultValue = "1") Integer List allTagList = tagService.listTag(); model.addAttribute("allTagList", allTagList); //最新评论 - List recentCommentList = commentService.listRecentComment(10); + List recentCommentList = commentService.listRecentComment(null, 10); model.addAttribute("recentCommentList", recentCommentList); model.addAttribute("pageUrlPrefix", "/article?pageIndex"); return "Home/index"; @@ -91,9 +91,9 @@ public String search( List
mostCommentArticleList = articleService.listArticleByCommentCount(8); model.addAttribute("mostCommentArticleList", mostCommentArticleList); //最新评论 - List recentCommentList = commentService.listRecentComment(10); + List recentCommentList = commentService.listRecentComment(null, 10); model.addAttribute("recentCommentList", recentCommentList); - model.addAttribute("pageUrlPrefix", "/search?pageIndex"); + model.addAttribute("pageUrlPrefix", "/search?keywords=" + keywords + "&pageIndex"); return "Home/Page/search"; } @@ -103,6 +103,13 @@ public String NotFound(@RequestParam(required = false) String message, Model mod return "Home/Error/404"; } + + @RequestMapping("/403") + public String Page403(@RequestParam(required = false) String message, Model model) { + model.addAttribute("message", message); + return "Home/Error/403"; + } + @RequestMapping("/500") public String ServerError(@RequestParam(required = false) String message, Model model) { model.addAttribute("message", message); diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/LinkController.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/LinkController.java index d14e35c2..f0f07f0f 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/LinkController.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/LinkController.java @@ -38,7 +38,7 @@ public String applyLinkView(Model model) { } - @RequestMapping(value = "/applyLinkSubmit",method = {RequestMethod.POST}) + @RequestMapping(value = "/applyLinkSubmit",method = {RequestMethod.POST}, produces = {"text/plain;charset=UTF-8"}) @ResponseBody public void applyLinkSubmit(Link link) { link.setLinkStatus(LinkStatus.HIDDEN.getValue()); diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/TagController.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/TagController.java index 43e0c5f0..cfa3c3c4 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/TagController.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/controller/home/TagController.java @@ -64,7 +64,8 @@ public String getArticleListByTag(@PathVariable("tagId") Integer tagId, //获得热评文章 List
mostCommentArticleList = articleService.listArticleByCommentCount(8); model.addAttribute("mostCommentArticleList", mostCommentArticleList); - model.addAttribute("pageUrlPrefix", "/tag?pageIndex"); + model.addAttribute("pageUrlPrefix", "/tag/"+tagId+"?pageIndex"); + return "Home/Page/articleListByTag"; } diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/dto/ArticleParam.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/dto/ArticleParam.java index af19e332..835e09f9 100644 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/dto/ArticleParam.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/dto/ArticleParam.java @@ -25,6 +25,8 @@ public class ArticleParam { private Integer articleStatus; + private String articleThumbnail; + private List articleTagIds; } diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/dto/Response.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/dto/Response.java deleted file mode 100644 index afb761fa..00000000 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/dto/Response.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.liuyanzhao.ssm.blog.dto; - -/** - * @author 言曌 - * @date 2018/9/2 下午8:34 - */ - -public class Response { - - private Boolean success; - - private String message; - - private T data; - - public Response() { - } - - public Response(Boolean success) { - this.success = success; - } - - public Response(Boolean success, String message) { - this.success = success; - this.message = message; - } - - public Response(Boolean success, String message, T data) { - this.success = success; - this.message = message; - this.data = data; - } - - public static Response yes() { - return new Response(true, "操作成功"); - } - - public static Response yes(T data) { - return new Response(true, "操作成功", data); - } - - - public static Response no() { - return new Response(false, "操作失败"); - } - - public static Response no(String message) { - return new Response(false, message); - } - - public Boolean isSuccess() { - return success; - } - - public void setSuccess(Boolean success) { - this.success = success; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - public T getData() { - return data; - } - - public void setData(T data) { - this.data = data; - } -} diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/entity/Article.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/entity/Article.java index 4dd53fca..7b852a30 100644 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/entity/Article.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/entity/Article.java @@ -40,6 +40,8 @@ public class Article implements Serializable{ private String articleSummary; + private String articleThumbnail; + private User user; private List tagList; diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/entity/Category.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/entity/Category.java index 6244d429..904f7676 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/entity/Category.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/entity/Category.java @@ -48,6 +48,8 @@ public Category(Integer categoryId, String categoryName) { public Category(Integer categoryId) { this.categoryId = categoryId; } + + public Category() {} /** * 未分类 @@ -59,4 +61,4 @@ public static Category Default() { } -} \ No newline at end of file +} diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/entity/Comment.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/entity/Comment.java index 6f856947..31f17798 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/entity/Comment.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/entity/Comment.java @@ -7,10 +7,11 @@ /** * 文章评论 + * * @author liuyanzhao */ @Data -public class Comment implements Serializable{ +public class Comment implements Serializable { private static final long serialVersionUID = -1038897351672911219L; private Integer commentId; @@ -42,9 +43,13 @@ public class Comment implements Serializable{ */ private Integer commentRole; + /** + * 评论用户ID + */ + private Integer commentUserId; + /** * 非数据库字段 */ private Article article; - } \ No newline at end of file diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/entity/User.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/entity/User.java index 88544a9d..326ef3c4 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/entity/User.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/entity/User.java @@ -33,6 +33,11 @@ public class User implements Serializable{ private Integer userStatus; + /** + * 用户角色:admin/user + */ + private String userRole; + /** * 文章数量(不是数据库字段) */ diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/enums/Role.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/enums/Role.java index d4096a6b..a95b01ff 100644 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/enums/Role.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/enums/Role.java @@ -7,8 +7,8 @@ public enum Role { - ADMIN(1, "博主"), - VISITOR(0, "访客"); + OWNER(1, "博主"), + VISITOR(0, "其他用户"); private Integer value; diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/enums/UserRole.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/enums/UserRole.java new file mode 100644 index 00000000..3de0d5a0 --- /dev/null +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/enums/UserRole.java @@ -0,0 +1,40 @@ +package com.liuyanzhao.ssm.blog.enums; + +/** + * 角色枚举 + * + * @author 言曌 + * @date 2021/2/25 2:31 下午 + */ + +public enum UserRole { + + ADMIN("admin", "管理员"), + + USER("user", "用户"); + + private String value; + + private String message; + + UserRole(String value, String message) { + this.value = value; + this.message = message; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/interceptor/AdminInterceptor.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/interceptor/AdminInterceptor.java new file mode 100755 index 00000000..7c151209 --- /dev/null +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/interceptor/AdminInterceptor.java @@ -0,0 +1,45 @@ +package com.liuyanzhao.ssm.blog.interceptor; + +import com.liuyanzhao.ssm.blog.entity.User; +import com.liuyanzhao.ssm.blog.enums.UserRole; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Objects; + + +/** + * @author liuyanzhao + */ +@Component +public class AdminInterceptor extends HandlerInterceptorAdapter { + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws IOException { + //这里可以根据session的用户来判断角色的权限,根据权限来转发不同的页面 + User user = (User) request.getSession().getAttribute("user"); + if (user == null) { + response.sendRedirect("/login"); + return false; + } + if (!Objects.equals(user.getUserRole(), UserRole.ADMIN.getValue())) { + return false; + } + return true; + } + + @Override + public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) { + + } + + @Override + public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) { + + } +} + diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/interceptor/SecurityInterceptor.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/interceptor/LoginInterceptor.java similarity index 89% rename from ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/interceptor/SecurityInterceptor.java rename to ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/interceptor/LoginInterceptor.java index 6b03d4aa..1201d869 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/interceptor/SecurityInterceptor.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/interceptor/LoginInterceptor.java @@ -1,7 +1,6 @@ package com.liuyanzhao.ssm.blog.interceptor; import org.springframework.stereotype.Component; -import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; @@ -14,7 +13,7 @@ * @author liuyanzhao */ @Component -public class SecurityInterceptor extends HandlerInterceptorAdapter { +public class LoginInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws IOException { @@ -23,6 +22,7 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons response.sendRedirect("/login"); return false; } + return true; } diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/mapper/ArticleMapper.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/mapper/ArticleMapper.java index ed86da42..c6930dae 100644 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/mapper/ArticleMapper.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/mapper/ArticleMapper.java @@ -23,6 +23,14 @@ public interface ArticleMapper { */ Integer deleteById(Integer articleId); + /** + * 根据用户ID删除 + * + * @param userId 用户ID + * @return 影响函数 + */ + Integer deleteByUserId(Integer userId); + /** * 添加文章 * @@ -49,6 +57,7 @@ public interface ArticleMapper { /** * 文章归档 + * * @return */ List
listAllNotWithContent(); @@ -86,7 +95,7 @@ public interface ArticleMapper { * 根据id查询用户信息 * * @param status 状态 - * @param id 文章ID + * @param id 文章ID * @return 文章 */ Article getArticleByStatusAndId(@Param(value = "status") Integer status, @Param(value = "id") Integer id); @@ -101,8 +110,8 @@ public interface ArticleMapper { */ @Deprecated List
pageArticle(@Param(value = "status") Integer status, - @Param(value = "pageIndex") Integer pageIndex, - @Param(value = "pageSize") Integer pageSize); + @Param(value = "pageIndex") Integer pageIndex, + @Param(value = "pageSize") Integer pageSize); /** @@ -140,13 +149,12 @@ List
pageArticle(@Param(value = "status") Integer status, /** * 热评文章 * - * @param limit 查询数量 + * @param limit 查询数量 * @return 文章列表 */ List
listArticleByCommentCount(@Param(value = "limit") Integer limit); - /** * 更新文章的评论数 * @@ -195,7 +203,7 @@ List
findArticleByCategoryIds(@Param("categoryIds") List categ * @param limit 查询数量 * @return 列表 */ - List
listArticleByLimit(Integer limit); + List
listArticleByLimit(@Param("userId") Integer userId, @Param("limit") Integer limit); /** * 批量删除文章 @@ -204,4 +212,12 @@ List
findArticleByCategoryIds(@Param("categoryIds") List categ * @return 影响行数 */ Integer deleteBatch(@Param("ids") List ids); + + /** + * 获得一个用户的文章id集合 + * + * @param userId + * @return + */ + List listArticleIdsByUserId(Integer userId); } \ No newline at end of file diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/mapper/CommentMapper.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/mapper/CommentMapper.java index da94bca6..e8976b35 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/mapper/CommentMapper.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/mapper/CommentMapper.java @@ -4,6 +4,7 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; +import java.util.HashMap; import java.util.List; @Mapper @@ -55,7 +56,16 @@ public interface CommentMapper { * * @return 列表 */ - List listComment(); + List listComment(HashMap criteria); + + + /** + * 获得某个用户收到的评论 + * + * @return 列表 + */ + List getReceiveComment(List articleIds); + /** * 统计评论数 @@ -70,7 +80,8 @@ public interface CommentMapper { * @param limit 查询数量 * @return 列表 */ - List listRecentComment(@Param(value = "limit") Integer limit); + List listRecentComment(@Param(value = "userId") Integer userId, + @Param(value = "limit") Integer limit); /** * 获得评论的子评论 @@ -79,4 +90,22 @@ public interface CommentMapper { * @return 列表 */ List listChildComment(@Param(value = "id") Integer id); + + + /** + * 根据用户ID删除 + * + * @param userId 用户ID + * @return 影响函数 + */ + Integer deleteByUserId(Integer userId); + + + /** + * 根据文章ID删除 + * + * @param articleId 文章ID + * @return 影响函数 + */ + Integer deleteByArticleId(Integer articleId); } \ No newline at end of file diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/ArticleService.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/ArticleService.java index a4495d8e..f489bdce 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/ArticleService.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/ArticleService.java @@ -67,7 +67,7 @@ public interface ArticleService { * @param limit 查询数量 * @return 列表 */ - List
listRecentArticle(Integer limit); + List
listRecentArticle(Integer userId, Integer limit); /** diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/CommentService.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/CommentService.java index f66caae7..17613a83 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/CommentService.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/CommentService.java @@ -4,6 +4,7 @@ import com.liuyanzhao.ssm.blog.entity.Comment; import org.springframework.stereotype.Service; +import java.util.HashMap; import java.util.List; @@ -47,14 +48,20 @@ public interface CommentService { */ PageInfo listCommentByPage( Integer pageIndex, - Integer pageSize); + Integer pageSize, + HashMap criteria); /** - * 获得评论列表 + * 获得某个用户收到的评论 * + * @param pageIndex 第几页开始 + * @param pageSize 一页显示数量 * @return 列表 */ - List listComment(); + PageInfo listReceiveCommentByPage( + Integer pageIndex, + Integer pageSize, + Integer userId); /** @@ -84,7 +91,7 @@ PageInfo listCommentByPage( * @param limit 查询数量 * @return 列表 */ - List listRecentComment(Integer limit); + List listRecentComment(Integer userId, Integer limit); /** * 获得评论的子评论 diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/ArticleServiceImpl.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/ArticleServiceImpl.java index 5899dfc6..b1c5e94e 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/ArticleServiceImpl.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/ArticleServiceImpl.java @@ -1,17 +1,17 @@ package com.liuyanzhao.ssm.blog.service.impl; +import cn.hutool.core.util.RandomUtil; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import com.liuyanzhao.ssm.blog.enums.ArticleCommentStatus; +import com.liuyanzhao.ssm.blog.mapper.*; import com.liuyanzhao.ssm.blog.service.ArticleService; import com.liuyanzhao.ssm.blog.entity.*; -import com.liuyanzhao.ssm.blog.mapper.ArticleCategoryRefMapper; -import com.liuyanzhao.ssm.blog.mapper.ArticleMapper; -import com.liuyanzhao.ssm.blog.mapper.ArticleTagRefMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; import java.util.ArrayList; import java.util.Date; @@ -28,15 +28,21 @@ @Slf4j public class ArticleServiceImpl implements ArticleService { - @Autowired(required = false) + @Autowired private ArticleMapper articleMapper; - @Autowired(required = false) + @Autowired private ArticleCategoryRefMapper articleCategoryRefMapper; - @Autowired(required = false) + @Autowired private ArticleTagRefMapper articleTagRefMapper; + @Autowired + private UserMapper userMapper; + + @Autowired + private CommentMapper commentMapper; + @Override public Integer countArticle(Integer status) { Integer count = 0; @@ -98,8 +104,8 @@ public List
listArticle(HashMap criteria) { } @Override - public List
listRecentArticle(Integer limit) { - return articleMapper.listArticleByLimit(limit); + public List
listRecentArticle(Integer userId, Integer limit) { + return articleMapper.listArticleByLimit(userId, limit); } @Override @@ -140,8 +146,15 @@ public void deleteArticleBatch(List ids) { } @Override + @Transactional(rollbackFor = Exception.class) public void deleteArticle(Integer id) { articleMapper.deleteById(id); + // 删除分类关联 + articleCategoryRefMapper.deleteByArticleId(id); + // 删除标签管理 + articleTagRefMapper.deleteByArticleId(id); + // 删除评论 + commentMapper.deleteByArticleId(id); } @@ -159,6 +172,8 @@ public PageInfo
pageArticle(Integer pageIndex, categoryList.add(Category.Default()); } articleList.get(i).setCategoryList(categoryList); + + articleList.get(i).setUser(userMapper.getUserById(articleList.get(i).getArticleUserId())); // //封装TagList // List tagList = articleTagRefMapper.listTagByArticleId(articleList.get(i).getArticleId()); // articleList.get(i).setTagList(tagList); @@ -216,6 +231,10 @@ public void insertArticle(Article article) { article.setArticleLikeCount(0); article.setArticleCommentCount(0); article.setArticleOrder(1); + if (StringUtils.isEmpty(article.getArticleThumbnail())) { + article.setArticleThumbnail("/img/thumbnail/random/img_" + RandomUtil.randomNumbers(1) + ".jpg"); + } + articleMapper.insert(article); //添加分类和文章关联 for (int i = 0; i < article.getCategoryList().size(); i++) { diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/CategoryServiceImpl.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/CategoryServiceImpl.java index 2efb300a..6cd6cd70 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/CategoryServiceImpl.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/CategoryServiceImpl.java @@ -23,10 +23,10 @@ @Slf4j public class CategoryServiceImpl implements CategoryService { - @Autowired(required = false) + @Autowired private CategoryMapper categoryMapper; - @Autowired(required = false) + @Autowired private ArticleCategoryRefMapper articleCategoryRefMapper; @Override diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/CommentServiceImpl.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/CommentServiceImpl.java index 89a66b2b..20f9015d 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/CommentServiceImpl.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/CommentServiceImpl.java @@ -13,6 +13,8 @@ import org.springframework.stereotype.Service; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; /** @@ -23,10 +25,10 @@ @Slf4j public class CommentServiceImpl implements CommentService { - @Autowired(required = false) + @Autowired private CommentMapper commentMapper; - @Autowired(required = false) + @Autowired private ArticleMapper articleMapper; @Override @@ -64,11 +66,11 @@ public Comment getCommentById(Integer id) { } @Override - public PageInfo listCommentByPage(Integer pageIndex, Integer pageSize) { + public PageInfo listCommentByPage(Integer pageIndex, Integer pageSize, HashMap criteria) { PageHelper.startPage(pageIndex, pageSize); List commentList = null; try { - commentList = commentMapper.listComment(); + commentList = commentMapper.listComment(criteria); for (int i = 0; i < commentList.size(); i++) { Article article = articleMapper.getArticleByStatusAndId(ArticleStatus.PUBLISH.getValue(), commentList.get(i).getCommentArticleId()); commentList.get(i).setArticle(article); @@ -81,15 +83,23 @@ public PageInfo listCommentByPage(Integer pageIndex, Integer pageSize) } @Override - public List listComment() { - List commentList = null; + public PageInfo listReceiveCommentByPage(Integer pageIndex, Integer pageSize, Integer userId) { + PageHelper.startPage(pageIndex, pageSize); + List commentList = new ArrayList<>(); try { - commentList = commentMapper.listComment(); + List articleIds = articleMapper.listArticleIdsByUserId(userId); + if (articleIds != null && articleIds.size() > 0) { + commentList = commentMapper.getReceiveComment(articleIds); + for (int i = 0; i < commentList.size(); i++) { + Article article = articleMapper.getArticleByStatusAndId(ArticleStatus.PUBLISH.getValue(), commentList.get(i).getCommentArticleId()); + commentList.get(i).setArticle(article); + } + } } catch (Exception e) { e.printStackTrace(); - log.error("获得评论列表失败:cause:{}", e); + log.error("分页获得评论失败,pageIndex:{}, pageSize:{}, cause:{}", pageIndex, pageSize, e); } - return commentList; + return new PageInfo<>(commentList); } @Override @@ -125,10 +135,10 @@ public Integer countComment() { } @Override - public List listRecentComment(Integer limit) { + public List listRecentComment(Integer userId, Integer limit) { List commentList = null; try { - commentList = commentMapper.listRecentComment(limit); + commentList = commentMapper.listRecentComment(userId, limit); for (int i = 0; i < commentList.size(); i++) { Article article = articleMapper.getArticleByStatusAndId(ArticleStatus.PUBLISH.getValue(), commentList.get(i).getCommentArticleId()); commentList.get(i).setArticle(article); diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/LinkServiceImpl.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/LinkServiceImpl.java index e26f8f4d..ef0ad3eb 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/LinkServiceImpl.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/LinkServiceImpl.java @@ -17,7 +17,7 @@ @Service public class LinkServiceImpl implements LinkService { - @Autowired(required = false) + @Autowired private LinkMapper linkMapper; @Override diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/MenuServiceImpl.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/MenuServiceImpl.java index 9ccf80e9..baf2cbdf 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/MenuServiceImpl.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/MenuServiceImpl.java @@ -16,7 +16,7 @@ public class MenuServiceImpl implements MenuService { - @Autowired(required = false) + @Autowired private MenuMapper menuMapper; @Override diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/NoticeServiceImpl.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/NoticeServiceImpl.java index d7155a6f..f460fcb0 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/NoticeServiceImpl.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/NoticeServiceImpl.java @@ -15,7 +15,7 @@ @Service public class NoticeServiceImpl implements NoticeService { - @Autowired(required = false) + @Autowired private NoticeMapper noticeMapper; @Override diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/OptionsServiceImpl.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/OptionsServiceImpl.java index 9bda54e9..de8892f1 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/OptionsServiceImpl.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/OptionsServiceImpl.java @@ -17,7 +17,7 @@ public class OptionsServiceImpl implements OptionsService { - @Autowired(required = false) + @Autowired private OptionsMapper optionsMapper; @Override diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/PageServiceImpl.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/PageServiceImpl.java index 41dbb1a1..2d2b8827 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/PageServiceImpl.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/PageServiceImpl.java @@ -16,7 +16,7 @@ @Service public class PageServiceImpl implements PageService { - @Autowired(required = false) + @Autowired private PageMapper pageMapper; @Override diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/TagServiceImpl.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/TagServiceImpl.java index eb6f38a9..6cc1bd6c 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/TagServiceImpl.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/TagServiceImpl.java @@ -20,10 +20,10 @@ @Slf4j public class TagServiceImpl implements TagService { - @Autowired(required = false) + @Autowired private TagMapper tagMapper; - @Autowired(required = false) + @Autowired private ArticleTagRefMapper articleTagRefMapper; @Override diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/UserServiceImpl.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/UserServiceImpl.java index 1513ac30..5fe8e2f5 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/UserServiceImpl.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/service/impl/UserServiceImpl.java @@ -1,11 +1,14 @@ package com.liuyanzhao.ssm.blog.service.impl; import com.liuyanzhao.ssm.blog.mapper.ArticleMapper; +import com.liuyanzhao.ssm.blog.mapper.CommentMapper; import com.liuyanzhao.ssm.blog.mapper.UserMapper; import com.liuyanzhao.ssm.blog.entity.User; +import com.liuyanzhao.ssm.blog.service.ArticleService; import com.liuyanzhao.ssm.blog.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.Date; import java.util.List; @@ -19,12 +22,17 @@ @Service public class UserServiceImpl implements UserService { - @Autowired(required = false) + @Autowired private UserMapper userMapper; - @Autowired(required = false) + @Autowired private ArticleMapper articleMapper; + @Autowired + private ArticleService articleService; + @Autowired + private CommentMapper commentMapper; + @Override public List listUser() { List userList = userMapper.listUser(); @@ -46,8 +54,19 @@ public void updateUser(User user) { } @Override + @Transactional(rollbackFor = Exception.class) public void deleteUser(Integer id) { + // 删除用户 userMapper.deleteById(id); + // 删除评论 + commentMapper.deleteByUserId(id); + // 删除文章 + List articleIds = articleMapper.listArticleIdsByUserId(id); + if (articleIds != null && articleIds.size() > 0) { + for (Integer articleId : articleIds) { + articleService.deleteArticle(articleId); + } + } } @Override diff --git a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/util/MyUtils.java b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/util/MyUtils.java index 122f1e21..49d7704e 100755 --- a/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/util/MyUtils.java +++ b/ForestBlog/src/main/java/com/liuyanzhao/ssm/blog/util/MyUtils.java @@ -88,4 +88,5 @@ public static String getGravatar(String email) { return avatar; } + } \ No newline at end of file diff --git a/ForestBlog/src/main/resources/db.properties b/ForestBlog/src/main/resources/db.properties index 212c7ce0..cec7879c 100755 --- a/ForestBlog/src/main/resources/db.properties +++ b/ForestBlog/src/main/resources/db.properties @@ -1,4 +1,5 @@ #MySQL -mysql.url=jdbc:mysql://127.0.0.1:3306/forest_blog?useUnicode=true&characterEncoding=utf8 +mysql.driver=com.mysql.cj.jdbc.Driver +mysql.url=jdbc:mysql://127.0.0.1:3306/forest_blog?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC mysql.username=root mysql.password=123456 diff --git a/ForestBlog/src/main/resources/log4j.properties b/ForestBlog/src/main/resources/log4j.properties deleted file mode 100644 index c6b73837..00000000 --- a/ForestBlog/src/main/resources/log4j.properties +++ /dev/null @@ -1,11 +0,0 @@ -# Configure logging for testing: optionally with log file - log4j.rootLogger=WARN, stdout, logfile - -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n - -log4j.appender.logfile=org.apache.log4j.FileAppender -log4j.appender.logfile.File=target/ForestBlog.log -log4j.appender.logfile.layout=org.apache.log4j.PatternLayout -log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n \ No newline at end of file diff --git a/ForestBlog/src/main/resources/logback.xml b/ForestBlog/src/main/resources/logback.xml new file mode 100644 index 00000000..a7df509b --- /dev/null +++ b/ForestBlog/src/main/resources/logback.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + %date{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread]%logger{56}.%method:%L -%msg%n + + + + + + ${log.base}/${log.moduleName}.log + + + + ${log.base}/archive/${log.moduleName}_all_%d{yyyy-MM-dd}.%i.log.zip + + + + ${log.max.size} + + + + + %date{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread]%logger{56}.%method:%L -%msg%n + + + + \ No newline at end of file diff --git a/ForestBlog/src/main/resources/mapper/ArticleMapper.xml b/ForestBlog/src/main/resources/mapper/ArticleMapper.xml index df5ed01b..aa2dda6d 100644 --- a/ForestBlog/src/main/resources/mapper/ArticleMapper.xml +++ b/ForestBlog/src/main/resources/mapper/ArticleMapper.xml @@ -15,19 +15,20 @@ + article article_id, 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 + article_update_time, article_is_comment, article_status, article_order, article_thumbnail SELECT - article.article_id, article.article_user_id, article.article_title, + distinct article.article_id, article.article_user_id, article.article_title, article.article_view_count, article.article_comment_count, article.article_like_count, article.article_create_time, article.article_update_time, article.article_is_comment, article.article_status, article.article_order, - article.article_summary + article.article_summary, article.article_thumbnail FROM article, article_category_ref article.article_status = 1 AND @@ -318,10 +324,24 @@ SELECT article_id, article_user_id, article_title, article_view_count, article_comment_count, article_like_count, article_create_time, - article_update_time, article_is_comment, article_status, article_order + article_update_time, article_is_comment, article_status, article_order, article_thumbnail FROM + + + article_user_id = #{userId} + + ORDER BY article_id DESC - LIMIT #{value} + LIMIT #{limit} + + + diff --git a/ForestBlog/src/main/resources/mapper/CommentMapper.xml b/ForestBlog/src/main/resources/mapper/CommentMapper.xml index 9d9d0382..266d4d17 100755 --- a/ForestBlog/src/main/resources/mapper/CommentMapper.xml +++ b/ForestBlog/src/main/resources/mapper/CommentMapper.xml @@ -15,6 +15,7 @@ + comment @@ -22,7 +23,7 @@ comment_id, comment_pid, comment_pname, comment_article_id, comment_author_name, comment_author_email, comment_author_url, comment_author_avatar, comment_content, comment_agent, - comment_ip,comment_create_time, comment_role + comment_ip,comment_create_time, comment_role,comment_user_id + + @@ -107,7 +141,11 @@ FROM - WHERE comment_role = 0 + + + comment_user_id = #{userId} + + ORDER BY comment_id DESC LIMIT #{limit} diff --git a/ForestBlog/src/main/resources/mapper/UserMapper.xml b/ForestBlog/src/main/resources/mapper/UserMapper.xml index 07c53bf3..aab46d88 100755 --- a/ForestBlog/src/main/resources/mapper/UserMapper.xml +++ b/ForestBlog/src/main/resources/mapper/UserMapper.xml @@ -13,13 +13,14 @@ + user user_id, user_name, user_pass, user_nickname, user_email, user_url, user_avatar, - user_last_login_ip, user_register_time, user_last_login_time, user_status + user_last_login_ip, user_register_time, user_last_login_time, user_status, user_role + + +
@@ -107,16 +121,45 @@
-
- 温馨提示: - 1、插入代码前,可以点击 代码高亮,将代码转成HTML格式 -
+ + + + + +<%@ include file="../Public/framework.jsp" %> diff --git a/ForestBlog/src/main/webapp/WEB-INF/view/Admin/User/index.jsp b/ForestBlog/src/main/webapp/WEB-INF/view/Admin/User/index.jsp index 2a5b6fbd..d01182e3 100755 --- a/ForestBlog/src/main/webapp/WEB-INF/view/Admin/User/index.jsp +++ b/ForestBlog/src/main/webapp/WEB-INF/view/Admin/User/index.jsp @@ -1,100 +1,100 @@ -<%@ page language="java" contentType="text/html; charset=UTF-8" - pageEncoding="UTF-8" %> -<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> -<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> -<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> -<%@ taglib prefix="rapid" uri="http://www.rapid-framework.org.cn/rapid" %> - - - - 用户列表 - - - - - - -
- - 首页 - 用户列表 - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
用户名昵称电子邮件文章状态操作ID
- - ${u.userName} - - ${u.userNickname} - - ${u.userEmail} - - ${u.articleCount} - - - - 禁用 - - - 正常 - - - - 编辑 - 删除 - - ${u.userId} -
- -
- - - - -<%@ include file="../Public/framework.jsp"%> +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %> +<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %> +<%@ taglib prefix="rapid" uri="http://www.rapid-framework.org.cn/rapid" %> + + + - 用户列表 + + + + + + +
+ + 首页 + 用户列表 + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
用户名昵称电子邮件文章状态操作ID
+ + ${u.userName} + + ${u.userNickname} + + ${u.userEmail} + + ${u.articleCount} + + + + 禁用 + + + 正常 + + + + 编辑 + 删除 + + ${u.userId} +
+ +
+ + + + +<%@ include file="../Public/framework.jsp"%> diff --git a/ForestBlog/src/main/webapp/WEB-INF/view/Admin/User/insert.jsp b/ForestBlog/src/main/webapp/WEB-INF/view/Admin/User/insert.jsp index 3a4ce0a1..bd1e429c 100755 --- a/ForestBlog/src/main/webapp/WEB-INF/view/Admin/User/insert.jsp +++ b/ForestBlog/src/main/webapp/WEB-INF/view/Admin/User/insert.jsp @@ -6,7 +6,7 @@ <%@ taglib prefix="rapid" uri="http://www.rapid-framework.org.cn/rapid" %> - - 编辑用户 + - 添加用户 + + + + + +
+

${options.optionSiteTitle}

+
+

+ +

+

+ +

+

+ +

+

+ +

+

+ +

+

+ +

+
+ +

← 返回到风吟博客 | 登录

+ +
+ + +
+ + + + + + diff --git a/ForestBlog/src/main/webapp/WEB-INF/view/Home/Error/403.jsp b/ForestBlog/src/main/webapp/WEB-INF/view/Home/Error/403.jsp new file mode 100755 index 00000000..362d852b --- /dev/null +++ b/ForestBlog/src/main/webapp/WEB-INF/view/Home/Error/403.jsp @@ -0,0 +1,62 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8" %> + + + + + + 403 没有权限操作 + + + +
+
+
+

+ 403很抱歉,您没有权限操作! +

+
+
+

可能原因:

+
    +
  1. 访问了其他用户的数据
  2. +
  3. 系统异常
  4. +
  5. 输入的网址不正确
  6. +
+
+
+

可以尝试:

+
    +
  1. 返回首页
  2. +
  3. 留言反馈
  4. +
  5. + 联系站长 +
  6. +
+
+
+
+
+
+ + \ No newline at end of file diff --git a/ForestBlog/src/main/webapp/WEB-INF/view/Home/Page/articleDetail.jsp b/ForestBlog/src/main/webapp/WEB-INF/view/Home/Page/articleDetail.jsp index 1656618f..b05ac99d 100755 --- a/ForestBlog/src/main/webapp/WEB-INF/view/Home/Page/articleDetail.jsp +++ b/ForestBlog/src/main/webapp/WEB-INF/view/Home/Page/articleDetail.jsp @@ -120,7 +120,7 @@
    - +
"; + $("body").append(content); + } + + $(function () { + write(); + var $pay = $("#PAY_" + id).hide(); + $pay.find(".ds-payment-way").bind("click", function () { + $pay.find(".qrcode-img").hide(); + $pay.find(".qrCode_" + $pay.find("input[name=reward-way]:checked").val()).show(); + }); + $pay.find(".ds-close-dialog").bind("click", function () { + $pay.hide(); + }); + }); + var PaymentUtils = window['PaymentUtils'] = {}; + PaymentUtils.show = function () { + $("#PAY_" + id).show(); + } + PaymentUtils.hide = function () { + $("#PAY_" + id).hide(); + } +})(jQuery); + + +function confirmDelete() { + var msg = "您确定要删除吗?"; + if (confirm(msg) == true) { + return true; + } else { + return false; + } +} + +//退出登录 +function logout() { + $.ajax({ + async: false, + type: "POST", + url: '/admin/logout', + contentType: "application/x-www-form-urlencoded; charset=utf-8", + dataType: "text", + complete: function () { + window.location.reload(); + } + }) +} + +//删除评论 +function deleteComment(id) { + if (confirmDelete() == true) { + $.ajax({ + async: false, + type: "POST", + url: '/admin/comment/delete/' + id, + contentType: "application/x-www-form-urlencoded; charset=utf-8", + dataType: "text", + complete: function () { + window.location.reload(); + } + }) + } +} + +//评论区域 +$(".comment-reply-link").click(function () { + var authorName = $(this).parents('.comment-author').find("strong").text(); + $("#cancel-comment-reply-link").show(); + $("#reply-title-word").html("回复 " + authorName); + var commentId = $(this).parents('.comment-body').attr("id").match(/\d+/g); + $("input[name=commentPid]").attr("value", commentId); + $("input[name=commentPname]").attr("value", authorName); + $("#comment").attr("placeholder", "@ " + authorName) +}) + +$("#cancel-comment-reply-link").click(function () { + $("#cancel-comment-reply-link").hide(); + $("input[name=commentPid]").attr("value", 0); + $("input[name=commentPname]").attr("value", ""); + $("#reply-title-word").html("发表评论"); +}) + +//文章浏览量+1 +function increaseViewCount(articleId) { + if ($.cookie("viewId") != articleId || $.cookie("viewId") == null) { + $.ajax({ + async: false, + type: "POST", + url: "/article/view/" + articleId, + dataType: 'json', + contentType: 'application/json', + success: function (data) { + console.log(data); + $(".articleViewCount").html(data); + $.cookie( + "viewId", + articleId,//需要cookie写入的业务 + { + "path": "/", //cookie的默认属性 + } + ); + }, + error: function () { + alert("获取数据出错!"); + }, + }); + } +} + + +//点赞+1 +function increaseLikeCount() { + if ($.cookie("likeId") != articleId || $.cookie("likeId") == null) { + $.ajax({ + async: false, + type: "POST", + url: "/article/like/" + articleId, + dataType: 'json', + contentType: 'application/json', + success: function (data) { + $(".count").html(data); + $.cookie( + "likeId", + articleId,//需要cookie写入的业务 + { + "path": "/", //cookie的默认属性 + } + ); + }, + error: function () { + //alert("获取数据出错!"); + }, + }); + } +} + + +//ajax提交评论信息 +$("#comment_form").submit(function () { + $.ajax({ + async: false, + type: "POST", + url: '/comment', + contentType: "application/x-www-form-urlencoded; charset=utf-8", + data: $("#comment_form").serialize(), + success: function (data) { + if (data.code == 0) { + layer.msg("评论成功!"); + localStorage.setItem('author', $("#author_name").val()); + localStorage.setItem('email', $("#author_email").val()); + localStorage.setItem('url', $("#author_url").val()); + window.setTimeout("window.location.reload()", 2000); + return false; + } else { + layer.msg(data.msg); + } + + }, + error: function () { + } + }) + return false; +}) + +//百度分享 +window._bd_share_config = { + "common": { + "bdSnsKey": {}, + "bdText": "", + "bdMini": "2", + "bdMiniList": false, + "bdPic": "", + "bdStyle": "0", + "bdSize": "16" + }, + "share": {}, + // "image": {"viewList": ["qzone", "tsina", "tqq", "renren", "weixin"], "viewText": "分享到:", "viewSize": "16"}, + // "selectShare": {"bdContainerClass": null, "bdSelectMiniList": ["qzone", "tsina", "tqq", "renren", "weixin"]} +}; +with (document) 0[(getElementsByTagName('head')[0] || body).appendChild(createElement('script')).src = 'http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion=' + ~(-new Date() / 36e5)]; + + +//申请友情链接 +$("#applyLinkForm").submit(function () { + $.ajax({ + async: false, + type: "POST", + url: '/applyLinkSubmit', + contentType: "application/x-www-form-urlencoded; charset=utf-8", + data: $("#applyLinkForm").serialize(), + success: function () { + alert("申请成功,请耐心等待审核!"); + } + }) +}) diff --git a/ForestBlog/src/test/java/com/liuyanzhao/ssm/blog/BaseTest.java b/ForestBlog/src/test/java/com/liuyanzhao/ssm/blog/BaseTest.java new file mode 100644 index 00000000..5f9e6fc0 --- /dev/null +++ b/ForestBlog/src/test/java/com/liuyanzhao/ssm/blog/BaseTest.java @@ -0,0 +1,15 @@ +package com.liuyanzhao.ssm.blog; + +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * 响应一个朋友号召,给其讲解项目,加单元测试 + * @author 言曌 + * @date 2020/10/10 2:32 下午 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration({"classpath:spring/spring-mybatis.xml", "classpath:spring/spring-mvc.xml", "classpath:mybatis/mybatis-config.xml"}) +public class BaseTest { +} diff --git a/ForestBlog/src/test/java/com/liuyanzhao/ssm/blog/mapper/ArticleMapperTest.java b/ForestBlog/src/test/java/com/liuyanzhao/ssm/blog/mapper/ArticleMapperTest.java new file mode 100644 index 00000000..f99b464e --- /dev/null +++ b/ForestBlog/src/test/java/com/liuyanzhao/ssm/blog/mapper/ArticleMapperTest.java @@ -0,0 +1,31 @@ +package com.liuyanzhao.ssm.blog.mapper; + +import com.liuyanzhao.ssm.blog.BaseTest; +import com.liuyanzhao.ssm.blog.entity.Article; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; + +/** + * @author 言曌 + * @date 2020/10/11 10:53 上午 + */ + +public class ArticleMapperTest extends BaseTest { + + @Autowired + private ArticleMapper articleMapper; + + @Test + public void countArticleView() { + int result = articleMapper.countArticleView(); + System.out.println(result); + } + + @Test + public void listArticle() { + List
articleList = articleMapper.listArticle(); + System.out.println(articleList); + } +} diff --git a/ForestBlog/src/test/java/com/liuyanzhao/ssm/blog/mapper/UserMapperTest.java b/ForestBlog/src/test/java/com/liuyanzhao/ssm/blog/mapper/UserMapperTest.java new file mode 100644 index 00000000..c0710872 --- /dev/null +++ b/ForestBlog/src/test/java/com/liuyanzhao/ssm/blog/mapper/UserMapperTest.java @@ -0,0 +1,53 @@ +package com.liuyanzhao.ssm.blog.mapper; + +import com.liuyanzhao.ssm.blog.BaseTest; +import com.liuyanzhao.ssm.blog.entity.User; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.Assert; + +import java.util.Date; +import java.util.List; + +/** + * @author 言曌 + * @date 2020/10/10 2:13 下午 + */ + +public class UserMapperTest extends BaseTest { + + @Autowired + private UserMapper userMapper; + + @Test + public void listUser() { + List userList = userMapper.listUser(); + System.out.println(userList); + Assert.notEmpty(userList, "用户数量为空"); + } + + + @Test + public void insert() { + User user = new User(); + user.setUserId(1234); + user.setUserName("test123"); + user.setUserNickname("测试用户"); + user.setArticleCount(0); + user.setUserEmail("test123@gmail.com"); + user.setUserPass("123456"); + user.setUserRegisterTime(new Date()); + user.setUserUrl("https://www.baidu.com"); + user.setUserStatus(1); + int row = userMapper.insert(user); + Assert.isTrue(row == 1, "创建用户失败"); + } + + + @Test + public void deleteById() { + int row = userMapper.deleteById(1234); + Assert.isTrue(row == 1, "删除用户失败"); + } + +} diff --git a/ForestBlog/src/test/java/com/liuyanzhao/ssm/blog/service/ArticleServiceTest.java b/ForestBlog/src/test/java/com/liuyanzhao/ssm/blog/service/ArticleServiceTest.java new file mode 100644 index 00000000..734eeb1a --- /dev/null +++ b/ForestBlog/src/test/java/com/liuyanzhao/ssm/blog/service/ArticleServiceTest.java @@ -0,0 +1,34 @@ +package com.liuyanzhao.ssm.blog.service; + +import com.liuyanzhao.ssm.blog.BaseTest; +import com.liuyanzhao.ssm.blog.entity.Article; +import com.liuyanzhao.ssm.blog.enums.ArticleStatus; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.Assert; + +import java.util.List; + +/** + * @author 言曌 + * @date 2020/10/10 2:36 下午 + */ + +public class ArticleServiceTest extends BaseTest { + + @Autowired + private ArticleService articleService; + + @Test + public void countArticle() { + int count = articleService.countArticle(ArticleStatus.PUBLISH.getValue()); + System.out.println(count); + Assert.state(count > 0, "已发布文章数量为0"); + } + + @Test + public void listRecentArticle() { + List
articleList = articleService.listRecentArticle(null, 5); + Assert.notEmpty(articleList, "文章数量为0"); + } +} diff --git a/ForestBlog/src/test/java/com/liuyanzhao/ssm/blog/service/UserServiceTest.java b/ForestBlog/src/test/java/com/liuyanzhao/ssm/blog/service/UserServiceTest.java new file mode 100644 index 00000000..6329a806 --- /dev/null +++ b/ForestBlog/src/test/java/com/liuyanzhao/ssm/blog/service/UserServiceTest.java @@ -0,0 +1,32 @@ +package com.liuyanzhao.ssm.blog.service; + +import com.liuyanzhao.ssm.blog.BaseTest; +import com.liuyanzhao.ssm.blog.entity.User; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.Assert; + +/** + * @author 言曌 + * @date 2020/10/10 2:32 下午 + */ + +public class UserServiceTest extends BaseTest { + + @Autowired + private UserService userService; + + @Test + public void getUserById() { + User user = userService.getUserById(122222); + System.out.println(user); + Assert.notNull(user, "用户不存在"); + } + + @Test + public void getUserByName() { + User user = userService.getUserByName("admin"); + System.out.println(user); + Assert.notNull(user, "用户名不存在"); + } +} diff --git a/README.md b/README.md index 87f13a65..b6e7f8db 100755 --- a/README.md +++ b/README.md @@ -1,15 +1,17 @@ -最新消息,SpringCloud 博客、音乐、商城、论坛系统开发中,开源,[sens-parent](https://github.com/saysky/sens-parent)
-最新消息,SpringBoot博客已经开源,[SENS](https://github.com/saysky/SENS) +最新消息,博主已开通B站账号:[Java刘哥](https://space.bilibili.com/160340478) +## 博主开发的其他博客或论坛项目全部在这里 +**[全部原创项目](https://liuyanzhao.com/shop.html)**
+**[https://liuyanzhao.com/shop.html?k=博客](https://liuyanzhao.com/shop.html?k=博客)**
+**[https://liuyanzhao.com/shop.html?k=论坛](https://liuyanzhao.com/shop.html?k=论坛)**
+- ------------------------------------------------------------ # 关于项目 +该博客是基于SSM实现的个人博客系统,适合初学SSM和个人博客制作的同学学习。
+博主已写了一篇该项目的毕业论文和录制了2个小时的代码讲解可以供大家学习,需要的可以联系博主,微信847064370。
-该博客是基于SSM实现的一个个人博客系统,适合初学SSM和个人博客制作的同学学习。 -主要涉及技术包括的包括 Maven、Spring、SpringMVC、MyBatis、Redis、JSP等。 -前端采用Layui框架和扒了一个[网站](http://liuyanzhao.com)的前台样式。
-详细介绍:[https://liuyanzhao.com/6347.html](https://liuyanzhao.com/6347.html) +主要涉及技术包括的包括 Maven、Spring、SpringMVC、MyBatis、JSP、MySQL等。
+详细介绍:[https://liuyanzhao.com/6347.html](https://liuyanzhao.com/6347.html)
+预览地址:[http://forestblog.liuyanzhao.com](http://forestblog.liuyanzhao.com)
-## 效果预览 -因博主服务器数量有限,目前该SSM博客已下线,不提供实时预览,大家可以本地部署。目前部署的是SpringBoot+MyBatis+FreeMarker的博客(已开源:[SENS](https://github.com/saysky/SENS)),样式差不多,特此说明。
-预览地址:http://blog.liuyanzhao.com
前台效果图
![image](uploads/home.png)
@@ -17,66 +19,91 @@ ![image](uploads/admin.png)
后台地址:/admin 或者 /login
-为了避免大家删除数据,暂时不给后台账号 +## 博主提供有偿服务 + +- 远程调试环境和运行项目 50-100  (已经有环境了50,需要代安装部分环境软件100) +- 项目论文 100 +- 项目详细讲解视频 100 +- 部署到云服务器 200 +- 项目和部分功能定制 200-500 +- 完整毕业设计定制 1000-1500 +- [更多项目、博主付费商品](https://liuyanzhao.com/shop.html) + ## 使用注意 #### 1.开发工具的选择 请使用 IntelliJ IDEA, 尽量不要用 Eclipse/MyEclipse,使用前者项目起不来我可以帮忙解决,后者直接忽视(理论上可以起) #### 2.确保你安装了 Maven -没有用过 `Maven` 的童鞋,感觉去学一下,安装一下,可以使用 3.3.9 或 3.5.0的版本 +从官网下载 Maven,并配置阿里云镜像,IDEA 或 Eclipse 里需要设置 Maven 的 settings.xml 。 #### 3.请安装 Lombok 插件 -代码中多次使用 `@Data` 注解,请确保你的 IDE 安装了 `Lombok` 插件,否则找不到 getter/setter 方法 - -#### 4.确保数据库版本是5.x 版本,8.x版本需要修改配置 -对于MySQL8.x版本需要修改 pom.xml 里MySQL驱动版本和数据库驱动名称 - +代码中多次使用 `@Data` 注解,请确保你的 IDE 安装了 `Lombok` 插件,否则找不到 getter/setter 方法。如果你的 lombok 无效,可能是 pom.xml 里的 lombok 版本和你安装的lombok 版本相差较大。
+无论是 Eclipse 还是 IDEA 都需要安装 lombok。 -#### 5.项目首页没有文件夹名称 +#### 4.项目首页没有文件夹名称 确保 tomcat 配置中 `application context` 是 /,而不是 /ForestBlog。这是导致你们首页css样式全无的原因,因为引用css路径都是 /xxx/xxx.css ![image](https://github.com/saysky/ForestBlog/blob/master/uploads/tomcat.png) -具体情况请看步骤 ## 使用步骤 任何问题都可以联系我
+简单的我可以直接口头回复你,复杂的可以有偿远程帮你。
Q Q:847064370
微信:847064370
在线留言:[https://liuyanzhao.com/message.html](https://liuyanzhao.com/message.html) #### 1、克隆项目
克隆或者下载项目到本地,解压,主要分为三个部分:ForestBlog、uploads 和 forest_blog.sql
-ForestBlog: 完整项目源码, 可以使用 IDEA 导入或者打开
+ForestBlog: 完整项目源码, 可以使用 IDEA 导入或者打开,记得要导入里面那个 ForestBlog项目,即 pom.xml所在的父目录。
uploads: 上传图片的目录,与源码分离开来,可以放到物理磁盘某一目录,如D盘某目录,后面会讲
forest_blog.sql: 数据库文件,请先创建数据库,然后以运行sql文件方式导入
-#### 2.使用 IDEA 导入项目 -确保你安装了 Maven,导入项目时,选择已存在的项目,类型是 Maven 项目 +#### 2.使用 IDEA 导入Maven项目 +确保你安装了 Maven,导入项目时,选择已存在的项目,类型是 Maven 项目。
+等待Maven下载完依赖,或者手动点击刷新,如果无法下载请检查 Maven 配置和切换网络,多次尝试。
+导入完成后,如果出现Java类里红色报错,多半是 Lombok 插件没有安装。 + #### 3、 导入数据库
新建数据库 `forest_blog`,导入数据库(即forest_blog.sql)。注意,数据库的编码和排序规则是utf-8和utf-8_general_ci
-数据库默认用户名 root,密码 123456 + #### 4、修改项目中的数据库连接信息
修改 `db.properties` 文件,该文件很容易找到,在 src/main/resources 中
里面有 MySQL, 请确保已安装和启动 MySQL
注意修改数据库地址、表名、用户名和密码。
+否则项目无法启动 -#### 5、配置 uploads 目录
+#### 5、配置 tomcat 和 uploads 目录
+该项目是 SSM 项目,没有启动类,需要通过 tomcat 来运行。无论是 Eclipse 还是 IDEA 都很容易。
+这里主要说一下上传目录映射问题:
该项目中,文件上传是传到本地,且和项目文件夹不在一起,就是说是源码和上传目录是分离的。
-比如我们把 uploads 目录放到 E盘根目录,比如有一张图片路径是 `E:/uploads/2017/10/avatar.jpg`, 我们想在项目中以 `http://loclahost:8080/uploads/2017/10/avatar.jpg ` 方式访问,需要以下两步:
+比如我们把 uploads 目录放到 E盘根目录(建议直接把我的那个uploads文件夹拷贝到D盘根目录),比如有一张图片路径是 `D:/uploads/2017/10/avatar.jpg`, 我们想在项目中以 `http://loclahost:8080/uploads/2017/10/avatar.jpg ` 方式访问,需要以下两步:
-1. 修改 UploadFileController.java 中上传路径,需要修改 rootPath 为你指定的 uploads 目录,如 `String rootPath ="E:/uploads/";`
+1、 修改 UploadFileController.java 中上传路径,需要修改 rootPath 为你指定的 uploads 目录,如 `String rootPath ="D:/uploads/";`
如果不修改,会出现无法上传失败;
-2. 给 IDEA 设置静态资源映射,在 tomcat 配置中 Deployment 里面添加,如图 -![image](https://github.com/saysky/ForestBlog/blob/master/uploads/tomcat2.png) +2、为 tomcat 设置 uploads 映射。
+- IDEA 操作如下:
+在 tomcat 配置中 Deployment 里面添加,如图 +![image](uploads/tomcat.png) +![image](uploads/tomcat2.png) + +- Eclispe 操作如下:
+在 Server/server.xml 的 前面添加 +``` + + +``` +- Linux 服务器环境部署 -如果你是在 Linux 服务器上部署,需要在 Tomcat 配置文件中添加
在 tomcat/conf/server.xml 的 Host 标签内添加如下代码
-`` +``` + + +``` 如果不修改,会出现uploads的图片无法加载,无法显示上传目录的图片 @@ -86,25 +113,18 @@ Github地址:https://github.com/saysky/ForestBlog (如果可以帮忙点一次Star和Fork) ## 更新记录 -第二次更新:2018年11月26日
-大更,重新规范了代码,完善了注释 - -首次提交:2017年10月10日
- -### 新的架构博客已开源,需要的可以联系博主 -2019年2月(已完成)
-基于 SpringBoot+ElasticSearch+Shiro+FreeMarker+AdminLte实现,几乎重做,预览地址:http://blog.liuyanzhao.com
-[SENS](https://github.com/saysky/SENS) - -2019年7月(开发中)
-前端使用 Vue Element UI改造,后端优化
+- 2021年02月25日 +- 第三次大更,修复部分Bug,新增用户注册、用户管理等功能 +- 2018年11月26日 +- 第二次大更,重新规范了代码,完善了注释 -## 请喝一杯奶茶 -请我喝一杯奶茶吧! -插条广告:博主最近有点时间,长期接毕设定制,软件定制,Java问题解决
-具体查看[有偿服务](https://liuyanzhao.com/bulletin/my-service) +- 2017年10月10日 +- 首次完整提交 -![image](https://github.com/saysky/ForestBlog/blob/master/uploads/donate.png) +## 广告 +插条广告:博主长期接毕设定制,软件定制,Java问题解决
+具体查看[有偿服务](https://liuyanzhao.com/notice/my-service.html)
+博主付费商品:[https://liuyanzhao.com/shop.html](https://liuyanzhao.com/shop.html) diff --git a/forest_blog.sql b/forest_blog.sql index 108b6675..19b32941 100644 --- a/forest_blog.sql +++ b/forest_blog.sql @@ -1,209 +1,388 @@ -/* - Navicat Premium Data Transfer - - Source Server : localhost - Source Server Type : MySQL - Source Server Version : 50641 - Source Host : localhost - Source Database : forest_blog - - Target Server Type : MySQL - Target Server Version : 50641 - File Encoding : utf-8 - - Date: 04/25/2019 22:03:53 PM -*/ - -SET NAMES utf8; +SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- --- Table structure for `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 '0', - `article_comment_count` int(11) DEFAULT '0', - `article_like_count` int(11) DEFAULT '0', - `article_is_comment` int(1) unsigned DEFAULT NULL, - `article_status` int(1) unsigned DEFAULT '1', - `article_order` int(11) unsigned DEFAULT NULL, - `article_update_time` datetime DEFAULT NULL, - `article_create_time` datetime DEFAULT NULL, - `article_summary` text, + `article_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '文章ID', + `article_user_id` int(11) unsigned DEFAULT NULL COMMENT '用户ID', + `article_title` varchar(255) DEFAULT NULL COMMENT '标题', + `article_content` mediumtext COMMENT '内容', + `article_view_count` int(11) DEFAULT '0' COMMENT '访问量', + `article_comment_count` int(11) DEFAULT '0' COMMENT '评论数', + `article_like_count` int(11) DEFAULT '0' COMMENT '点赞数', + `article_is_comment` int(1) unsigned DEFAULT NULL COMMENT '是否允许评论', + `article_status` int(1) unsigned DEFAULT '1' COMMENT '状态', + `article_order` int(11) unsigned DEFAULT NULL COMMENT '排序值', + `article_update_time` datetime DEFAULT NULL COMMENT '更新时间', + `article_create_time` datetime DEFAULT NULL COMMENT '创建时间', + `article_summary` text COMMENT '摘要', + `article_thumbnail` varchar(255) DEFAULT NULL COMMENT '缩略图', PRIMARY KEY (`article_id`) -) ENGINE=MyISAM AUTO_INCREMENT=35 DEFAULT CHARSET=utf8; +) ENGINE=MyISAM AUTO_INCREMENT=50 DEFAULT CHARSET=utf8; -- ---------------------------- --- Records of `article` +-- Records of article -- ---------------------------- BEGIN; -INSERT INTO `article` VALUES ('1', '1', '[转载]SpringMVC中使用Interceptor拦截器', '

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


一、定义Interceptor实现类

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


(一)实现HandlerInterceptor接口

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


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


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


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


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


import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;

import org.springframework.web.servlet.ModelAndView;

public class SpringMVCInterceptor implements HandlerInterceptor {

    /** 

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

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

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

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

     */

    @Override

    public boolean preHandle(HttpServletRequest request,

            HttpServletResponse response, Object handler) throws Exception {

        // TODO Auto-generated method stub  

        return false;

    }

    /** 

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

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

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

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

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

     */

    @Override

    public void postHandle(HttpServletRequest request,

            HttpServletResponse response, Object handler,

            ModelAndView modelAndView) throws Exception {

        // TODO Auto-generated method stub  

    }

    /** 

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

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

     */

    @Override

    public void afterCompletion(HttpServletRequest request,

            HttpServletResponse response, Object handler, Exception ex)

    throws Exception {

        // TODO Auto-generated method stub  

    }

}

(二)实现WebRequestInterceptor 接口

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


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


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


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


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


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


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


 


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


import org.springframework.ui.ModelMap;

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

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

public class AllInterceptor implements WebRequestInterceptor {

    /** 

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

     */

    @Override

    public void preHandle(WebRequest request) throws Exception {

        // TODO Auto-generated method stub  

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

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

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

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

    }

    /** 

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

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

     */

    @Override

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

        // TODO Auto-generated method stub  

        for (String key:map.keySet())

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

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

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

    }

    /** 

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

     */

    @Override

    public void afterCompletion(WebRequest request, Exception exception)

    throws Exception {

        // TODO Auto-generated method stub  

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

    }

}


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

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


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

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

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

下面是我的声明示例:




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


 


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



    

    

    

        

        

        

    



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


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


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


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


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




', '27', '0', '3', '1', '1', '1', '2018-11-25 20:51:52', '2017-10-06 23:54:18', null), ('2', '1', 'springmvc 表单中文乱码解决方案', '

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

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

拦截器代码如下

  1.   
  2.   
  3. <filter>  
  4.     <filter-name>CharacterEncodingFilterfilter-name>  
  5.     <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>  
  6.       
  7.     <init-param>  
  8.       <param-name>encodingparam-name>  
  9.       <param-value>UTF-8param-value>  
  10.     init-param>  
  11.       
  12.     <init-param>  
  13.       <param-name>forceEncodingparam-name>  
  14.       <param-value>trueparam-value>  
  15.     init-param>  
  16. filter>  
  17.   
  18. <filter-mapping>  
  19.     <filter-name>CharacterEncodingFilterfilter-name>  
  20.     <url-pattern>/*url-pattern>  
  21. filter-mapping>  


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

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

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

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

 

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

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

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

 

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

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

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

', '16', '2', '1', '1', '1', '1', '2018-11-25 20:52:13', '2017-10-07 12:12:42', null), ('4', '1', '[转载]HTTP的幂等性', '

理解HTTP幂等性

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

幂等性定义

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

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

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

分布式事务 vs 幂等设计

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

bool withdraw(account_id, amount)\r\n

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

\"20171007153621950.png\"

图1

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

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

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

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

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

\"20171007153648208.png\"

图2

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

HTTP的幂等性

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

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

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

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

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

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

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

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

总结

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

参考

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

The Importance of Idempotence

Stackoverflow - PUT vs POST in REST

 

 

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

', '13', '0', '0', '1', '1', '1', '2018-11-25 20:51:05', '2017-10-07 15:37:20', null), ('3', '1', '使用rapid-framework继承jsp页面', '

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

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

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

父页面

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

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

    子页面

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

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

    一、JDBC 常用 API

    1、Connection 接口

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

    2、Statement 接口

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

     execute是executeQuery和executeUpdate的综合.

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

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

    3、PreparedStatement 接口

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

    DML 语句:SELECT、UPDATE、INSERT、DELETE

    DLL 语句:CREATE DROP ALERT

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

    4、ResultSet 接口

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

    二、案例

    1、往数据库里添加数据

    ① 数据表结构如下

    \"2017100715402014.png\"

    ② 代码如下

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

    ③运行结果

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

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

    ①数据表如下

    \"20171007154127663.png\"

    ②代码如下

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

    ③ 运行结果

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

    \"20171007154222212.jpg\"

    ', '0', '0', '0', '1', '1', '1', '2018-11-25 20:51:27', '2017-10-07 15:42:43', null), ('6', '1', 'Hibernate 简单的CURD操作', '

    一、单表操作 CURD 实例

    • save
    • update
    • delete
    • get/load (查询单个记录)

     

    二、代码实现

    StudentTest.java

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

     

    三、get 和 load 的区别

    区别一、

    • 不考虑缓存的情况下,get 方法会在调用之后,立即向数据库发送 sql 语句,返回持久化对象。
    • load 方法会在调用后返回一个持久化对象。该代理对象只保留了实体对象的 id,直到使用实体对象的非主键属性时才发出 sql 语句。

    区别二、

    • 查询数据库中不存在的数据时,get 方法返回 null 。
    • load 方法返回异常 org.hibernate.ObjectNotFoundException

    p


    ', '3', '2', '0', '1', '1', '1', '2018-11-25 20:52:33', '2017-10-07 15:49:54', null), ('7', '1', 'Hibernate 基本类型', '

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

    一、日期类型

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

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

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

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

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

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

    二、对象类型

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

    1、Student 类中 成员变量如下

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

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

    3、在测试类添加 testWriteBlob方法

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

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

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

    \"2017100715505630.png\"

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

    在测试类中添加 testReadBlob 方法

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

    6、使用 Junit Test 运行 testReadBlob 方法

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

     

    \"20171007155038568.png\"

    三、组件属性

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

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

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

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

    2、Address 类 部分代码如下

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

    3、修改 Student.hbm.xml

    将之前的

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

    改成

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

    注意:单词不要打错

    4、修改 测试类 testSaveStudent 代码

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

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

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

    6、用 Junit Test 运行 testSaveStudent 方法

    运行成功,查看数据表

    \"2017100715512437.jpg\"

    查看 表结构

    \"20171007155135604.jpg\"

     

    ', '1', '0', '0', '1', '1', '1', '2018-11-25 20:55:13', '2017-10-07 15:51:57', null), ('8', '1', 'Mybatis高级映射多对多查询', '

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

    一、开发准备

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

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

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

    \"20171007155334244.png\"

    \"20171007155349805.png\"


    \"20171007155410485.png\"


    \"20171007155450773.png\"

    2、思路分析

    \"20171007155545742.png\"

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

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

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

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

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

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

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

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

    我们先看一下查询结果:

    \"2017100717420134.png\"


    二、代码实现

    1、四个持久化类

    ① User.java

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

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

     

    ② Items.java

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

     

    ③ Orders.java

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

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

     

    ④ Orderdetail.java

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

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

     

    2、订单代理 即mapper.java

    OrdersMapperCustom.java

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

     

    3、OrdersMapperCustom.xml    映射文件

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

     

    4、测试类 OrderMapperCustomTest.java

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

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

     

    小结

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

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

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

    ', '8', '3', '0', '1', '1', '1', '2018-11-25 20:53:43', '2017-10-07 15:56:00', null); -INSERT INTO `article` VALUES ('9', '1', 'Servlet 实现验证码', '

    分析

    生成图片实现类

    ImageServlet 类

    ① 定义BufferedImage 对象

    ② 获得 Graphics 对象

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

    ④ 使用 Graphics 绘制图片

    ⑤ 记录验证码信息到 session 中

    ⑥ 使用 ImageIO 输出图片

    代码实现

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

    index.jsp

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

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

    ImageServlet.java

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

    LoginServlet.java

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

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

    1. <servlet>
    2.      <servlet-name>ImageServletservlet-name>
    3.      <servlet-class>com.liuyanzhao.ImageServletservlet-class>
    4.  servlet>
    5.  <servlet-mapping>
    6.      
    7.      <servlet-name>ImageServletservlet-name>
    8.      <url-pattern>/ImageServleturl-pattern>
    9.  servlet-mapping>
    10.  <servlet>
    11.      <servlet-name>LoginServletservlet-name>
    12.      <servlet-class>com.liuyanzhao.LoginServletservlet-class>
    13.  servlet>
    14.  <servlet-mapping>
    15.      
    16.      <servlet-name>LoginServletservlet-name>
    17.      <url-pattern>/LoginServleturl-pattern>
    18.  servlet-mapping>

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

    效果如下

    \"20171007155723341.png\"

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

    ', '5', '2', '0', '1', '1', '1', '2018-11-25 20:53:29', '2017-10-07 15:57:38', null), ('10', '1', 'Integer与int的种种比较你知道多少?', '

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

    首先看代码:

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

    详解如下:

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

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

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

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

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

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

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

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

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

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

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

     

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


    ', '1', '1', '1', '1', '1', '1', '2018-11-25 20:52:45', '2017-10-07 15:58:51', null), ('11', '1', 'Java如何让程序一直运行,不停止', '

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

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

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

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

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

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

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

     

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

    demo1:构造方法

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

    demo2:普通代码块

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

    demo3:构造代码块

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

    demo4:静态代码块

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

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

     

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

     

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

     

    详细解释:

    一、DDL is Data Definition Language statements

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

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

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

    3.DROP - delete objects from the database   删除

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

    TRUNCATE TABLE [Table Name]。

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

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

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

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

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

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

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

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

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

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

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

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

     

    二、DML is Data Manipulation Language statements

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

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

    2.INSERT - insert data into a table                    添加

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

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

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

    6.EXPLAIN PLAN - explain access path to data

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

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

     

    三、DCL is Data Control Language statements

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

    1.COMMIT - save work done 提交

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

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

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


    ', '1', '3', '1', '1', '1', '1', '2018-11-25 20:47:30', '2017-10-07 16:00:50', null), ('14', '1', 'execute、executeUpdate、executeQuery三者的区别', '

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

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

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

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

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

           execute是executeQuery和executeUpdate的综合.

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

    -----

     

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

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

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

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

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

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

    ', '4', '3', '0', '1', '1', '1', '2018-11-25 20:45:25', '2017-10-07 16:02:12', null), ('15', '1', 'JSP 中 forward 转发 和 sendRedirect 重定向的区别', '

    本质区别:

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

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

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

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

           3.一种解释:

    假设你去办理某个执照

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

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

    工作流程:

    1.重定向

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

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

    2.转发

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

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

     

    调用方式:

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

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

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

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

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

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

     

         1.重定向:
              1).response.sendRedirect(url);
              2).response.setState(302);
                   response.setHeader(\"location\",url);
         2.转发:
              1).request.getRequestDispatcher(url).forward(request,response);
              2).request.getRequestDispatcher(url).include(request,response);
              3).
    ', '1', '0', '0', '1', '1', '1', '2018-11-25 20:47:10', '2017-10-07 16:03:08', null), ('16', '1', 'Java中File类的使用', '

    一、File类常用API介绍

    内容见代码和注释

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

     

    二、遍历目录

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

    一、问题

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

    执行结果是icon无法显示

     

    二、解决

    类中有如下调用:

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

     

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

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

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

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

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

     

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

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

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

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

     

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

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

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

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

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

     

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

     

    三、其他

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

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

    Public ImageIcon(URL url)

     

    第一种构造不在赘述。

    第二种通过URL来得到图片

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

    ImageIcon icon = new ImageIcon(url);

     

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

    ', '9', '5', '0', '1', '1', '1', '2018-11-25 20:46:27', '2017-10-07 16:05:55', null), ('27', '1', 'SpringCloud 中使用 Eureka 和 Feign 实现增删改查', '

    在没有接触微服务或者分布式项目之前,我们的项目时没有分层的,我们的服务也是没有分层的。

    比如,我们有5台服务器使用 Nginx 做负载均衡,上面都是安装了我们的 Tomcat 服务器,部署的一样的项目。当用户访问某个请求的时候,按照负载均衡的机制(随机、轮询之类)来转发到对应的服务器上,然后在该服务器上先是到 Controller,然后 Service,再到 DAO,然后返回给 Service,返回给 Controller,最后返回给前台。似乎这样也行。

    但是当我们网站访问量不断飙升的时候,似乎有点撑不住了,只能加服务器,成本也明显增加。我们能不能再分一分呢 ?

    同时,我们发现真正比较耗费时间的是一些 IO 操作,比如数据库操作、上传文件之类的。为了方便开发,我们可以把 DAO 和 Service 层放在一起,暂且称为业务层。业务层耗费的远远大于 Controller 层的。我们可以在 5 台服务器中拿 1 台部署 Controller 层,另外 4 台部署业务层代码。

    如果将他们连接起来呢?

    Eureka + Feign!

    Eureka 做注册中心,业务层和控制器层每一个实例可以作为一个服务,比如 UserService 是一个服务,UserController 也可以是一个服务,一个服务可以有多个状态(可以有多台机器注册到该服务)。

    下面通过一个例子来讲解

     

    源码地址:https://github.com/saysky/spring-cloud-demo

     

    一、下载源码

    请上 Github 上下载本例子源码

    源码地址:https://github.com/saysky/spring-cloud-demo

     

    二、项目结构

    注意这是三个项目(不是一个项目),用 IDEA 打开分别打开三个项目,最终如图

    \"SpringCloud

     

    1、eureka-server(一个 SpringBoot 项目)

    \"SpringCloud

     

     

    2、cloud (一个多模块项目,API + 业务层)   \"SpringCloud

     

     

    (1)其中 cloud-api 是 api,里面有 model 和 service接口,cloud-core 和 cloud-web 将依赖它

    到时候需要首先将该模块 install,即打包到本地仓库中

    \"SpringCloud

     

    (2)其中 cloud-core,里面 dao 和 serviceImpl,作为生产者的存在

    启动类在这里面

    \"SpringCloud

     

    3、cloud-web( 一个 SpringBoot 项目)

    只有控制器层,需要依赖 cloud-api ,作为消费者的存在(可以是其他人调用)

    \"SpringCloud

     

    三、如何启动

    1、新建数据库 summer,导入 user.sql

    2、导入新项目,先后顺序随便(如eureka-server、cloud、cloud-web)

    3、启动 eureka-server 项目,端口为 8761

    4、对 cloud 项目中的 cloud-api 模块进行install,maven命令是 mvn clean install

    5、启动 cloud-core 项目,端口为 8090

    6、(可选)其实这时候我们可以在地址栏访问一下 localhost:8090/user 其实是可以访问的,如果你不行,说明有问题

    7、启动 cloud-web 项目,端口为 8080

    8、在地址栏访问 localhost:8080/user,如果可以看到数据,说明成功

     

    四、补充

    1、我这里模拟了四台注册了业务层服务(cloud-producer) 和 一台控制器层服务(cloud-consumer)

    \"SpringCloud

    ps:因为我是用本机测试,所以开了不同的端口表示不同的服务器

     

    2、访问控制器层,然后 Feign 会帮我们在注册中心中找到对应的服务名和对应的方法

    我们访问 localhost:8080/user/1 首先到的是控制器层,控制器层调用 userService,userService 是 Cloud-API 里的,添加了 @FeignClient(name=\"cloud-producer\") 注解,然后找到在注册中心中寻找 cloud-producer 的服务,从一台(负载均衡)服务器中找到 userService 的实现类,即 userServiceImpl,也就是进入了 cloud-core,然后完成一系列 DAO 和 Service 操作,最后返回给 Controller。

    \"SpringCloud

    其中 Service 和 Service 的实现都需要加 RequestMapping,Service 实现需要加 @RestController,否则会出现 404

    \"SpringCloud

    所以,其实我们可以直接访问 cloud-core 这一层,在地址栏访问 localhost:8090/user/1

    \"SpringCloud

     

    事实上,我们访问 控制器的时候,当需要某个 Service 的时候,Feign 帮我们向 Service 又发了一个同样的请求。我们可以直接访问这个 Service 请求

    但是,通常我们会在生产环境中,不让别人直接访问到 Service,可以通过一些机制实现,比如内网啦。

     

    六、学习过程中遇到的问题

    1、cloud-web 中注入 userService,一直注入失败,无法扫描到 userServiceImpl

    解决方案:在 cloud-web 启动类上添加

    @EnableFeignClients(basePackages = \"com.liuyanzhao.cloud\")

    \"SpringCloud

     

    2、出现 404 错误

    cloud-core 里的 userServiceImpl 也需要加 @RequestMapping,并且需要将 @Service 改成 @RestController

     

    UserService(cloud-api)

    \"SpringCloud

     

    UserServiceImpl(cloud-core)

    \"SpringCloud

     

    ', '0', '0', '0', '1', '1', '1', '2019-04-25 21:43:14', '2018-11-25 20:56:50', '在没有接触微服务或者分布式项目之前,我们的项目时没有分层的,我们的服务也是没有分层的。比如,我们有5台服务器使用 Nginx 做负载均衡,上面都是安装了我们的 Tomcat 服务器,部署的一样的项目。当用户访问某个请求的时候,按照负载均衡的机制(随机、轮询之类)来转发到对应的服务器上,然后在该服务器'); -INSERT INTO `article` VALUES ('23', '1', '是是是搜索', '是是', '5', '0', '0', '1', '0', '1', '2018-11-25 23:56:08', '2018-11-25 16:47:05', null), ('24', '1', 'sssss', 'sssss', '0', '0', '0', '1', '0', '1', '2018-11-25 17:28:38', '2018-11-25 17:19:09', null), ('25', '1', 'sssssssss', 'ssssssss', '1', '0', '0', '1', '0', '1', '2018-11-25 17:25:31', '2018-11-25 17:22:10', null), ('26', '1', 'SpringBoot 整合 elasticsearch 实例', '

    上一篇文章介绍了安装 mac 上安装 elasticsearch

    本文介绍 SpringBoot 集成 elasticsearch

     

    一、下载 并启动 elasticsearch

    下载地址:https://www.elastic.co/downloads/past-releases

    选择一个版本,下载

    博主这里测试使用的是 2.4.4

    下载方式可以选择 ZIP 包

     

    启动的话,windows 和 mac 有些细微区别

    windows :进入文件目录下的 bin,然后点击 elasticsearch.bat 即可

    mac:在终端执行命令 bin/elasticsearch

     

     

    二、配置 Maven

    1. <!-- Spring Boot Elasticsearch 依赖 -->
    2. <dependency>
    3.    <groupId>org.springframework.boot</groupId>
    4.    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    5. </dependency>

     

    1. <properties>
    2.    <elasticsearch.version>2.4.4</elasticsearch.version>
    3. </properties>

     

     

    添加 spring-boot-starter-data-elasticsearch 依赖,并设置 elasticsearch 版本为 2.4.4

     

    ES 和 SpirngBoot 版本参考:

    https://github.com/spring-projects/spring-data-elasticsearch/wiki/Spring-Data-Elasticsearch---Spring-Boot---version-matrix

     

    三、修改 application.properties

    1. #Project
    2. server.port=8080
    3. debug=true
    4. server.context-path=/chuyun
    5. # DataSource
    6. spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    7. spring.datasource.username=root
    8. spring.datasource.password=123456
    9. spring.datasource.url=jdbc:mysql://localhost:3306/chuyun?characterEncodeing=utf-8&useSSL=false
    10. # JPA
    11. spring.jpa.show-sql=true
    12. spring.jpa.hibernate.ddl-auto=update
    13. #Thymeleaf
    14. spring.thymeleaf.encoding=UTF-8
    15. spring.thymeleaf.cache=false
    16. spring.thymeleaf.cache-period=0
    17. spring.template.cache=false
    18. spring.thymeleaf.mode=HTML5
    19. spring.thymeleaf.prefix=classpath:templates/
    20. spring.thymeleaf.suffix=.html
    21. #Elasticsearch
    22. spring.data.elasticsearch.cluster-nodes=localhost:9300
    23. spring.data.elasticsearch.properties.transport.tcp.connect_timeout=120s

    主要是添加最后面两条

     

    四、创建 ES Bean 和 Repository

    Article.java

    1. package com.liuyanzhao.chuyun.domain.es;
    2. import org.springframework.data.elasticsearch.annotations.Document;
    3. import javax.persistence.Id;
    4. import java.util.Date;
    5. /**
    6.  * @author 言曌
    7.  * @date 2018/1/22 下午4:45
    8.  */
    9. @Document(indexName=\"chuyun\",type=\"article\",indexStoreType=\"fs\",shards=5,replicas=1,refreshInterval=\"-1\")
    10. public class Article {
    11.     //文章ID,这里必须为 id
    12.     @Id
    13.     private Long id;
    14.     //标题
    15.     private String title;
    16.     //内容
    17.     private String content;
    18.     //浏览量
    19.     private Integer viewCount;
    20.     //发布时间
    21.     private Date createTime;
    22.     //更新时间
    23.     private Date updateTime;
    24.     public Article() {
    25.     }
    26.     public Long getId() {
    27.         return id;
    28.     }
    29.     public void setId(Long id) {
    30.         this.id = id;
    31.     }
    32.     public String getTitle() {
    33.         return title;
    34.     }
    35.     public void setTitle(String title) {
    36.         this.title = title;
    37.     }
    38.     public String getContent() {
    39.         return content;
    40.     }
    41.     public void setContent(String content) {
    42.         this.content = content;
    43.     }
    44.     public Integer getViewCount() {
    45.         return viewCount;
    46.     }
    47.     public void setViewCount(Integer viewCount) {
    48.         this.viewCount = viewCount;
    49.     }
    50.     public Date getCreateTime() {
    51.         return createTime;
    52.     }
    53.     public void setCreateTime(Date createTime) {
    54.         this.createTime = createTime;
    55.     }
    56.     public Date getUpdateTime() {
    57.         return updateTime;
    58.     }
    59.     public void setUpdateTime(Date updateTime) {
    60.         this.updateTime = updateTime;
    61.     }
    62.     @Override
    63.     public String toString() {
    64.         return \"Article{\" +
    65.                 \"id=\" + id +
    66.                 \", title=\'\" + title + \'\\\'\' +
    67.                 \", content=\'\" + content + \'\\\'\' +
    68.                 \", viewCount=\" + viewCount +
    69.                 \", createTime=\" + createTime +
    70.                 \", updateTime=\" + updateTime +
    71.                 \'}\';
    72.     }
    73. }

     

    ArticleRepository.java

    1. package com.liuyanzhao.chuyun.repository.es;
    2. import com.liuyanzhao.chuyun.domain.es.Article;
    3. import org.springframework.data.domain.Page;
    4. import org.springframework.data.domain.Pageable;
    5. import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
    6. /**
    7.  * @author 言曌
    8.  * @date 2018/1/22 下午5:05
    9.  */
    10. public interface ArticleRepository extends ElasticsearchRepository<Article, Long> {
    11.     Page<Article> findDistinctByTitleContainingOrContentContaining(String title, String content, Pageable pageable);
    12. }

     

     

    五、创建测试类

    ArticleRepositoryTest.java

    1. package com.liuyanzhao.chuyun.repository.es;
    2. import com.liuyanzhao.chuyun.domain.es.Article;
    3. import com.liuyanzhao.chuyun.entity.User;
    4. import org.junit.Before;
    5. import org.junit.Test;
    6. import org.junit.runner.RunWith;
    7. import org.springframework.beans.factory.annotation.Autowired;
    8. import org.springframework.boot.test.context.SpringBootTest;
    9. import org.springframework.data.domain.Page;
    10. import org.springframework.data.domain.PageRequest;
    11. import org.springframework.data.domain.Pageable;
    12. import org.springframework.test.context.junit4.SpringRunner;
    13. import java.util.Date;
    14. /**
    15.  * @author 言曌
    16.  * @date 2018/1/21 下午5:03
    17.  */
    18. @RunWith(SpringRunner.class)
    19. @SpringBootTest
    20. public class ArticleRepositoryTest {
    21.     @Autowired
    22.     private ArticleRepository articleRepository;
    23.     @Before
    24.     public void initRepositoryData() {
    25.         //清除所有数据
    26.         articleRepository.deleteAll();
    27.         Article article = new Article();
    28.         article.setId((long1);
    29.         article.setTitle(\"《蝶恋花》\");
    30.         article.setContent(\"槛菊愁烟兰泣露,罗幕轻寒,燕子双飞去。明月不谙离恨苦,斜光到晓穿朱户。昨夜西风凋碧树,独上高楼,望尽天涯路。欲寄彩笺兼尺素,山长水阔知何处?\");
    31.         article.setCreateTime(new Date());
    32.         article.setUpdateTime(new Date());
    33.         article.setViewCount(678);
    34.         articleRepository.save(article);
    35.         Article article2 = new Article();
    36.         article2.setId((long2);
    37.         article2.setTitle(\"《蝶恋花》\");
    38.         article2.setContent(\"伫倚危楼风细细,望极春愁,黯黯生天际。草色烟光残照里,无言谁会凭阑意。拟把疏狂图一醉,对酒当歌,强乐还无味。衣带渐宽终不悔,为伊消得人憔悴。\");
    39.         article2.setCreateTime(new Date());
    40.         article2.setUpdateTime(new Date());
    41.         article.setViewCount(367);
    42.         articleRepository.save(article2);
    43.         Article article3 = new Article();
    44.         article3.setId((long3);
    45.         article3.setTitle(\"《青玉案·元夕》\");
    46.         article3.setContent(\"东风夜放花千树,更吹落,星如雨。宝马雕车香满路。凤箫声动,玉壶光转,一夜鱼龙舞。蛾儿雪柳黄金缕,笑语盈盈暗香去。众里寻他千百度,蓦然回首,那人却在,灯火阑珊处。\");
    47.         article3.setCreateTime(new Date());
    48.         article3.setUpdateTime(new Date());
    49.         article3.setViewCount(786);
    50.         articleRepository.save(article3);
    51.     }
    52.     @Test
    53.     public void findDistinctByTitleContainingOrContentContainingTest() throws Exception {
    54.         Pageable pageable = new PageRequest(0,20);
    55.         String title = \"我爱罗琪\";
    56.         String content = \"花千树\";
    57.         Page<Article> page = articleRepository.findDistinctByTitleContainingOrContentContaining(title, content, pageable);
    58.         System.out.println(page);
    59.         System.out.println(\"---start---\");
    60.         for(Article article : page.getContent()) {
    61.             System.out.println(article.toString());
    62.         }
    63.         System.out.println(\"---end---\");
    64.     }
    65. }

     

    运行 @Test 注解的方法

    根据 title 和 content 内容查到一条数据

    \"SpringBoot

     

    修改 title 和 content

    String title = \"蝶恋\";\r\nString content = \"东风\";

    查到三条数据

    \"SpringBoot

     

    六、访问 http://localhost:9200/_plugin/head/

    因为上一篇文章中,我们讲了装一个 head 插件,现在我们就能看到里面的数据了(多余的数据是之前测试的)

    \"SpringBoot

    七、新建 Controller 类

    ArticleController.java

    1. package com.liuyanzhao.chuyun.controller;
    2. import com.liuyanzhao.chuyun.domain.es.Article;
    3. import com.liuyanzhao.chuyun.repository.es.ArticleRepository;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.data.domain.Page;
    6. import org.springframework.data.domain.PageRequest;
    7. import org.springframework.data.domain.Pageable;
    8. import org.springframework.web.bind.annotation.RequestMapping;
    9. import org.springframework.web.bind.annotation.RequestParam;
    10. import org.springframework.web.bind.annotation.RestController;
    11. import java.util.List;
    12. /**
    13.  * @author 言曌
    14.  * @date 2018/1/22 下午9:07
    15.  */
    16. @RestController
    17. @RequestMapping(\"/article\")
    18. public class ArticleController {
    19.     @Autowired
    20.     private ArticleRepository ArticleRepository;
    21.     @RequestMapping(\"\")
    22.     public List<Article> list(@RequestParam(value = \"title\", required = false) String title,
    23.                                 @RequestParam(value = \"content\", required = false) String content,
    24.                                 @RequestParam(value = \"pageIndex\", defaultValue = \"0\"int pageIndex,
    25.                                 @RequestParam(value = \"pageSize\", defaultValue = \"10\"int pageSize) {
    26.         Pageable pageable = new PageRequest(pageIndex, pageSize);
    27.         Page<Article> page = ArticleRepository.findDistinctByTitleContainingOrContentContaining(title, content, pageable);
    28.         return page.getContent();
    29.     }
    30. }

     

    因为之前在 测试类 里已经给 elasticsearch 添加了数据

    所有现在可以在浏览器上访问:

    http://localhost:8080/chuyun/article?title=浪淘沙&content=伊人

    \"SpringBoot

     

     

     

    本文地址:https://liuyanzhao.com/7170.html

    ', '1', '0', '0', '1', '1', '1', '2018-11-25 20:42:58', '2018-11-25 20:42:58', null), ('28', '1', 'SpringCloud 中使用 Eureka 和 Feign 实现增删改查', '

    在没有接触微服务或者分布式项目之前,我们的项目时没有分层的,我们的服务也是没有分层的。

    比如,我们有5台服务器使用 Nginx 做负载均衡,上面都是安装了我们的 Tomcat 服务器,部署的一样的项目。当用户访问某个请求的时候,按照负载均衡的机制(随机、轮询之类)来转发到对应的服务器上,然后在该服务器上先是到 Controller,然后 Service,再到 DAO,然后返回给 Service,返回给 Controller,最后返回给前台。似乎这样也行。

    但是当我们网站访问量不断飙升的时候,似乎有点撑不住了,只能加服务器,成本也明显增加。我们能不能再分一分呢 ?

    同时,我们发现真正比较耗费时间的是一些 IO 操作,比如数据库操作、上传文件之类的。为了方便开发,我们可以把 DAO 和 Service 层放在一起,暂且称为业务层。业务层耗费的远远大于 Controller 层的。我们可以在 5 台服务器中拿 1 台部署 Controller 层,另外 4 台部署业务层代码。

    如果将他们连接起来呢?

    Eureka + Feign!

    Eureka 做注册中心,业务层和控制器层每一个实例可以作为一个服务,比如 UserService 是一个服务,UserController 也可以是一个服务,一个服务可以有多个状态(可以有多台机器注册到该服务)。

    下面通过一个例子来讲解

     

    源码地址:https://github.com/saysky/spring-cloud-demo

     

    一、下载源码

    请上 Github 上下载本例子源码

    源码地址:https://github.com/saysky/spring-cloud-demo

     

    二、项目结构

    注意这是三个项目(不是一个项目),用 IDEA 打开分别打开三个项目,最终如图

    \"SpringCloud

     

    1、eureka-server(一个 SpringBoot 项目)

    \"SpringCloud

     

     

    2、cloud (一个多模块项目,API + 业务层)   \"SpringCloud

     

     

    (1)其中 cloud-api 是 api,里面有 model 和 service接口,cloud-core 和 cloud-web 将依赖它

    到时候需要首先将该模块 install,即打包到本地仓库中

    \"SpringCloud

     

    (2)其中 cloud-core,里面 dao 和 serviceImpl,作为生产者的存在

    启动类在这里面

    \"SpringCloud

     

    3、cloud-web( 一个 SpringBoot 项目)

    只有控制器层,需要依赖 cloud-api ,作为消费者的存在(可以是其他人调用)

    \"SpringCloud

     

    三、如何启动

    1、新建数据库 summer,导入 user.sql

    2、导入新项目,先后顺序随便(如eureka-server、cloud、cloud-web)

    3、启动 eureka-server 项目,端口为 8761

    4、对 cloud 项目中的 cloud-api 模块进行install,maven命令是 mvn clean install

    5、启动 cloud-core 项目,端口为 8090

    6、(可选)其实这时候我们可以在地址栏访问一下 localhost:8090/user 其实是可以访问的,如果你不行,说明有问题

    7、启动 cloud-web 项目,端口为 8080

    8、在地址栏访问 localhost:8080/user,如果可以看到数据,说明成功

     

    四、补充

    1、我这里模拟了四台注册了业务层服务(cloud-producer) 和 一台控制器层服务(cloud-consumer)

    \"SpringCloud

    ps:因为我是用本机测试,所以开了不同的端口表示不同的服务器

     

    2、访问控制器层,然后 Feign 会帮我们在注册中心中找到对应的服务名和对应的方法

    我们访问 localhost:8080/user/1 首先到的是控制器层,控制器层调用 userService,userService 是 Cloud-API 里的,添加了 @FeignClient(name=\"cloud-producer\") 注解,然后找到在注册中心中寻找 cloud-producer 的服务,从一台(负载均衡)服务器中找到 userService 的实现类,即 userServiceImpl,也就是进入了 cloud-core,然后完成一系列 DAO 和 Service 操作,最后返回给 Controller。

    \"SpringCloud

    其中 Service 和 Service 的实现都需要加 RequestMapping,Service 实现需要加 @RestController,否则会出现 404

    \"SpringCloud

    所以,其实我们可以直接访问 cloud-core 这一层,在地址栏访问 localhost:8090/user/1

    \"SpringCloud

     

    事实上,我们访问 控制器的时候,当需要某个 Service 的时候,Feign 帮我们向 Service 又发了一个同样的请求。我们可以直接访问这个 Service 请求

    但是,通常我们会在生产环境中,不让别人直接访问到 Service,可以通过一些机制实现,比如内网啦。

     

    六、学习过程中遇到的问题

    1、cloud-web 中注入 userService,一直注入失败,无法扫描到 userServiceImpl

    解决方案:在 cloud-web 启动类上添加

    @EnableFeignClients(basePackages = \"com.liuyanzhao.cloud\")

    \"SpringCloud

     

    2、出现 404 错误

    cloud-core 里的 userServiceImpl 也需要加 @RequestMapping,并且需要将 @Service 改成 @RestController

     

    UserService(cloud-api)

    \"SpringCloud

     

    UserServiceImpl(cloud-core)

    \"SpringCloud

     

    ', '2', '0', '0', '1', '1', '1', '2019-04-25 21:43:11', '2018-11-25 20:58:22', '在没有接触微服务或者分布式项目之前,我们的项目时没有分层的,我们的服务也是没有分层的。比如,我们有5台服务器使用 Nginx 做负载均衡,上面都是安装了我们的 Tomcat 服务器,部署的一样的项目。当用户访问某个请求的时候,按照负载均衡的机制(随机、轮询之类)来转发到对应的服务器上,然后在该服务器'), ('29', '1', 'IDEA启动EDAS项目', '

    一、下载EDAS配置中心

    这个有点像我们SpringCloud里的Eurake注册中心

    下载地址:点此

    官网地址:点此

    启动 edas配置中心:windows下点击 edas-lite-configcenter/bin 中的 startup.bat

    \"IDEA启动EDAS项目\"

    默认启动在 8080 端口

     

    二、安装 Ali-Tomcat 和 Pandora

    1、下载 Ali-Tomcat,保存后解压至相应的目录(如:d:\\work\\tomcat\\)。

     

    2、下载 Pandora 容器,保存后将内容解压至上述保存的 Ali-Tomcat 的 deploy 目录(d:\\work\\tomcat\\deploy)下。

    下载后是两个文件夹

    \"IDEA启动EDAS项目\"

     

     

    解压后,将 taobao-haf 放到 taobao-tomcat-7.0.59/deploy 里面

     

    比如我们有三个服务,分别是A、B、C

    我们建立三个文件夹,分别是A、B 和 C,每个文件夹里都拷贝一份上面的 taobao-tomcat-7.0.59

     

    三、IDEA 配置 Tomcat

    1、创建三个Tomcat

    \"IDEA启动EDAS项目\"

    指定启动的端口,VM options里填写对应的 Tomcat 的启动参数

    D:\\EDAS\\A\\taobao-tomcat-7.0.59\\deploy\\taobao-hsf.sar

     

    2、在Deployment里选择对应的项目

    \"IDEA启动EDAS项目\"

     

    然后依次启动几个项目就行了

     

    官方文档:https://help.aliyun.com/product/29500.html?spm=a2c4g.11186623.6.540.102bb8e7ETwPNH

     

    ', '2', '0', '0', '1', '1', '1', '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'), ('30', '1', 'SpringBoot + mongodb 整合, 记录网站操作日志,常用查询操作', '

    mongodb 是一种文档型数据库。跟 Redis 一样是非关系型数据库,Redis 属于那种小而快的数据库,常常用作缓存。

    而如果我们需要存一些类似于日志的那种,可以尝试用 mongodb (当然也有人用 MySQL,就是有点慢)。我们尝试用 mongodb 来存储博客的日志信息。

    本文主要介绍 SpringBoot 和 mongodb 整合,和基本的查询操作。

     

    一、依赖和配置

    创建 springboot 项目,引入 web 和 lombok,然后再添加 mongodb 依赖

    1、pom.xml

    1. <dependency>
    2.     <groupId>org.springframework.bootgroupId>
    3.     <artifactId>spring-boot-starter-data-mongodbartifactId>
    4. dependency>

    类似 Spring Data JPA

     

    2、application.properties

    1. spring.data.mongodb.uri=mongodb://localhost:27017/saysky

    saysky是数据库名称,确保你本地启动了 mongodb

     

    二、代码实例

    1、日志实体

    1. package com.liuyanzhao.mongodb.model;
    2. import lombok.Data;
    3. import java.util.Date;
    4. /**
    5.  * 日志
    6.  * @author 言曌
    7.  * @date 2018/9/3 20:00
    8.  */
    9. @Data
    10. public class Log {
    11.     private Long id;
    12.     private Long userId;
    13.     private Integer type;
    14.     private String url;
    15.     private String desc;
    16.     private Date createTime;
    17. }

     

    2、LogRepository

    1. package com.liuyanzhao.mongodb.dao;
    2. import com.liuyanzhao.mongodb.model.Log;
    3. import org.springframework.data.domain.Page;
    4. import org.springframework.data.domain.Pageable;
    5. import org.springframework.data.mongodb.repository.MongoRepository;
    6. import org.springframework.stereotype.Repository;
    7. import java.util.Date;
    8. import java.util.List;
    9. /**
    10.  * @author 言曌
    11.  * @date 2018/9/3 14:17
    12.  */
    13. @Repository
    14. public interface LogRepository extends   {
    15.     /**
    16.      * 根据用户ID查询
    17.      * @param userId
    18.      * @return
    19.      */
    20.     List findByUserId(Long userId);
    21.     /**
    22.      * 根据描述查询
    23.      * @param desc
    24.      * @return
    25.      */
    26.     List findByDesc(String desc);
    27.     /**
    28.      * 根据创建日期范围查询
    29.      * @param startTime
    30.      * @param endTime
    31.      * @return
    32.      */
    33.     List findByCreateTimeBetween(Date startTime, Date endTime);
    34.     /**
    35.      * 根据描述查询
    36.      * 分页查询
    37.      * @param desc
    38.      * @return
    39.      */
    40.     Page findByDesc(String desc, Pageable pageable);
    41.     /**
    42.      * 根据创建日期范围查询
    43.      * 分页查询
    44.      * @param startTime
    45.      * @param endTime
    46.      * @return
    47.      */
    48.     Page findByCreateTimeBetween(Date startTime, Date endTime,Pageable pageable);
    49. }

     

    3、测试类

    1. package com.liuyanzhao.mongodb.dao;
    2. import com.liuyanzhao.mongodb.model.Log;
    3. import org.junit.Test;
    4. import org.junit.runner.RunWith;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.boot.test.context.SpringBootTest;
    7. import org.springframework.data.domain.Page;
    8. import org.springframework.data.domain.PageRequest;
    9. import org.springframework.test.context.junit4.SpringRunner;
    10. import java.util.Date;
    11. /**
    12.  * @author 言曌
    13.  * @date 2018/9/3 14:21
    14.  */
    15. @SpringBootTest
    16. @RunWith(SpringRunner.class)
    17. public class LogRepositoryTest {
    18.     @Autowired
    19.     private LogRepository logRepository;
    20.     @Test
    21.     public void save() {
    22.         Log log = new Log();
    23.         log.setId(7L);
    24.         log.setType(1);
    25.         log.setDesc(\"更新用户\");
    26.         log.setUserId(10002L);
    27.         log.setUrl(\"/user/update\");
    28.         log.setCreateTime(new Date());
    29.         logRepository.save(log);
    30.     }
    31.     @Test
    32.     public void findById() {
    33.         Log Log = logRepository.findById(1L).get();
    34.         System.out.println(Log);
    35.     }
    36.     @Test
    37.     public void findByDes() {
    38.         //不分页
    39. //        List LogList = logRepository.findByDesc(\"添加用户\");
    40. //        System.out.println(LogList);
    41.         //分页查询
    42.         //查询第1页,每页显示2条
    43.         PageRequest pageRequest = new PageRequest(0,2);
    44.         Page logPage = logRepository.findByDesc(\"添加用户\",pageRequest);
    45.         System.out.println(logPage);
    46.     }
    47.     @Test
    48.     public void findByCreateTimeBetween() {
    49.         //根据时间区间查询
    50.         //不分页
    51.         Date createdAtStart = new Date(1535974057016L);
    52.         Date createdAtEnd = new Date(1535974145009L);
    53. //        List LogList = logRepository.findByCreateTimeBetween(createdAtStart,createdAtEnd);
    54. //        System.out.println(LogList);
    55.         //分页查询
    56.         //查询第1页,每页显示2条
    57.         PageRequest pageRequest = new PageRequest(0,2);
    58.         Page logPage = logRepository.findByCreateTimeBetween(createdAtStart,createdAtEnd,pageRequest);
    59.         System.out.println(logPage);
    60.     }
    61. }

     

    我们通过mongodb可视化工具可以看到数据

    \"SpringBoot

     

    三、更多查询

    因为引入的 MongoRepository 是一种 JPA 框架,所以增删改查都很容易。

    可以参考 Spring Data JPA 方法命名规范

      ', '0', '0', '0', '1', '1', '1', '2019-04-25 21:43:06', '2018-11-25 21:01:24', 'mongodb 是一种文档型数据库。跟 Redis 一样是非关系型数据库,Redis 属于那种小而快的数据库,常常用作缓存。而如果我们需要存一些类似于日志的那种,可以尝试用 mongodb (当然也有人用 MySQL,就是有点慢)。我们尝试用 mongodb 来存储博客的日志信息。本文主要介绍&nb'); -INSERT INTO `article` VALUES ('31', '1', 'RocketMQ 实战之快速入门', '

      最近 RocketMQ 刚刚上生产环境,闲暇之时在这里做一些分享,主要目的是让初学者能快速上手RocketMQ。

      \r\n

      RocketMQ 是什么

      \r\n

      Github 上关于 RocketMQ 的介绍:
      \r\nRcoketMQ 是一款低延迟、高可靠、可伸缩、易于使用的消息中间件。具有以下特性:

      \r\n
        \r\n
      1. 支持发布/订阅(Pub/Sub)和点对点(P2P)消息模型
      2. \r\n
      3. 在一个队列中可靠的先进先出(FIFO)和严格的顺序传递
      4. \r\n
      5. 支持拉(pull)和推(push)两种消息模式
      6. \r\n
      7. 单一队列百万消息的堆积能力
      8. \r\n
      9. 支持多种消息协议,如 JMS、MQTT 等
      10. \r\n
      11. 分布式高可用的部署架构,满足至少一次消息传递语义
      12. \r\n
      13. 提供 docker 镜像用于隔离测试和云集群部署
      14. \r\n
      15. 提供配置、指标和监控等功能丰富的 Dashboard
      16. \r\n
      \r\n

      对于这些特性描述,大家简单过一眼就即可,深入学习之后自然就明白了。

      \r\n

      专业术语

      \r\n

      Producer

      \r\n

      消息生产者,生产者的作用就是将消息发送到 MQ,生产者本身既可以产生消息,如读取文本信息等。也可以对外提供接口,由外部应用来调用接口,再由生产者将收到的消息发送到 MQ。

      \r\n

      Producer Group

      \r\n

      生产者组,简单来说就是多个发送同一类消息的生产者称之为一个生产者组。在这里可以不用关心,只要知道有这么一个概念即可。

      \r\n

      Consumer

      \r\n

      消息消费者,简单来说,消费 MQ 上的消息的应用程序就是消费者,至于消息是否进行逻辑处理,还是直接存储到数据库等取决于业务需要。

      \r\n

      Consumer Group

      \r\n

      消费者组,和生产者类似,消费同一类消息的多个 consumer 实例组成一个消费者组。

      \r\n

      Topic

      \r\n

      Topic 是一种消息的逻辑分类,比如说你有订单类的消息,也有库存类的消息,那么就需要进行分类,一个是订单 Topic 存放订单相关的消息,一个是库存 Topic 存储库存相关的消息。

      \r\n

      Message

      \r\n

      Message 是消息的载体。一个 Message 必须指定 topic,相当于寄信的地址。Message 还有一个可选的 tag 设置,以便消费端可以基于 tag 进行过滤消息。也可以添加额外的键值对,例如你需要一个业务 key 来查找 broker 上的消息,方便在开发过程中诊断问题。

      \r\n

      Tag

      \r\n

      标签可以被认为是对 Topic 进一步细化。一般在相同业务模块中通过引入标签来标记不同用途的消息。

      \r\n

      Broker

      \r\n

      Broker 是 RocketMQ 系统的主要角色,其实就是前面一直说的 MQ。Broker 接收来自生产者的消息,储存以及为消费者拉取消息的请求做好准备。

      \r\n

      Name Server

      \r\n

      Name Server 为 producer 和 consumer 提供路由信息。

      \r\n

      RocketMQ 架构

      \r\n
      \r\n
      \r\n
      \r\n
      \r\n
      \r\n
      RocketMQ 架构
      \r\n
      \r\n

      由这张图可以看到有四个集群,分别是 NameServer 集群、Broker 集群、Producer 集群和 Consumer 集群:

      \r\n
        \r\n
      1. NameServer: 提供轻量级的服务发现和路由。 每个 NameServer 记录完整的路由信息,提供等效的读写服务,并支持快速存储扩展。
      2. \r\n
      3. Broker: 通过提供轻量级的 Topic 和 Queue 机制来处理消息存储,同时支持推(push)和拉(pull)模式以及主从结构的容错机制。
      4. \r\n
      5. Producer:生产者,产生消息的实例,拥有相同 Producer Group 的 Producer 组成一个集群。
      6. \r\n
      7. Consumer:消费者,接收消息进行消费的实例,拥有相同 Consumer Group 的
        \r\nConsumer 组成一个集群。
      8. \r\n
      \r\n

      简单说明一下图中箭头含义,从 Broker 开始,Broker Master1 和 Broker Slave1 是主从结构,它们之间会进行数据同步,即 Date Sync。同时每个 Broker 与
      \r\nNameServer 集群中的所有节
      \r\n点建立长连接,定时注册 Topic 信息到所有 NameServer 中。

      \r\n

      Producer 与 NameServer 集群中的其中一个节点(随机选择)建立长连接,定期从 NameServer 获取 Topic 路由信息,并向提供 Topic 服务的 Broker Master 建立长连接,且定时向 Broker 发送心跳。Producer 只能将消息发送到 Broker master,但是 Consumer 则不一样,它同时和提供 Topic 服务的 Master 和 Slave
      \r\n建立长连接,既可以从 Broker Master 订阅消息,也可以从 Broker Slave 订阅消息。

      \r\n

      RocketMQ 集群部署模式

      \r\n
        \r\n
      1. 单 master 模式
        \r\n也就是只有一个 master 节点,称不上是集群,一旦这个 master 节点宕机,那么整个服务就不可用,适合个人学习使用。
      2. \r\n
      3. 多 master 模式
        \r\n多个 master 节点组成集群,单个 master 节点宕机或者重启对应用没有影响。
        \r\n优点:所有模式中性能最高
        \r\n缺点:单个 master 节点宕机期间,未被消费的消息在节点恢复之前不可用,消息的实时性就受到影响。
        \r\n注意:使用同步刷盘可以保证消息不丢失,同时 Topic 相对应的 queue 应该分布在集群中各个节点,而不是只在某各节点上,否则,该节点宕机会对订阅该 topic 的应用造成影响。
      4. \r\n
      5. 多 master 多 slave 异步复制模式
        \r\n在多 master 模式的基础上,每个 master 节点都有至少一个对应的 slave。master
        \r\n节点可读可写,但是 slave 只能读不能写,类似于 mysql 的主备模式。
        \r\n优点: 在 master 宕机时,消费者可以从 slave 读取消息,消息的实时性不会受影响,性能几乎和多 master 一样。
        \r\n缺点:使用异步复制的同步方式有可能会有消息丢失的问题。
      6. \r\n
      7. 多 master 多 slave 同步双写模式
        \r\n同多 master 多 slave 异步复制模式类似,区别在于 master 和 slave 之间的数据同步方式。
        \r\n优点:同步双写的同步模式能保证数据不丢失。
        \r\n缺点:发送单个消息 RT 会略长,性能相比异步复制低10%左右。
        \r\n刷盘策略:同步刷盘和异步刷盘(指的是节点自身数据是同步还是异步存储)
        \r\n同步方式:同步双写和异步复制(指的一组 master 和 slave 之间数据的同步)
        \r\n注意:要保证数据可靠,需采用同步刷盘和同步双写的方式,但性能会较其他方式低。
      8. \r\n
      \r\n

      RocketMQ 单主部署

      \r\n

      鉴于是快速入门,我选择的是第一种单 master 的部署模式。先说明一下我的安装环境:

      \r\n
        \r\n
      1. Centos 7.2
      2. \r\n
      3. jdk 1.8
      4. \r\n
      5. Maven 3.2.x
      6. \r\n
      7. Git
      8. \r\n
      \r\n

      这里 git 可用可不用,主要是用来直接下载 github 上的源码。也可以选择自己到
      \r\ngithub 上下载,然后上传到服务器上。以git操作为示例。

      \r\n
        \r\n
      1. clone 源码并用 maven 编译
      2. \r\n
      \r\n
      > git clone https://github.com/alibaba/RocketMQ.git /opt/RocketMQ\r\n> cd /opt/RocketMQ && mvn -Dmaven.test.skip=true clean package install assembly:assembly -U\r\n> cd target/alibaba-rocketmq-broker/alibaba-rocketmq\r\n
      \r\n

      此处可能遇到的问题
      \r\n一、执行\"git clone https://github.com/alibaba/RocketMQ.git /home/inspkgs/RocketMQ\"时出现以下提示:

      \r\n
      fatal: unable to access \'https://github.com/alibaba/RocketMQ.git/\': Could not resolve host: github.com; Unknown error\r\n
      \r\n

      解决办法:一般是由于网络原因造成的,执行以下命令

      \r\n
      > ping github.com\r\n
      \r\n

      确定可以 ping 通之后,再重新执行 git clone 命令。
      \r\n二、执行\"mvn -Dmaven.test.skip=true clean package install assembly:assembly -U\"编译时,可能出现下载相关jar很慢的情况。
      \r\n这也是由于默认 maven 中央仓库在国外的原因,可以根据需要在 /home/maven/conf/setting.xml 中的 添加以下内容后重新编译:

      \r\n
      <mirror>\r\n    <id>aliyunid>\r\n    <mirrorOf>centralmirrorOf>\r\n    <name>aliyun mavenname>\r\n    <url>http://maven.aliyun.com/nexus/content/groups/public/url>\r\nmirror>\r\n
      \r\n
        \r\n
      1. 启动 Name Server
      2. \r\n
      \r\n
      > nohup sh /opt/RocketMQ/bin/mqnamesrv &\r\n//执行 jps 查看进程\r\n> jps\r\n25913 NamesrvStartup\r\n//查看日志确保服务已正常启动\r\n> tail -f ~/logs/rocketmqlogs/namesrv.log\r\nThe Name Server boot success...\r\n
      \r\n
        \r\n
      1. 启动 broker
      2. \r\n
      \r\n
      > nohup sh /opt/RocketMQ/bin/mqbroker -n localhost:9876 &\r\n//执行 jps 查看进程\r\n> jps\r\n25954 BrokerStartup\r\n//查看日志确保服务已正常启动\r\n> tail -f ~/logs/rocketmqlogs/broker.log \r\nThe broker[broker-a, 10.1.54.121:10911] boot success...\r\n
      \r\n
        \r\n
      1. 发送和接收消息
        \r\n发送/接收消息之前,我们需要告诉客户端 NameServer 地址。RocketMQ 提供了多种方式来实现这一目标。为简单起见,我们使用环境变量 NAMESRV_ADDR。
      2. \r\n
      \r\n
      > export 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
      \r\n
        \r\n
      1. 关闭服务
      2. \r\n
      \r\n
      > sh /opt/RocketMQ/bin/mqshutdown broker\r\nThe mqbroker(36695) is running...\r\nSend shutdown request to mqbroker(36695) OK\r\n> sh /opt/RocketMQ/bin/mqshutdown namesrv\r\nThe mqnamesrv(36664) is running...\r\nSend shutdown request to mqnamesrv(36664) OK\r\n
      \r\n

      生产者、消费者 Demo

      \r\n
        \r\n
      1. 生产者
      2. \r\n
      \r\n
      public class Producer {\r\n    public static void main(String[] args) throws MQClientException, InterruptedException {\r\n\r\n        //声明并初始化一个producer\r\n        //需要一个producer group名字作为构造方法的参数,这里为producer1\r\n        DefaultMQProducer producer = new DefaultMQProducer(\"producer1\");\r\n        \r\n        //设置NameServer地址,此处应改为实际NameServer地址,多个地址之间用;分隔\r\n        //NameServer的地址必须有,但是也可以通过环境变量的方式设置,不一定非得写死在代码里\r\n        producer.setNamesrvAddr(\"10.1.54.121:9876;10.1.54.122:9876\");\r\n        \r\n        //调用start()方法启动一个producer实例\r\n        producer.start();\r\n\r\n        //发送10条消息到Topic为TopicTest,tag为TagA,消息内容为“Hello RocketMQ”拼接上i的值\r\n        for (int i = 0; i < 10; i++) {\r\n            try {\r\n                Message msg = new Message(\"TopicTest\",// topic\r\n                        \"TagA\",// tag\r\n                        (\"Hello RocketMQ \" + i).getBytes(RemotingHelper.DEFAULT_CHARSET)// body\r\n                );\r\n                \r\n                //调用producer的send()方法发送消息\r\n                //这里调用的是同步的方式,所以会有返回结果\r\n                SendResult sendResult = producer.send(msg);\r\n                \r\n                //打印返回结果,可以看到消息发送的状态以及一些相关信息\r\n                System.out.println(sendResult);\r\n            } catch (Exception e) {\r\n                e.printStackTrace();\r\n                Thread.sleep(1000);\r\n            }\r\n        }\r\n\r\n        //发送完消息之后,调用shutdown()方法关闭producer\r\n        producer.shutdown();\r\n    }\r\n}\r\n
      \r\n
        \r\n
      1. 消费者
      2. \r\n
      \r\n
      public class Consumer {\r\n\r\n    public static void main(String[] args) throws InterruptedException, MQClientException {\r\n    \r\n        //声明并初始化一个consumer\r\n        //需要一个consumer group名字作为构造方法的参数,这里为consumer1\r\n        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(\"consumer1\");\r\n\r\n        //同样也要设置NameServer地址\r\n        consumer.setNamesrvAddr(\"10.1.54.121:9876;10.1.54.122:9876\");\r\n\r\n        //这里设置的是一个consumer的消费策略\r\n        //CONSUME_FROM_LAST_OFFSET 默认策略,从该队列最尾开始消费,即跳过历史消息\r\n        //CONSUME_FROM_FIRST_OFFSET 从队列最开始开始消费,即历史消息(还储存在broker的)全部消费一遍\r\n        //CONSUME_FROM_TIMESTAMP 从某个时间点开始消费,和setConsumeTimestamp()配合使用,默认是半个小时以前\r\n        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);\r\n\r\n        //设置consumer所订阅的Topic和Tag,*代表全部的Tag\r\n        consumer.subscribe(\"TopicTest\", \"*\");\r\n\r\n        //设置一个Listener,主要进行消息的逻辑处理\r\n        consumer.registerMessageListener(new MessageListenerConcurrently() {\r\n\r\n            @Override\r\n            public ConsumeConcurrentlyStatus consumeMessage(List msgs,\r\n                                                            ConsumeConcurrentlyContext context) {\r\n\r\n                System.out.println(Thread.currentThread().getName() + \" Receive New Messages: \" + msgs);\r\n                \r\n                //返回消费状态\r\n                //CONSUME_SUCCESS 消费成功\r\n                //RECONSUME_LATER 消费失败,需要稍后重新消费\r\n                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;\r\n            }\r\n        });\r\n\r\n        //调用start()方法启动consumer\r\n        consumer.start();\r\n\r\n        System.out.println(\"Consumer Started.\");\r\n    }\r\n}\r\n
      \r\n



      ', '3', '0', '0', '1', '1', '1', '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支持发布/订'), ('32', '1', 'Docker_入门?只要这篇就够了!(纯干货适合0基础小白)', '

      原文地址:https://blog.csdn.net/S_gy_Zetrov/article/details/78161154

      写在前面

      • 这篇博客适合谁?
        • 对于Docker并不了解,只是有一点模糊的感觉,觉得Docker可以当成虚拟机用之类的
        • 只是下载了Docker软件,对于怎么配置,怎么玩,第一步干什么,完全一无所知
        • 本文适用于mac,PC用户不保证文章的效果,现在离开来得及
      • 网上Docker相关的教程都泛滥了,为啥还要写,为啥我要看你的文章
        • 首先欢迎你,能搜索到我的博客就是缘分
        • 其次,确实,现在Docker相关的文章真的太多了,那我为什么还要写呢?原因有三:
          • 其一,文章是很多,各种1小时入门什么的,相信你已经在知乎看过很多了,我也看过,
            因为我就是从知乎那几篇教程为起点,自己抠出来的,现在把自己的心得和经验贴出来,我觉得既是对自己负责也能服务他人。
          • 其二,我从对Docker只有及其模糊(真的是模糊,我除了知道Docker是跟虚拟系统有关其他啥都不知道),到现在起码一
            问一不知(对应一问三不知)的状态,大概花了8个小时。这期间全靠我从网络上搜索到的资料。现在用我的话写出来,我觉得对于跟我相同开始情况的小白用户,能省不少时间,所以我要写。
          • 其三,因为我自己是纯靠搜索到的教程和技术文章学习的Docker,我自然是懂得干涩的语言对学习的拖慢。所以我自己写的时候,自然会考虑到这个问题。本文尽量不用技术用语,尽量使用通俗易懂的文字,为进一步拉低Docker的入门门槛作出我自己的贡献。
      • 这篇文章主要是哪方面的Docker文章,纯入门?你做了哪些工作?
        • 很高兴你问了我这个问题。本文主要:
          • 首先对于Docker的概念作出我的解释,尽量通俗易懂
          • 接着针对我用Docker的目的,从最开始的软件下载,到最后push一个自己的镜像到hub,整个过程以白话的形式描述出来,降低入门门槛,节省你的学习时间
        • 我做了哪些工作?这是个好问题!
          • 首先,我一直都很好奇Docker这个东西,但从来没有机会去使用它。但是,这学期OS课的lab,纯Linux系统编程,虽然使用双系统或者虚拟机都是纯小学生的难易程度了,也不麻烦,但我想,为什么不用Docker呢?用双系统或者虚拟机也太过小儿科!所以,我就这样接触了Docker
          • 那么我使用Docker实现了什么?在lab中,我们需要用到的是纯终端下的gcc工具链还有vim等,那么这就是我需要的全部软件了!于是我在Docker中实现了「gcc+gdb
            +vim」环境,并push到了hub中。
      • 你这篇博客有没有抄别人的?
        • 额,我学习Docker时确实看了很多很多(真的是很多)文章,现在写这篇博客,除了自己的经验心得,自然还得去看那些文章。但我能保证的是,我写的都是取精华去糟粕,不然这篇文章就不必存在了你说是不是?
        • 参考资料会在文后统一附上,谢谢!

      好了,闲话说完,我们开始吧!

       

      入门Docker,你要下载什么?注册什么?

      + Docker.app你肯定是要下载的!

      Docker for mac,这个你肯定要下载:点我下载Docker for mac的社区免费版本

       

      + 注册Docker官方账号,你需要它!

      注册一个Docker的官方账号,有利而无害,相信我!点我注册Docker Cloud官方账号

      Docker安装好,账号也注册后,点击桌面顶栏的Docker图标,点击sign in,登陆你的Docker账号。

       

      + 注册DaoCloud账号,获取加速服务!

      不可否认,有时直接从Docker官方往本地pull镜像会十分缓慢。。。这时我们可以通过国内的Docker服务提供商免费获取加速pull镜像服务,阿里网易好像都有这种服务,我选择的是DaoCloud:点我注册DaoCloud账号

      注册后登陆DaoCloud,找到这个按钮:

      \"docker1.png\"

      接着,按照它的步骤,为自己添加Docker加速服务

      \"docker2\"

      + 到此准备工作结束,进入下一部分!

       

      要想入门Docker,首先你需要理解Docker!

      我也不扯淡了,直接上我对Docker的理解:(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)

      Docker,可以说是一个终端命令行的虚拟机,但更准确的说法,其实应该是一个虚拟环境。比如,你想要在PC上无缝使用Linux么?那么虚拟机并不是你唯一的出路,你还有Docker!我更愿意称Docker为一个容器,当然这只是Docker的一个狭义解释,Docker不止是一个容器。Docker包含3个重要概念:

      • 一个,是镜像(Image),镜像是静态的、可以被用户互相分享的文件。我们玩过双系统和虚拟机的人都知道,首先你需要一个.iso镜像,才能安装系统。Docker中的镜像也是这个东西,镜像是静态的,你不能对他操作,只能pull别人的镜像或者push自己的镜像
      • 还有一个,是容器(Container),前面说过,镜像是静态不可操作的,只能被分享和下载,那什么是能被操作的呢?就是容器里!容器可以理解为镜像的动态状态,也就是我们虚拟机中系统装好后的状态,其实这么说是不对的,容器最符合的描述应该是Linux的iso文件的Live CD模式,比如我们玩双系统时都进入过Live CD模式,不安装系统而直接进入系统,很神奇是吧,Docker的容器就是这个概念,只不过更加轻量更加迅速便捷。但是Live CD的害处就是你关机后作出的修改安装的软件全部gg,容器也是一样,一旦被直接推出,之前安装的gcc啊vim啊啥的就会全部gg掉。如果要保存修改,就需要将当前容器封装成一个新的镜像,这样下次启动这个新的镜像后之前作出的修改还都在
      • 最后,是仓库(Repository)。各位在前面看到我写的pull和push什么的,有没有晕?不知道各位对于git熟悉不熟悉,Docker中的仓库很像git的代码仓库,你可以pull自己之前push到自己仓库的镜像到本地,也可以pull别人push到公共仓库的镜像到自己本地。说白了就是百度云盘,你可以上传(push)自己做好环境的Docker上去,也可以下载(pull)自己云端的镜像到本地。同时,我们知道百度云最大的特点就是分享(你懂的嘿嘿嘿),类比Docker,如果你得到百度云分享链接(别人的镜像名字、标签和别人的用户名),你还可以下载(pull)别人分享的镜像到自己的本地,别人也可以下载(pull)你的镜像,因为Docker仓库都是公共的。当然,每个免费用户有一个名额把自己的一个镜像设为私有,也就是禁止被分享给别人,类比百度云上你自己保存的而没有被生成分享链接的小姐姐。

      接下来来张高大上的概念图,各位看个热闹2333

      \"docker3.png\"

      接下来就是实战了!打开你的iTerm2!(我是iTerm2党2333,打开普通terminal也行,这个不影响)

       

      + 查看Docker版本信息

      终端输入:\r\ndocker version

       

      显示的我的版本信息

       ~$ docker version\r\nClient:\r\n Version:      17.06.2-ce\r\n API version:  1.30\r\n Go version:   go1.8.3\r\n Git commit:   cec0b72\r\n Built:        Tue Sep  5 20:12:06 2017\r\n OS/Arch:      darwin/amd64\r\n ...\r\n //omitted by sgy(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)\r\n ...

      不过我一般不需要那么多信息,所以一直用的是docker -v命令

      + 是时候pull你的第一个镜像下来的!

      Docker安装好后是不会自带镜像的,你需要从仓库自己pull一个镜像下来,自己制作自己的镜像也是一个道理,你可以通过在已有的镜像基础上生成自己的镜像或者看一下这篇博客: 随便百度的如何创建自己Docker镜像得到的教程

      Docker镜像官方好像只提供Linux,这个很容易想,windows和mac是要交钱的吧大概?(否)

      搜索ubuntu的Docker镜像docker search ubuntu,这个如果你想要什么centos,直接改就行,不区分写法,我的返回结果:

      ~$ docker search ubuntu\r\nNAME                                                   DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED\r\nubuntu                                                 Ubuntu is a Debian-based Linux operating s...   6636                [OK]\r\ndorowu/ubuntu-desktop-lxde-vnc                         Ubuntu with openssh-server and NoVNC            131                                     [OK]\r\nrastasheep/ubuntu-sshd                                 Dockerized SSH service, built on top of of...   105                                     [OK]\r\nansible/ubuntu14.04-ansible                            Ubuntu 14.04 LTS with ansible                   86                                      [OK]\r\nubuntu-upstart                                         Upstart is an event-based replacement for ...   80                  [OK]\r\nneurodebian                                            NeuroDebian provides neuroscience research...   40                  [OK]\r\nubuntu-debootstrap                                     debootstrap --variant=minbase --components...   31                  [OK]\r\n...\r\n//omitted by sgy(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)\r\n...

       

      拉取官方最新版的ubuntu镜像:docker pull ubuntu:latest,其中的latest是一个标签(tag),表示是当前最新版本。你应该得到的信息,类似这样的

      ~$ docker pull ubuntu:latest\r\nTrying to pull repository docker.io/library/ubuntu ... \r\nlatest: Pulling from docker.io/library/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 for docker.io/ubuntu:latest

      你输入的命令实际上相当于docker pull registry.hub.docker.com/ubuntu:latest命令,即从注册服务器registry.hub.docker.com中的名为ubuntu的仓库中下载标签为latest的镜像

       

      由于我的lab统一要求,ubuntu需要14.04版本,所以我在hub里面搜了搜,找到了一个用户分享的14.04 64位纯净镜像(base image),下面将他的镜像扒下来

      ~$ docker pull chug/ubuntu14.04x64

      这个用户还有很多其他版本的ubuntu系统,12 13 14的32位64位都有,全是纯净镜像。

      + 查看你本地的镜像仓库!

      把初始镜像拉下来后,就可以启动它了,不过,可以先使用docker images命令查看你自己的本地镜像,我随便找了个例子,你的也应该是类似这样的:

      ~$ docker images\r\nREPOSITORY          TAG                 IMAGE ID            CREATED             SIZE\r\ndocker.io/ubuntu    16.04               e4415b714b62        11 days ago         128.1 MB\r\ndocker.io/ubuntu    latest              e4415b714b62        11 days ago         128.1 MB\r\ndocker.io/ubuntu    12.04               aefa163f7a7e        11 days ago         103.5 MB\r\ndocker.io/centos    latest              0584b3d2cf6d        3 weeks ago         196.5 MB

       

      从网上一个教程中找到以下说法,比我说的好,那就看这个吧!

      在列出信息中,可以看到几个字段信息:

      • 来自于哪个仓库,比如 ubuntu
      • 镜像的标记,比如 16.04
      • 它的 ID 号(唯一),比如e4415b714b62
      • 创建时间
      • 镜像大小

      其中镜像的 ID 唯一标识了镜像,注意到 ubuntu:16.04 和 ubuntu:latest具有相同的镜像 ID ,说明它们实际上是同一镜像。 TAG 信息用来标记来自同一个仓库的不同镜像。例如 ubuntu 仓库中有多个镜像,通过 TAG 信息来区分发行版本,例如10.04 、 12.04 、 12.10 、 13.04 、 14.04 等。例如可以使用docker run -t -i ubuntu:16.04 /bin/bash命令指定使用镜像ubuntu:16.04来启动一个容器。如果不指定具体的标记,则默认使用latest标记信息。

      + 启动你的镜像并尽情安装软件吧!

      以下内容均以我自己pull下来的chug的初始镜像为例:

      现在你已经有一个初始的镜像了,注意这个里面是什么都没有的,连vim都没装,是精简到不能再精简的镜像了。

      首先启动它:

      $ docker run -it chug/ubuntu14.04x64 /bin/bash\r\nroot@aa97ba3292ce:/#

      -it 表示运行在交互模式,是-i -t的缩写,即-it是两个参数:-i和-t。前者表示打开并保持stdout,后者表示分配一个终端(pseudo-tty)一般这个模式就是可以启动bash,然后和容器有命令行的交互

      可以看到我们终端的字头变成root@aa97ba3292ce:/#了,这就意味着我们的镜像已经创建了一个容器实例。现在我们可以对这个“ubuntu系统”进行操作了

       

      比如安装vim:

      root@aa97ba3292ce:/# apt-get install vim\r\nReading package lists... Done\r\nBuilding dependency tree... Done\r\nThe following extra packages will be installed:\r\n  file libexpat1 libffi6 libgpm2 libmagic1 libpython2.7 libpython2.7-minimal libpython2.7-stdlib libsqlite3-0 libssl1.0.0 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  file libexpat1 libffi6 libgpm2 libmagic1 libpython2.7 libpython2.7-minimal libpython2.7-stdlib libsqlite3-0 libssl1.0.0 mime-support vim vim-common vim-runtime\r\n0 upgraded, 14 newly installed, 0 to remove and 0 not upgraded.\r\nNeed to get 10.7 MB of archives.\r\nAfter this operation, 50.7 MB of additional disk space will be used.\r\nDo you want to continue? [Y/n]

      看到我没有用sudo,是因为本来就已经是超级用户(root)状态了。

      同理按照我自己的需求,我安装了gcc和gdb

      + 想退出容器?很简单!

      如果使用exit,命令退出,则容器的状态处于Exit,而不是后台运行。如果想让容器一直运行,而不是停止,可以使用快捷键 ctrl+p ctrl+q 退出,此时容器的状态为Up。

      查看当前正在运行的容器:

      ~$ docker ps\r\nCONTAINER ID        IMAGE                 COMMAND             CREATED             STATUS              PORTS               NAMES\r\naa97ba3292ce        chug/ubuntu14.04x64   \"/bin/bash\"         7 minutes ago       Up 7 minutes                           relaxed_hoover

       

      看到当前有一个ID为aa97ba3292ce的容器(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)

      启动、停止、重启容器aa97ba3292ce的命令:

      ~$ docker start aa97ba3292ce\r\n~$ docker stop aa97ba3292ce\r\n~$ docker restart aa97ba3292ce

       

      后台启动一个容器后,如果想进入到这个容器,可以使用attach命令

      ~$ docker attach aa97ba3292ce

      + 软件装完,想保存环境?commit帮你!

      将容器转化为一个镜像,即执行commit操作,完成后可使用docker images查看

      root@aa97ba3292ce:/#exit //先退出容器\r\n~$ docker commit -m \"ubuntu with vim\" -a \"sgy\" aa97ba3292ce sgy/ubuntu:vim\r\n~$ docker images\r\nREPOSITORY                    TAG    IMAGE ID         CREATED             SIZE\r\nsgy/ubuntu                    vim    52166e4475ed     5 seconds ago       358.1 MB\r\nchug/ubuntu14.04x64           latest 0584b3d2cf6d     9 days ago          196.5 MB

       

      其中,-m指定说明信息;-a指定用户信息;aa97ba3292ce代表容器的id;sgy/ubuntu:vim指定目标镜像的用户名、仓库名和 tag 信息。我这里都是为了博客瞎编的用户名,我自己的用户名也不是sgy,你运行命令的时候使用自己注册Docker时的用户名。

      此时Docker中就有了我们新建的镜像sgy/ubuntu:vim,此镜像和原有的ubuntu镜像区别在于多了个vim工具。此时我们利用新镜像创建的容器,本身就自带vim了。

      启动新创建的镜像,可以看到vim已经自带了。

      ~$ docker run -it sgy/ubuntu:vim /bin/bash\r\nroot@520afc596c51:/# vim --version\r\nVIM - Vi IMproved 7.4 (2013 Aug 10, compiled Apr  4 2017 18:14:54)\r\n...\r\n//omitted by sgy(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)\r\n...

      利用exit退出容器。此时Docker引擎中就有了两个容器,可使用docker ps -a查看。

      + 想要删除容器或者镜像?用这个!

      如果想删除容器或者镜像,可以使用rm命令,注意:删除镜像前必须先删除以此镜像为基础的容器(哪怕是已经停止的容器),否则无法删除该镜像,会报错Failed to remove image (e4415b714b62): Error response from daemon: conflict: unable to delete e4415b714b62 (cannot be forced) - image has dependent child images类似这种

      ~$ docker rm container_id\r\n~$ docker rmi image_id

       

      有的时候尽管删除了全部容器,镜像还是无法删除,这时点击mac顶栏中的docker logo,选择restart,然后再试一次rmi,应该就没问题了。(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)

      + 附上一张高大上的Docker命令图

      \"docker4.png\"

      + 一次配置,到处使用?那就push到hub上吧!

      因为之前已经在Docker.app中登陆了Docker账号,所以现在直接

      ~$ docker push sgy/ubuntu:vim

      就可以了!

      下次到了机房,在ubuntu系统中安装Docker,配置好加速器,启动Docker,docker login登陆自己的账号,然后直接~$ docker pull sgy/ubuntu:vim就可以把你push到hub的已经配置好的环境的镜像给扒下来。做完实验,再push上去就ok了!

      Docker中安装gcc、gdb时遇到的问题

      + add-apt-repository: command not found

      14.04系统解决办法:

      apt-get install software-properties-common

       

      older版本的系统:

      apt-get install python-software-properties

      + Docker中使用gdb无法进入断点,无法调试

      加上--privileged参数

      ~$ docker run -it --privileged sgy/ubuntu:vim /bin/bash
      ', '7', '0', '0', '1', '1', '1', '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软件,对于怎么配置,怎么玩,第一步干什么,'), ('33', '1', 'MySQL常用命令语句', '

      常用的语句

      查询 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 name=\"jack\";

      删除 delete form bbs where id=2;

      字符串替换: UPDATE `category` SET guid = REPLACE ( guid, \'articles\', \'article\' );

       

      更多

      1.进入数据库:

      1. mysql -u root -p
      2. mysql -h localhost -u root -p database_name

      2.列出数据库:

      1. show databases;

      3.选择数据库:

      1. use databases_name;

      4.列出数据表:

      1. show tables;

      5.显示表格列的属性:

      1. show columns from table_name;
      2. describe table_name;

      6.导出整个数据库:

      1. mysqldump -u user_name -p database_name > /tmp/file_name

      例如:mysqldump -u root -p test_db > d:/test_db.sql

      7.导出一个表:

      1. mysqldump -u user_name -p database_name table_name > /tmp/file_name

      例如:mysqldump -u root -p test_db table1 > d:/table1.sql

      8.导出一个数据库结构:

      1. mysqldump -u user_name -p -d --add--table database_name > file_name

      例如:mysqldump -u root -p -d --add-drop-table test_db > test_db.sql

      9.导入数据库:

      1. source file_name;
      2. mysql -u user_name -p database_name < file_name

      例如:

      source /tmp/bbs.sql;

      source d:/bbs.sql;

      mysql -u root -p bbs < \"d:/bbs.sql\"

      mysql -u root -p bbs < \"/tmp/bbs.sql\"

      10.将文本文件导入数据表中(excel与之相同)

      1. load data infile \"tables.txt\" into table table_name;

      例如:

      load data infile \"/tmp/bbs.txt\" into table bbs;

      load data infile \"/tmp/bbs.xls\" into table bbs;

      load data infile \"d:/bbs.txt\" into table bbs;

      load data infile \"d:/bbs.xls\" into table bbs;

      11.将数据表导出为文本文件(excel与之相同)

      1. select * into outfile \"path_file_name\" from table_name;

      例如:

      select * into outfile \"/tmp/bbs.txt\" from bbs;

      select * into outfile \"/tmp/bbs.xls\" from bbs where id=1;

      select * into outfile \"d:/bbs.txt\" from bbs;

      select * into outfile \"d:/bbs.xls\" from bbs where id=1;

      12.创建数据库时先判断数据库是否存在:

      1. create database if not exists database_name;

      例如:create database if not exists bbs

      13.创建数据库:

      1. create database database_name;

      例如:create database bbs;

      14.删除数据库:

      1. drop database database_name;

      例如:drop database bbs;

      15.创建数据表:

      1. mysql> create table  ( <column 1 name ,<column 2 name , ...);

      例如:create table (id int not null auto_increment primary key,name char(16) not null default \"jack\",date_year date not null);

      16.删除数据表中数据:

      1. delete from table_name;

      例如:

      delete from bbs;

      delete from bbs where id=2;

      17.删除数据库中的数据表:

      1. drop table table_name;

      例如:

      drop table test_db;

      rm -f database_name/table_name.* (linux下)

      例如:

      rm -rf bbs/accp.*

      18.向数据库中添加数据:

      1. insert into table_name set column_name1=value1,column_name2=value2;

      例如:insert into bbs set name=\"jack\",date_year=\"1993-10-01\";

      1. insert into table_name values (column1,column2,...);

      例如:insert into bbs (\"2\",\"jack\",\"1993-10-02\")

      1. insert into table_name (column_name1,column_name2,...) values (value1,value2);

      例如:insert into bbs (name,data_year) values (\"jack\",\"1993-10-01\");

      19.查询数据表中的数据:

      1. select * from table_name;

      例如:select * from bbs where id=1;

      20.修改数据表中的数据:

      1. update table_name set col_name=new_value where id=1;

      例如:update bbs set name=\"tom\",age=18 where name=\"jack\";

      21.增加一个字段:

      1. alter table table_name add column field_name datatype not null default \"1\";

      例如:alter table bbs add column tel char(16) not null;

      22.增加多个字段:(column可省略不写)

      1. alter table table_name add column filed_name1 datatype,add column filed_name2 datatype;

      例如:alter table bbs add column tel char(16) not null,add column address text;

      23.删除一个字段:

      1. alter table table_name drop field_name;

      例如:alter table bbs drop tel;

      24.修改字段的数据类型:

      1. alter table table_name modify id int unsigned;//修改列id的类型为int unsigned
      2. alter table table_name change id sid int unsigned;//修改列id的名字为sid,而且把属性修改为int unsigned

      25.修改一个字段的默认值:

      1. alter table table_name modify column_name datatype not null default \"\";

      例如:alter table test_db modify name char(16) default not null \"yourname\";

      26.对表重新命名:

      1. alter table table_name rename as new_table_name;

      例如:alter table bbs rename as bbs_table;

      1. rename table old_table_name to new_table_name;

      例如:rename table test_db to accp;

      27.从已经有的表中复制表的结构:

      1. create table table2 select * from table1 where 1<>1;

      例如:create table test_db select * from accp where 1<>1;

      28.查询时间:

      1. select now();

      29.查询当前用户:

      1. select user();

      30.查询数据库版本:

      1. select version();

      31.创建索引:

      1. alter table table1 add index ind_id(id);
      2. create index ind_id on table1(id);
      3. create unique index ind_id on table1(id);//建立唯一性索引

      32.删除索引:

      1. drop index idx_id on table1;
      2. alter table table1 drop index ind_id;

      33.联合字符或者多个列(将id与\":\"和列name和\"=\"连接)

      1. select concat(id,\':\',name,\'=\') from table;

      34.limit(选出10到20条)

      1. select * from bbs order by id limit 9,10;

      (从查询结果中列出第几到几条的记录)

      35.增加一个管理员账号:

      1. grant all on *.* to user@localhost identified by \"password\";

      36.创建表是先判断表是否存在

      1. create table if not exists students(……);

      37.复制表:

      1. create table table2 select * from table1;

      例如:create table test_db select * from accp;

      38.授于用户远程访问mysql的权限

      1. grant all privileges on *.* to \"root\"@\"%\" identified by \"password\" with grant option;

      或者是修改mysql数据库中的user表中的host字段

      1. use mysql;
      2. select user,host from user;
      3. update user set host=\"%\" where user=\"user_name\";

      39.查看当前状态

      1. show status;

      40.查看当前连接的用户

      1. show processlist;

      (如果是root用户,则查看全部的线程,得到的用户连接数同show status;里的 Threads_connected值是相同的)

      参考:http://www.92csz.com/56/992.html

      本文地址:https://liuyanzhao.com/5306.html

      ', '11', '0', '0', '1', '1', '1', '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 (1, 1, '[转载]SpringMVC中使用Interceptor拦截器', '

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


      一、定义Interceptor实现类

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


      (一)实现HandlerInterceptor接口

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


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


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


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


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


      import javax.servlet.http.HttpServletRequest;

      import javax.servlet.http.HttpServletResponse;

      import org.springframework.web.servlet.HandlerInterceptor;

      import org.springframework.web.servlet.ModelAndView;

      public class SpringMVCInterceptor implements HandlerInterceptor {

          /** 

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

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

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

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

           */

          @Override

          public boolean preHandle(HttpServletRequest request,

                  HttpServletResponse response, Object handler) throws Exception {

              // TODO Auto-generated method stub  

              return false;

          }

          /** 

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

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

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

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

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

           */

          @Override

          public void postHandle(HttpServletRequest request,

                  HttpServletResponse response, Object handler,

                  ModelAndView modelAndView) throws Exception {

              // TODO Auto-generated method stub  

          }

          /** 

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

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

           */

          @Override

          public void afterCompletion(HttpServletRequest request,

                  HttpServletResponse response, Object handler, Exception ex)

          throws Exception {

              // TODO Auto-generated method stub  

          }

      }

      (二)实现WebRequestInterceptor 接口

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


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


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


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


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


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


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


       


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


      import org.springframework.ui.ModelMap;

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

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

      public class AllInterceptor implements WebRequestInterceptor {

          /** 

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

           */

          @Override

          public void preHandle(WebRequest request) throws Exception {

              // TODO Auto-generated method stub  

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

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

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

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

          }

          /** 

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

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

           */

          @Override

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

              // TODO Auto-generated method stub  

              for (String key:map.keySet())

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

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

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

          }

          /** 

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

           */

          @Override

          public void afterCompletion(WebRequest request, Exception exception)

          throws Exception {

              // TODO Auto-generated method stub  

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

          }

      }


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

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


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

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

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

      下面是我的声明示例:




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


       


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



          

          

          

              

              

              

          



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


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


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


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


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




      ', 27, 0, 3, 1, 1, 1, '2018-11-25 20:51:52', '2017-10-06 23:54:18', NULL, '/img/thumbnail/random/img_1.jpg'); +INSERT INTO `article` VALUES (2, 2, 'springmvc 表单中文乱码解决方案', '

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

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

      拦截器代码如下

      1.   
      2.   
      3. <filter>  
      4.     <filter-name>CharacterEncodingFilterfilter-name>  
      5.     <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>  
      6.       
      7.     <init-param>  
      8.       <param-name>encodingparam-name>  
      9.       <param-value>UTF-8param-value>  
      10.     init-param>  
      11.       
      12.     <init-param>  
      13.       <param-name>forceEncodingparam-name>  
      14.       <param-value>trueparam-value>  
      15.     init-param>  
      16. filter>  
      17.   
      18. <filter-mapping>  
      19.     <filter-name>CharacterEncodingFilterfilter-name>  
      20.     <url-pattern>/*url-pattern>  
      21. filter-mapping>  


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

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

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

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

       

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

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

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

       

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

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

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

      ', 16, 2, 1, 1, 1, 1, '2018-11-25 20:52:13', '2017-10-07 12:12:42', NULL, '/img/thumbnail/random/img_2.jpg'); +INSERT INTO `article` VALUES (4, 1, '[转载]HTTP的幂等性', '

      理解HTTP幂等性

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

      幂等性定义

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

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

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

      分布式事务 vs 幂等设计

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

      bool withdraw(account_id, amount)\r\n

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

      \"20171007153621950.png\"

      图1

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

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

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

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

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

      \"20171007153648208.png\"

      图2

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

      HTTP的幂等性

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

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

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

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

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

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

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

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

      总结

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

      参考

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

      The Importance of Idempotence

      Stackoverflow - PUT vs POST in REST

       

       

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

      ', 13, 0, 0, 1, 1, 1, '2018-11-25 20:51:05', '2017-10-07 15:37:20', NULL, '/img/thumbnail/random/img_4.jpg'); +INSERT INTO `article` VALUES (3, 2, '使用rapid-framework继承jsp页面', '

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

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

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

      父页面

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

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

        子页面

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

      •  <%@ include file=\"base.jsp\"%>引入父页面
      • 重写父页面中叫做content的部分。
      • 访问子页面,父页面内部的元素不会显示,代替的是子页面重写的内容。
      • ', 2, 0, 0, 1, 1, 1, '2018-11-25 20:54:02', '2017-10-07 15:30:17', NULL, '/img/thumbnail/random/img_3.jpg'); +INSERT INTO `article` VALUES (5, 1, 'JDBC常用API和使用', '

        一、JDBC 常用 API

        1、Connection 接口

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

        2、Statement 接口

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

         execute是executeQuery和executeUpdate的综合.

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

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

        3、PreparedStatement 接口

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

        DML 语句:SELECT、UPDATE、INSERT、DELETE

        DLL 语句:CREATE DROP ALERT

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

        4、ResultSet 接口

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

        二、案例

        1、往数据库里添加数据

        ① 数据表结构如下

        \"2017100715402014.png\"

        ② 代码如下

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

        ③运行结果

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

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

        ①数据表如下

        \"20171007154127663.png\"

        ②代码如下

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

        ③ 运行结果

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

        \"20171007154222212.jpg\"

        ', 0, 0, 0, 1, 1, 1, '2018-11-25 20:51:27', '2017-10-07 15:42:43', NULL, '/img/thumbnail/random/img_5.jpg'); +INSERT INTO `article` VALUES (6, 1, 'Hibernate 简单的CURD操作', '

        一、单表操作 CURD 实例

        • save
        • update
        • delete
        • get/load (查询单个记录)

         

        二、代码实现

        StudentTest.java

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

         

        三、get 和 load 的区别

        区别一、

        • 不考虑缓存的情况下,get 方法会在调用之后,立即向数据库发送 sql 语句,返回持久化对象。
        • load 方法会在调用后返回一个持久化对象。该代理对象只保留了实体对象的 id,直到使用实体对象的非主键属性时才发出 sql 语句。

        区别二、

        • 查询数据库中不存在的数据时,get 方法返回 null 。
        • load 方法返回异常 org.hibernate.ObjectNotFoundException

        p


        ', 4, 2, 0, 1, 1, 1, '2018-11-25 20:52:33', '2017-10-07 15:49:54', NULL, '/img/thumbnail/random/img_6.jpg'); +INSERT INTO `article` VALUES (7, 2, 'Hibernate 基本类型', '

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

        一、日期类型

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

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

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

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

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

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

        二、对象类型

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

        1、Student 类中 成员变量如下

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

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

        3、在测试类添加 testWriteBlob方法

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

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

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

        \"2017100715505630.png\"

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

        在测试类中添加 testReadBlob 方法

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

        6、使用 Junit Test 运行 testReadBlob 方法

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

         

        \"20171007155038568.png\"

        三、组件属性

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

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

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

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

        2、Address 类 部分代码如下

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

        3、修改 Student.hbm.xml

        将之前的

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

        改成

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

        注意:单词不要打错

        4、修改 测试类 testSaveStudent 代码

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

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

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

        6、用 Junit Test 运行 testSaveStudent 方法

        运行成功,查看数据表

        \"2017100715512437.jpg\"

        查看 表结构

        \"20171007155135604.jpg\"

         

        ', 1, 0, 0, 1, 1, 1, '2018-11-25 20:55:13', '2017-10-07 15:51:57', NULL, '/img/thumbnail/random/img_7.jpg'); +INSERT INTO `article` VALUES (8, 1, 'Mybatis高级映射多对多查询', '

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

        一、开发准备

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

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

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

        \"20171007155334244.png\"

        \"20171007155349805.png\"


        \"20171007155410485.png\"


        \"20171007155450773.png\"

        2、思路分析

        \"20171007155545742.png\"

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

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

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

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

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

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

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

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

        我们先看一下查询结果:

        \"2017100717420134.png\"


        二、代码实现

        1、四个持久化类

        ① User.java

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

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

         

        ② Items.java

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

         

        ③ Orders.java

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

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

         

        ④ Orderdetail.java

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

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

         

        2、订单代理 即mapper.java

        OrdersMapperCustom.java

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

         

        3、OrdersMapperCustom.xml    映射文件

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

         

        4、测试类 OrderMapperCustomTest.java

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

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

         

        小结

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

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

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

        ', 9, 3, 0, 1, 1, 1, '2018-11-25 20:53:43', '2017-10-07 15:56:00', NULL, '/img/thumbnail/random/img_8.jpg'); +INSERT INTO `article` VALUES (9, 1, 'Servlet 实现验证码', '

        分析

        生成图片实现类

        ImageServlet 类

        ① 定义BufferedImage 对象

        ② 获得 Graphics 对象

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

        ④ 使用 Graphics 绘制图片

        ⑤ 记录验证码信息到 session 中

        ⑥ 使用 ImageIO 输出图片

        代码实现

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

        index.jsp

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

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

        ImageServlet.java

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

        LoginServlet.java

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

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

        1. <servlet>
        2.      <servlet-name>ImageServletservlet-name>
        3.      <servlet-class>com.liuyanzhao.ImageServletservlet-class>
        4.  servlet>
        5.  <servlet-mapping>
        6.      
        7.      <servlet-name>ImageServletservlet-name>
        8.      <url-pattern>/ImageServleturl-pattern>
        9.  servlet-mapping>
        10.  <servlet>
        11.      <servlet-name>LoginServletservlet-name>
        12.      <servlet-class>com.liuyanzhao.LoginServletservlet-class>
        13.  servlet>
        14.  <servlet-mapping>
        15.      
        16.      <servlet-name>LoginServletservlet-name>
        17.      <url-pattern>/LoginServleturl-pattern>
        18.  servlet-mapping>

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

        效果如下

        \"20171007155723341.png\"

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

        ', 5, 2, 0, 1, 1, 1, '2018-11-25 20:53:29', '2017-10-07 15:57:38', NULL, '/img/thumbnail/random/img_9.jpg'); +INSERT INTO `article` VALUES (10, 1, 'Integer与int的种种比较你知道多少?', '

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

        首先看代码:

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

        详解如下:

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

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

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

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

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

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

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

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

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

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

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

         

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


        ', 1, 1, 1, 1, 1, 1, '2018-11-25 20:52:45', '2017-10-07 15:58:51', NULL, '/img/thumbnail/random/img_0.jpg'); +INSERT INTO `article` VALUES (11, 1, 'Java如何让程序一直运行,不停止', '

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

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

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

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

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

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

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

         

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

        demo1:构造方法

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

        demo2:普通代码块

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

        demo3:构造代码块

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

        demo4:静态代码块

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

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

         

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

         

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

         

        详细解释:

        一、DDL is Data Definition Language statements

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

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

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

        3.DROP - delete objects from the database   删除

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

        TRUNCATE TABLE [Table Name]。

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

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

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

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

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

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

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

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

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

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

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

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

         

        二、DML is Data Manipulation Language statements

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

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

        2.INSERT - insert data into a table                    添加

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

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

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

        6.EXPLAIN PLAN - explain access path to data

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

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

         

        三、DCL is Data Control Language statements

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

        1.COMMIT - save work done 提交

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

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

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


        ', 1, 3, 1, 1, 1, 1, '2018-11-25 20:47:30', '2017-10-07 16:00:50', NULL, '/img/thumbnail/random/img_3.jpg'); +INSERT INTO `article` VALUES (14, 1, 'execute、executeUpdate、executeQuery三者的区别', '

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

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

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

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

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

               execute是executeQuery和executeUpdate的综合.

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

        -----

         

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

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

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

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

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

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

        ', 5, 3, 0, 1, 1, 1, '2018-11-25 20:45:25', '2017-10-07 16:02:12', NULL, '/img/thumbnail/random/img_4.jpg'); +INSERT INTO `article` VALUES (15, 1, 'JSP 中 forward 转发 和 sendRedirect 重定向的区别', '

        本质区别:

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

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

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

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

               3.一种解释:

        假设你去办理某个执照

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

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

        工作流程:

        1.重定向

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

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

        2.转发

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

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

         

        调用方式:

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

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

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

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

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

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

         

             1.重定向:
                  1).response.sendRedirect(url);
                  2).response.setState(302);
                       response.setHeader(\"location\",url);
             2.转发:
                  1).request.getRequestDispatcher(url).forward(request,response);
                  2).request.getRequestDispatcher(url).include(request,response);
                  3).
        ', 1, 0, 0, 1, 1, 1, '2018-11-25 20:47:10', '2017-10-07 16:03:08', NULL, '/img/thumbnail/random/img_5.jpg'); +INSERT INTO `article` VALUES (16, 1, 'Java中File类的使用', '

        一、File类常用API介绍

        内容见代码和注释

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

         

        二、遍历目录

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

        一、问题

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

        执行结果是icon无法显示

         

        二、解决

        类中有如下调用:

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

         

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

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

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

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

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

         

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

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

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

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

         

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

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

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

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

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

         

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

         

        三、其他

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

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

        Public ImageIcon(URL url)

         

        第一种构造不在赘述。

        第二种通过URL来得到图片

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

        ImageIcon icon = new ImageIcon(url);

         

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

        ', 12, 5, 0, 1, 1, 1, '2018-11-25 20:46:27', '2017-10-07 16:05:55', NULL, '/img/thumbnail/random/img_7.jpg'); +INSERT INTO `article` VALUES (27, 1, 'SpringCloud 中使用 Eureka 和 Feign 实现增删改查', '

        在没有接触微服务或者分布式项目之前,我们的项目时没有分层的,我们的服务也是没有分层的。

        比如,我们有5台服务器使用 Nginx 做负载均衡,上面都是安装了我们的 Tomcat 服务器,部署的一样的项目。当用户访问某个请求的时候,按照负载均衡的机制(随机、轮询之类)来转发到对应的服务器上,然后在该服务器上先是到 Controller,然后 Service,再到 DAO,然后返回给 Service,返回给 Controller,最后返回给前台。似乎这样也行。

        但是当我们网站访问量不断飙升的时候,似乎有点撑不住了,只能加服务器,成本也明显增加。我们能不能再分一分呢 ?

        同时,我们发现真正比较耗费时间的是一些 IO 操作,比如数据库操作、上传文件之类的。为了方便开发,我们可以把 DAO 和 Service 层放在一起,暂且称为业务层。业务层耗费的远远大于 Controller 层的。我们可以在 5 台服务器中拿 1 台部署 Controller 层,另外 4 台部署业务层代码。

        如果将他们连接起来呢?

        Eureka + Feign!

        Eureka 做注册中心,业务层和控制器层每一个实例可以作为一个服务,比如 UserService 是一个服务,UserController 也可以是一个服务,一个服务可以有多个状态(可以有多台机器注册到该服务)。

        下面通过一个例子来讲解

         

        源码地址:https://github.com/saysky/spring-cloud-demo

         

        一、下载源码

        请上 Github 上下载本例子源码

        源码地址:https://github.com/saysky/spring-cloud-demo

         

        二、项目结构

        注意这是三个项目(不是一个项目),用 IDEA 打开分别打开三个项目,最终如图

        \"SpringCloud

         

        1、eureka-server(一个 SpringBoot 项目)

        \"SpringCloud

         

         

        2、cloud (一个多模块项目,API + 业务层)   \"SpringCloud

         

         

        (1)其中 cloud-api 是 api,里面有 model 和 service接口,cloud-core 和 cloud-web 将依赖它

        到时候需要首先将该模块 install,即打包到本地仓库中

        \"SpringCloud

         

        (2)其中 cloud-core,里面 dao 和 serviceImpl,作为生产者的存在

        启动类在这里面

        \"SpringCloud

         

        3、cloud-web( 一个 SpringBoot 项目)

        只有控制器层,需要依赖 cloud-api ,作为消费者的存在(可以是其他人调用)

        \"SpringCloud

         

        三、如何启动

        1、新建数据库 summer,导入 user.sql

        2、导入新项目,先后顺序随便(如eureka-server、cloud、cloud-web)

        3、启动 eureka-server 项目,端口为 8761

        4、对 cloud 项目中的 cloud-api 模块进行install,maven命令是 mvn clean install

        5、启动 cloud-core 项目,端口为 8090

        6、(可选)其实这时候我们可以在地址栏访问一下 localhost:8090/user 其实是可以访问的,如果你不行,说明有问题

        7、启动 cloud-web 项目,端口为 8080

        8、在地址栏访问 localhost:8080/user,如果可以看到数据,说明成功

         

        四、补充

        1、我这里模拟了四台注册了业务层服务(cloud-producer) 和 一台控制器层服务(cloud-consumer)

        \"SpringCloud

        ps:因为我是用本机测试,所以开了不同的端口表示不同的服务器

         

        2、访问控制器层,然后 Feign 会帮我们在注册中心中找到对应的服务名和对应的方法

        我们访问 localhost:8080/user/1 首先到的是控制器层,控制器层调用 userService,userService 是 Cloud-API 里的,添加了 @FeignClient(name=\"cloud-producer\") 注解,然后找到在注册中心中寻找 cloud-producer 的服务,从一台(负载均衡)服务器中找到 userService 的实现类,即 userServiceImpl,也就是进入了 cloud-core,然后完成一系列 DAO 和 Service 操作,最后返回给 Controller。

        \"SpringCloud

        其中 Service 和 Service 的实现都需要加 RequestMapping,Service 实现需要加 @RestController,否则会出现 404

        \"SpringCloud

        所以,其实我们可以直接访问 cloud-core 这一层,在地址栏访问 localhost:8090/user/1

        \"SpringCloud

         

        事实上,我们访问 控制器的时候,当需要某个 Service 的时候,Feign 帮我们向 Service 又发了一个同样的请求。我们可以直接访问这个 Service 请求

        但是,通常我们会在生产环境中,不让别人直接访问到 Service,可以通过一些机制实现,比如内网啦。

         

        六、学习过程中遇到的问题

        1、cloud-web 中注入 userService,一直注入失败,无法扫描到 userServiceImpl

        解决方案:在 cloud-web 启动类上添加

        @EnableFeignClients(basePackages = \"com.liuyanzhao.cloud\")

        \"SpringCloud

         

        2、出现 404 错误

        cloud-core 里的 userServiceImpl 也需要加 @RequestMapping,并且需要将 @Service 改成 @RestController

         

        UserService(cloud-api)

        \"SpringCloud

         

        UserServiceImpl(cloud-core)

        \"SpringCloud

         

        ', 3, 4, 0, 1, 1, 1, '2019-04-25 21:43:14', '2018-11-25 20:56:50', '在没有接触微服务或者分布式项目之前,我们的项目时没有分层的,我们的服务也是没有分层的。比如,我们有5台服务器使用 Nginx 做负载均衡,上面都是安装了我们的 Tomcat 服务器,部署的一样的项目。当用户访问某个请求的时候,按照负载均衡的机制(随机、轮询之类)来转发到对应的服务器上,然后在该服务器', '/img/thumbnail/random/img_7.jpg'); +INSERT INTO `article` VALUES (23, 1, '是是是搜索', '是是', 5, 0, 0, 1, 0, 1, '2018-11-25 23:56:08', '2018-11-25 16:47:05', NULL, '/img/thumbnail/random/img_3.jpg'); +INSERT INTO `article` VALUES (28, 1, 'SpringCloud 中使用 Eureka 和 Feign 实现增删改查', '

        在没有接触微服务或者分布式项目之前,我们的项目时没有分层的,我们的服务也是没有分层的。

        比如,我们有5台服务器使用 Nginx 做负载均衡,上面都是安装了我们的 Tomcat 服务器,部署的一样的项目。当用户访问某个请求的时候,按照负载均衡的机制(随机、轮询之类)来转发到对应的服务器上,然后在该服务器上先是到 Controller,然后 Service,再到 DAO,然后返回给 Service,返回给 Controller,最后返回给前台。似乎这样也行。

        但是当我们网站访问量不断飙升的时候,似乎有点撑不住了,只能加服务器,成本也明显增加。我们能不能再分一分呢 ?

        同时,我们发现真正比较耗费时间的是一些 IO 操作,比如数据库操作、上传文件之类的。为了方便开发,我们可以把 DAO 和 Service 层放在一起,暂且称为业务层。业务层耗费的远远大于 Controller 层的。我们可以在 5 台服务器中拿 1 台部署 Controller 层,另外 4 台部署业务层代码。

        如果将他们连接起来呢?

        Eureka + Feign!

        Eureka 做注册中心,业务层和控制器层每一个实例可以作为一个服务,比如 UserService 是一个服务,UserController 也可以是一个服务,一个服务可以有多个状态(可以有多台机器注册到该服务)。

        下面通过一个例子来讲解

         

        源码地址:https://github.com/saysky/spring-cloud-demo

         

        一、下载源码

        请上 Github 上下载本例子源码

        源码地址:https://github.com/saysky/spring-cloud-demo

         

        二、项目结构

        注意这是三个项目(不是一个项目),用 IDEA 打开分别打开三个项目,最终如图

        \"SpringCloud

         

        1、eureka-server(一个 SpringBoot 项目)

        \"SpringCloud

         

         

        2、cloud (一个多模块项目,API + 业务层)   \"SpringCloud

         

         

        (1)其中 cloud-api 是 api,里面有 model 和 service接口,cloud-core 和 cloud-web 将依赖它

        到时候需要首先将该模块 install,即打包到本地仓库中

        \"SpringCloud

         

        (2)其中 cloud-core,里面 dao 和 serviceImpl,作为生产者的存在

        启动类在这里面

        \"SpringCloud

         

        3、cloud-web( 一个 SpringBoot 项目)

        只有控制器层,需要依赖 cloud-api ,作为消费者的存在(可以是其他人调用)

        \"SpringCloud

         

        三、如何启动

        1、新建数据库 summer,导入 user.sql

        2、导入新项目,先后顺序随便(如eureka-server、cloud、cloud-web)

        3、启动 eureka-server 项目,端口为 8761

        4、对 cloud 项目中的 cloud-api 模块进行install,maven命令是 mvn clean install

        5、启动 cloud-core 项目,端口为 8090

        6、(可选)其实这时候我们可以在地址栏访问一下 localhost:8090/user 其实是可以访问的,如果你不行,说明有问题

        7、启动 cloud-web 项目,端口为 8080

        8、在地址栏访问 localhost:8080/user,如果可以看到数据,说明成功

         

        四、补充

        1、我这里模拟了四台注册了业务层服务(cloud-producer) 和 一台控制器层服务(cloud-consumer)

        \"SpringCloud

        ps:因为我是用本机测试,所以开了不同的端口表示不同的服务器

         

        2、访问控制器层,然后 Feign 会帮我们在注册中心中找到对应的服务名和对应的方法

        我们访问 localhost:8080/user/1 首先到的是控制器层,控制器层调用 userService,userService 是 Cloud-API 里的,添加了 @FeignClient(name=\"cloud-producer\") 注解,然后找到在注册中心中寻找 cloud-producer 的服务,从一台(负载均衡)服务器中找到 userService 的实现类,即 userServiceImpl,也就是进入了 cloud-core,然后完成一系列 DAO 和 Service 操作,最后返回给 Controller。

        \"SpringCloud

        其中 Service 和 Service 的实现都需要加 RequestMapping,Service 实现需要加 @RestController,否则会出现 404

        \"SpringCloud

        所以,其实我们可以直接访问 cloud-core 这一层,在地址栏访问 localhost:8090/user/1

        \"SpringCloud

         

        事实上,我们访问 控制器的时候,当需要某个 Service 的时候,Feign 帮我们向 Service 又发了一个同样的请求。我们可以直接访问这个 Service 请求

        但是,通常我们会在生产环境中,不让别人直接访问到 Service,可以通过一些机制实现,比如内网啦。

         

        六、学习过程中遇到的问题

        1、cloud-web 中注入 userService,一直注入失败,无法扫描到 userServiceImpl

        解决方案:在 cloud-web 启动类上添加

        @EnableFeignClients(basePackages = \"com.liuyanzhao.cloud\")

        \"SpringCloud

         

        2、出现 404 错误

        cloud-core 里的 userServiceImpl 也需要加 @RequestMapping,并且需要将 @Service 改成 @RestController

         

        UserService(cloud-api)

        \"SpringCloud

         

        UserServiceImpl(cloud-core)

        \"SpringCloud

         

        ', 2, 0, 0, 1, 1, 1, '2019-04-25 21:43:11', '2018-11-25 20:58:22', '在没有接触微服务或者分布式项目之前,我们的项目时没有分层的,我们的服务也是没有分层的。比如,我们有5台服务器使用 Nginx 做负载均衡,上面都是安装了我们的 Tomcat 服务器,部署的一样的项目。当用户访问某个请求的时候,按照负载均衡的机制(随机、轮询之类)来转发到对应的服务器上,然后在该服务器', '/img/thumbnail/random/img_8.jpg'); +INSERT INTO `article` VALUES (29, 1, 'IDEA启动EDAS项目', '

        一、下载EDAS配置中心

        这个有点像我们SpringCloud里的Eurake注册中心

        下载地址:点此

        官网地址:点此

        启动 edas配置中心:windows下点击 edas-lite-configcenter/bin 中的 startup.bat

        \"IDEA启动EDAS项目\"

        默认启动在 8080 端口

         

        二、安装 Ali-Tomcat 和 Pandora

        1、下载 Ali-Tomcat,保存后解压至相应的目录(如:d:\\work\\tomcat\\)。

         

        2、下载 Pandora 容器,保存后将内容解压至上述保存的 Ali-Tomcat 的 deploy 目录(d:\\work\\tomcat\\deploy)下。

        下载后是两个文件夹

        \"IDEA启动EDAS项目\"

         

         

        解压后,将 taobao-haf 放到 taobao-tomcat-7.0.59/deploy 里面

         

        比如我们有三个服务,分别是A、B、C

        我们建立三个文件夹,分别是A、B 和 C,每个文件夹里都拷贝一份上面的 taobao-tomcat-7.0.59

         

        三、IDEA 配置 Tomcat

        1、创建三个Tomcat

        \"IDEA启动EDAS项目\"

        指定启动的端口,VM options里填写对应的 Tomcat 的启动参数

        D:\\EDAS\\A\\taobao-tomcat-7.0.59\\deploy\\taobao-hsf.sar

         

        2、在Deployment里选择对应的项目

        \"IDEA启动EDAS项目\"

         

        然后依次启动几个项目就行了

         

        官方文档:https://help.aliyun.com/product/29500.html?spm=a2c4g.11186623.6.540.102bb8e7ETwPNH

         

        ', 2, 0, 0, 1, 1, 1, '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', '/img/thumbnail/random/img_9.jpg'); +INSERT INTO `article` VALUES (30, 1, 'SpringBoot + mongodb 整合, 记录网站操作日志,常用查询操作', '

        mongodb 是一种文档型数据库。跟 Redis 一样是非关系型数据库,Redis 属于那种小而快的数据库,常常用作缓存。

        而如果我们需要存一些类似于日志的那种,可以尝试用 mongodb (当然也有人用 MySQL,就是有点慢)。我们尝试用 mongodb 来存储博客的日志信息。

        本文主要介绍 SpringBoot 和 mongodb 整合,和基本的查询操作。

         

        一、依赖和配置

        创建 springboot 项目,引入 web 和 lombok,然后再添加 mongodb 依赖

        1、pom.xml

        1. <dependency>
        2.     <groupId>org.springframework.bootgroupId>
        3.     <artifactId>spring-boot-starter-data-mongodbartifactId>
        4. dependency>

        类似 Spring Data JPA

         

        2、application.properties

        1. spring.data.mongodb.uri=mongodb://localhost:27017/saysky

        saysky是数据库名称,确保你本地启动了 mongodb

         

        二、代码实例

        1、日志实体

        1. package com.liuyanzhao.mongodb.model;
        2. import lombok.Data;
        3. import java.util.Date;
        4. /**
        5.  * 日志
        6.  * @author 言曌
        7.  * @date 2018/9/3 20:00
        8.  */
        9. @Data
        10. public class Log {
        11.     private Long id;
        12.     private Long userId;
        13.     private Integer type;
        14.     private String url;
        15.     private String desc;
        16.     private Date createTime;
        17. }

         

        2、LogRepository

        1. package com.liuyanzhao.mongodb.dao;
        2. import com.liuyanzhao.mongodb.model.Log;
        3. import org.springframework.data.domain.Page;
        4. import org.springframework.data.domain.Pageable;
        5. import org.springframework.data.mongodb.repository.MongoRepository;
        6. import org.springframework.stereotype.Repository;
        7. import java.util.Date;
        8. import java.util.List;
        9. /**
        10.  * @author 言曌
        11.  * @date 2018/9/3 14:17
        12.  */
        13. @Repository
        14. public interface LogRepository extends   {
        15.     /**
        16.      * 根据用户ID查询
        17.      * @param userId
        18.      * @return
        19.      */
        20.     List findByUserId(Long userId);
        21.     /**
        22.      * 根据描述查询
        23.      * @param desc
        24.      * @return
        25.      */
        26.     List findByDesc(String desc);
        27.     /**
        28.      * 根据创建日期范围查询
        29.      * @param startTime
        30.      * @param endTime
        31.      * @return
        32.      */
        33.     List findByCreateTimeBetween(Date startTime, Date endTime);
        34.     /**
        35.      * 根据描述查询
        36.      * 分页查询
        37.      * @param desc
        38.      * @return
        39.      */
        40.     Page findByDesc(String desc, Pageable pageable);
        41.     /**
        42.      * 根据创建日期范围查询
        43.      * 分页查询
        44.      * @param startTime
        45.      * @param endTime
        46.      * @return
        47.      */
        48.     Page findByCreateTimeBetween(Date startTime, Date endTime,Pageable pageable);
        49. }

         

        3、测试类

        1. package com.liuyanzhao.mongodb.dao;
        2. import com.liuyanzhao.mongodb.model.Log;
        3. import org.junit.Test;
        4. import org.junit.runner.RunWith;
        5. import org.springframework.beans.factory.annotation.Autowired;
        6. import org.springframework.boot.test.context.SpringBootTest;
        7. import org.springframework.data.domain.Page;
        8. import org.springframework.data.domain.PageRequest;
        9. import org.springframework.test.context.junit4.SpringRunner;
        10. import java.util.Date;
        11. /**
        12.  * @author 言曌
        13.  * @date 2018/9/3 14:21
        14.  */
        15. @SpringBootTest
        16. @RunWith(SpringRunner.class)
        17. public class LogRepositoryTest {
        18.     @Autowired
        19.     private LogRepository logRepository;
        20.     @Test
        21.     public void save() {
        22.         Log log = new Log();
        23.         log.setId(7L);
        24.         log.setType(1);
        25.         log.setDesc(\"更新用户\");
        26.         log.setUserId(10002L);
        27.         log.setUrl(\"/user/update\");
        28.         log.setCreateTime(new Date());
        29.         logRepository.save(log);
        30.     }
        31.     @Test
        32.     public void findById() {
        33.         Log Log = logRepository.findById(1L).get();
        34.         System.out.println(Log);
        35.     }
        36.     @Test
        37.     public void findByDes() {
        38.         //不分页
        39. //        List LogList = logRepository.findByDesc(\"添加用户\");
        40. //        System.out.println(LogList);
        41.         //分页查询
        42.         //查询第1页,每页显示2条
        43.         PageRequest pageRequest = new PageRequest(0,2);
        44.         Page logPage = logRepository.findByDesc(\"添加用户\",pageRequest);
        45.         System.out.println(logPage);
        46.     }
        47.     @Test
        48.     public void findByCreateTimeBetween() {
        49.         //根据时间区间查询
        50.         //不分页
        51.         Date createdAtStart = new Date(1535974057016L);
        52.         Date createdAtEnd = new Date(1535974145009L);
        53. //        List LogList = logRepository.findByCreateTimeBetween(createdAtStart,createdAtEnd);
        54. //        System.out.println(LogList);
        55.         //分页查询
        56.         //查询第1页,每页显示2条
        57.         PageRequest pageRequest = new PageRequest(0,2);
        58.         Page logPage = logRepository.findByCreateTimeBetween(createdAtStart,createdAtEnd,pageRequest);
        59.         System.out.println(logPage);
        60.     }
        61. }

         

        我们通过mongodb可视化工具可以看到数据

        \"SpringBoot

         

        三、更多查询

        因为引入的 MongoRepository 是一种 JPA 框架,所以增删改查都很容易。

        可以参考 Spring Data JPA 方法命名规范

          ', 2, 0, 0, 1, 1, 1, '2019-04-25 21:43:06', '2018-11-25 21:01:24', 'mongodb 是一种文档型数据库。跟 Redis 一样是非关系型数据库,Redis 属于那种小而快的数据库,常常用作缓存。而如果我们需要存一些类似于日志的那种,可以尝试用 mongodb (当然也有人用 MySQL,就是有点慢)。我们尝试用 mongodb 来存储博客的日志信息。本文主要介绍&nb', '/img/thumbnail/random/img_0.jpg'); +INSERT INTO `article` VALUES (31, 1, 'RocketMQ 实战之快速入门', '

          最近 RocketMQ 刚刚上生产环境,闲暇之时在这里做一些分享,主要目的是让初学者能快速上手RocketMQ。

          \r\n

          RocketMQ 是什么

          \r\n

          Github 上关于 RocketMQ 的介绍:
          \r\nRcoketMQ 是一款低延迟、高可靠、可伸缩、易于使用的消息中间件。具有以下特性:

          \r\n
            \r\n
          1. 支持发布/订阅(Pub/Sub)和点对点(P2P)消息模型
          2. \r\n
          3. 在一个队列中可靠的先进先出(FIFO)和严格的顺序传递
          4. \r\n
          5. 支持拉(pull)和推(push)两种消息模式
          6. \r\n
          7. 单一队列百万消息的堆积能力
          8. \r\n
          9. 支持多种消息协议,如 JMS、MQTT 等
          10. \r\n
          11. 分布式高可用的部署架构,满足至少一次消息传递语义
          12. \r\n
          13. 提供 docker 镜像用于隔离测试和云集群部署
          14. \r\n
          15. 提供配置、指标和监控等功能丰富的 Dashboard
          16. \r\n
          \r\n

          对于这些特性描述,大家简单过一眼就即可,深入学习之后自然就明白了。

          \r\n

          专业术语

          \r\n

          Producer

          \r\n

          消息生产者,生产者的作用就是将消息发送到 MQ,生产者本身既可以产生消息,如读取文本信息等。也可以对外提供接口,由外部应用来调用接口,再由生产者将收到的消息发送到 MQ。

          \r\n

          Producer Group

          \r\n

          生产者组,简单来说就是多个发送同一类消息的生产者称之为一个生产者组。在这里可以不用关心,只要知道有这么一个概念即可。

          \r\n

          Consumer

          \r\n

          消息消费者,简单来说,消费 MQ 上的消息的应用程序就是消费者,至于消息是否进行逻辑处理,还是直接存储到数据库等取决于业务需要。

          \r\n

          Consumer Group

          \r\n

          消费者组,和生产者类似,消费同一类消息的多个 consumer 实例组成一个消费者组。

          \r\n

          Topic

          \r\n

          Topic 是一种消息的逻辑分类,比如说你有订单类的消息,也有库存类的消息,那么就需要进行分类,一个是订单 Topic 存放订单相关的消息,一个是库存 Topic 存储库存相关的消息。

          \r\n

          Message

          \r\n

          Message 是消息的载体。一个 Message 必须指定 topic,相当于寄信的地址。Message 还有一个可选的 tag 设置,以便消费端可以基于 tag 进行过滤消息。也可以添加额外的键值对,例如你需要一个业务 key 来查找 broker 上的消息,方便在开发过程中诊断问题。

          \r\n

          Tag

          \r\n

          标签可以被认为是对 Topic 进一步细化。一般在相同业务模块中通过引入标签来标记不同用途的消息。

          \r\n

          Broker

          \r\n

          Broker 是 RocketMQ 系统的主要角色,其实就是前面一直说的 MQ。Broker 接收来自生产者的消息,储存以及为消费者拉取消息的请求做好准备。

          \r\n

          Name Server

          \r\n

          Name Server 为 producer 和 consumer 提供路由信息。

          \r\n

          RocketMQ 架构

          \r\n
          \r\n
          \r\n
          \r\n
          \r\n
          \r\n
          RocketMQ 架构
          \r\n
          \r\n

          由这张图可以看到有四个集群,分别是 NameServer 集群、Broker 集群、Producer 集群和 Consumer 集群:

          \r\n
            \r\n
          1. NameServer: 提供轻量级的服务发现和路由。 每个 NameServer 记录完整的路由信息,提供等效的读写服务,并支持快速存储扩展。
          2. \r\n
          3. Broker: 通过提供轻量级的 Topic 和 Queue 机制来处理消息存储,同时支持推(push)和拉(pull)模式以及主从结构的容错机制。
          4. \r\n
          5. Producer:生产者,产生消息的实例,拥有相同 Producer Group 的 Producer 组成一个集群。
          6. \r\n
          7. Consumer:消费者,接收消息进行消费的实例,拥有相同 Consumer Group 的
            \r\nConsumer 组成一个集群。
          8. \r\n
          \r\n

          简单说明一下图中箭头含义,从 Broker 开始,Broker Master1 和 Broker Slave1 是主从结构,它们之间会进行数据同步,即 Date Sync。同时每个 Broker 与
          \r\nNameServer 集群中的所有节
          \r\n点建立长连接,定时注册 Topic 信息到所有 NameServer 中。

          \r\n

          Producer 与 NameServer 集群中的其中一个节点(随机选择)建立长连接,定期从 NameServer 获取 Topic 路由信息,并向提供 Topic 服务的 Broker Master 建立长连接,且定时向 Broker 发送心跳。Producer 只能将消息发送到 Broker master,但是 Consumer 则不一样,它同时和提供 Topic 服务的 Master 和 Slave
          \r\n建立长连接,既可以从 Broker Master 订阅消息,也可以从 Broker Slave 订阅消息。

          \r\n

          RocketMQ 集群部署模式

          \r\n
            \r\n
          1. 单 master 模式
            \r\n也就是只有一个 master 节点,称不上是集群,一旦这个 master 节点宕机,那么整个服务就不可用,适合个人学习使用。
          2. \r\n
          3. 多 master 模式
            \r\n多个 master 节点组成集群,单个 master 节点宕机或者重启对应用没有影响。
            \r\n优点:所有模式中性能最高
            \r\n缺点:单个 master 节点宕机期间,未被消费的消息在节点恢复之前不可用,消息的实时性就受到影响。
            \r\n注意:使用同步刷盘可以保证消息不丢失,同时 Topic 相对应的 queue 应该分布在集群中各个节点,而不是只在某各节点上,否则,该节点宕机会对订阅该 topic 的应用造成影响。
          4. \r\n
          5. 多 master 多 slave 异步复制模式
            \r\n在多 master 模式的基础上,每个 master 节点都有至少一个对应的 slave。master
            \r\n节点可读可写,但是 slave 只能读不能写,类似于 mysql 的主备模式。
            \r\n优点: 在 master 宕机时,消费者可以从 slave 读取消息,消息的实时性不会受影响,性能几乎和多 master 一样。
            \r\n缺点:使用异步复制的同步方式有可能会有消息丢失的问题。
          6. \r\n
          7. 多 master 多 slave 同步双写模式
            \r\n同多 master 多 slave 异步复制模式类似,区别在于 master 和 slave 之间的数据同步方式。
            \r\n优点:同步双写的同步模式能保证数据不丢失。
            \r\n缺点:发送单个消息 RT 会略长,性能相比异步复制低10%左右。
            \r\n刷盘策略:同步刷盘和异步刷盘(指的是节点自身数据是同步还是异步存储)
            \r\n同步方式:同步双写和异步复制(指的一组 master 和 slave 之间数据的同步)
            \r\n注意:要保证数据可靠,需采用同步刷盘和同步双写的方式,但性能会较其他方式低。
          8. \r\n
          \r\n

          RocketMQ 单主部署

          \r\n

          鉴于是快速入门,我选择的是第一种单 master 的部署模式。先说明一下我的安装环境:

          \r\n
            \r\n
          1. Centos 7.2
          2. \r\n
          3. jdk 1.8
          4. \r\n
          5. Maven 3.2.x
          6. \r\n
          7. Git
          8. \r\n
          \r\n

          这里 git 可用可不用,主要是用来直接下载 github 上的源码。也可以选择自己到
          \r\ngithub 上下载,然后上传到服务器上。以git操作为示例。

          \r\n
            \r\n
          1. clone 源码并用 maven 编译
          2. \r\n
          \r\n
          > git clone https://github.com/alibaba/RocketMQ.git /opt/RocketMQ\r\n> cd /opt/RocketMQ && mvn -Dmaven.test.skip=true clean package install assembly:assembly -U\r\n> cd target/alibaba-rocketmq-broker/alibaba-rocketmq\r\n
          \r\n

          此处可能遇到的问题
          \r\n一、执行\"git clone https://github.com/alibaba/RocketMQ.git /home/inspkgs/RocketMQ\"时出现以下提示:

          \r\n
          fatal: unable to access \'https://github.com/alibaba/RocketMQ.git/\': Could not resolve host: github.com; Unknown error\r\n
          \r\n

          解决办法:一般是由于网络原因造成的,执行以下命令

          \r\n
          > ping github.com\r\n
          \r\n

          确定可以 ping 通之后,再重新执行 git clone 命令。
          \r\n二、执行\"mvn -Dmaven.test.skip=true clean package install assembly:assembly -U\"编译时,可能出现下载相关jar很慢的情况。
          \r\n这也是由于默认 maven 中央仓库在国外的原因,可以根据需要在 /home/maven/conf/setting.xml 中的 添加以下内容后重新编译:

          \r\n
          <mirror>\r\n    <id>aliyunid>\r\n    <mirrorOf>centralmirrorOf>\r\n    <name>aliyun mavenname>\r\n    <url>http://maven.aliyun.com/nexus/content/groups/public/url>\r\nmirror>\r\n
          \r\n
            \r\n
          1. 启动 Name Server
          2. \r\n
          \r\n
          > nohup sh /opt/RocketMQ/bin/mqnamesrv &\r\n//执行 jps 查看进程\r\n> jps\r\n25913 NamesrvStartup\r\n//查看日志确保服务已正常启动\r\n> tail -f ~/logs/rocketmqlogs/namesrv.log\r\nThe Name Server boot success...\r\n
          \r\n
            \r\n
          1. 启动 broker
          2. \r\n
          \r\n
          > nohup sh /opt/RocketMQ/bin/mqbroker -n localhost:9876 &\r\n//执行 jps 查看进程\r\n> jps\r\n25954 BrokerStartup\r\n//查看日志确保服务已正常启动\r\n> tail -f ~/logs/rocketmqlogs/broker.log \r\nThe broker[broker-a, 10.1.54.121:10911] boot success...\r\n
          \r\n
            \r\n
          1. 发送和接收消息
            \r\n发送/接收消息之前,我们需要告诉客户端 NameServer 地址。RocketMQ 提供了多种方式来实现这一目标。为简单起见,我们使用环境变量 NAMESRV_ADDR。
          2. \r\n
          \r\n
          > export 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
          \r\n
            \r\n
          1. 关闭服务
          2. \r\n
          \r\n
          > sh /opt/RocketMQ/bin/mqshutdown broker\r\nThe mqbroker(36695) is running...\r\nSend shutdown request to mqbroker(36695) OK\r\n> sh /opt/RocketMQ/bin/mqshutdown namesrv\r\nThe mqnamesrv(36664) is running...\r\nSend shutdown request to mqnamesrv(36664) OK\r\n
          \r\n

          生产者、消费者 Demo

          \r\n
            \r\n
          1. 生产者
          2. \r\n
          \r\n
          public class Producer {\r\n    public static void main(String[] args) throws MQClientException, InterruptedException {\r\n\r\n        //声明并初始化一个producer\r\n        //需要一个producer group名字作为构造方法的参数,这里为producer1\r\n        DefaultMQProducer producer = new DefaultMQProducer(\"producer1\");\r\n        \r\n        //设置NameServer地址,此处应改为实际NameServer地址,多个地址之间用;分隔\r\n        //NameServer的地址必须有,但是也可以通过环境变量的方式设置,不一定非得写死在代码里\r\n        producer.setNamesrvAddr(\"10.1.54.121:9876;10.1.54.122:9876\");\r\n        \r\n        //调用start()方法启动一个producer实例\r\n        producer.start();\r\n\r\n        //发送10条消息到Topic为TopicTest,tag为TagA,消息内容为“Hello RocketMQ”拼接上i的值\r\n        for (int i = 0; i < 10; i++) {\r\n            try {\r\n                Message msg = new Message(\"TopicTest\",// topic\r\n                        \"TagA\",// tag\r\n                        (\"Hello RocketMQ \" + i).getBytes(RemotingHelper.DEFAULT_CHARSET)// body\r\n                );\r\n                \r\n                //调用producer的send()方法发送消息\r\n                //这里调用的是同步的方式,所以会有返回结果\r\n                SendResult sendResult = producer.send(msg);\r\n                \r\n                //打印返回结果,可以看到消息发送的状态以及一些相关信息\r\n                System.out.println(sendResult);\r\n            } catch (Exception e) {\r\n                e.printStackTrace();\r\n                Thread.sleep(1000);\r\n            }\r\n        }\r\n\r\n        //发送完消息之后,调用shutdown()方法关闭producer\r\n        producer.shutdown();\r\n    }\r\n}\r\n
          \r\n
            \r\n
          1. 消费者
          2. \r\n
          \r\n
          public class Consumer {\r\n\r\n    public static void main(String[] args) throws InterruptedException, MQClientException {\r\n    \r\n        //声明并初始化一个consumer\r\n        //需要一个consumer group名字作为构造方法的参数,这里为consumer1\r\n        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(\"consumer1\");\r\n\r\n        //同样也要设置NameServer地址\r\n        consumer.setNamesrvAddr(\"10.1.54.121:9876;10.1.54.122:9876\");\r\n\r\n        //这里设置的是一个consumer的消费策略\r\n        //CONSUME_FROM_LAST_OFFSET 默认策略,从该队列最尾开始消费,即跳过历史消息\r\n        //CONSUME_FROM_FIRST_OFFSET 从队列最开始开始消费,即历史消息(还储存在broker的)全部消费一遍\r\n        //CONSUME_FROM_TIMESTAMP 从某个时间点开始消费,和setConsumeTimestamp()配合使用,默认是半个小时以前\r\n        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);\r\n\r\n        //设置consumer所订阅的Topic和Tag,*代表全部的Tag\r\n        consumer.subscribe(\"TopicTest\", \"*\");\r\n\r\n        //设置一个Listener,主要进行消息的逻辑处理\r\n        consumer.registerMessageListener(new MessageListenerConcurrently() {\r\n\r\n            @Override\r\n            public ConsumeConcurrentlyStatus consumeMessage(List msgs,\r\n                                                            ConsumeConcurrentlyContext context) {\r\n\r\n                System.out.println(Thread.currentThread().getName() + \" Receive New Messages: \" + msgs);\r\n                \r\n                //返回消费状态\r\n                //CONSUME_SUCCESS 消费成功\r\n                //RECONSUME_LATER 消费失败,需要稍后重新消费\r\n                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;\r\n            }\r\n        });\r\n\r\n        //调用start()方法启动consumer\r\n        consumer.start();\r\n\r\n        System.out.println(\"Consumer Started.\");\r\n    }\r\n}\r\n
          \r\n



          ', 5, 0, 0, 1, 1, 1, '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支持发布/订', '/img/thumbnail/random/img_1.jpg'); +INSERT INTO `article` VALUES (32, 2, 'Docker_入门?只要这篇就够了!(纯干货适合0基础小白)', '

          原文地址:https://blog.csdn.net/S_gy_Zetrov/article/details/78161154

          写在前面

          • 这篇博客适合谁?
            • 对于Docker并不了解,只是有一点模糊的感觉,觉得Docker可以当成虚拟机用之类的
            • 只是下载了Docker软件,对于怎么配置,怎么玩,第一步干什么,完全一无所知
            • 本文适用于mac,PC用户不保证文章的效果,现在离开来得及
          • 网上Docker相关的教程都泛滥了,为啥还要写,为啥我要看你的文章
            • 首先欢迎你,能搜索到我的博客就是缘分
            • 其次,确实,现在Docker相关的文章真的太多了,那我为什么还要写呢?原因有三:
              • 其一,文章是很多,各种1小时入门什么的,相信你已经在知乎看过很多了,我也看过,
                因为我就是从知乎那几篇教程为起点,自己抠出来的,现在把自己的心得和经验贴出来,我觉得既是对自己负责也能服务他人。
              • 其二,我从对Docker只有及其模糊(真的是模糊,我除了知道Docker是跟虚拟系统有关其他啥都不知道),到现在起码一
                问一不知(对应一问三不知)的状态,大概花了8个小时。这期间全靠我从网络上搜索到的资料。现在用我的话写出来,我觉得对于跟我相同开始情况的小白用户,能省不少时间,所以我要写。
              • 其三,因为我自己是纯靠搜索到的教程和技术文章学习的Docker,我自然是懂得干涩的语言对学习的拖慢。所以我自己写的时候,自然会考虑到这个问题。本文尽量不用技术用语,尽量使用通俗易懂的文字,为进一步拉低Docker的入门门槛作出我自己的贡献。
          • 这篇文章主要是哪方面的Docker文章,纯入门?你做了哪些工作?
            • 很高兴你问了我这个问题。本文主要:
              • 首先对于Docker的概念作出我的解释,尽量通俗易懂
              • 接着针对我用Docker的目的,从最开始的软件下载,到最后push一个自己的镜像到hub,整个过程以白话的形式描述出来,降低入门门槛,节省你的学习时间
            • 我做了哪些工作?这是个好问题!
              • 首先,我一直都很好奇Docker这个东西,但从来没有机会去使用它。但是,这学期OS课的lab,纯Linux系统编程,虽然使用双系统或者虚拟机都是纯小学生的难易程度了,也不麻烦,但我想,为什么不用Docker呢?用双系统或者虚拟机也太过小儿科!所以,我就这样接触了Docker
              • 那么我使用Docker实现了什么?在lab中,我们需要用到的是纯终端下的gcc工具链还有vim等,那么这就是我需要的全部软件了!于是我在Docker中实现了「gcc+gdb
                +vim」环境,并push到了hub中。
          • 你这篇博客有没有抄别人的?
            • 额,我学习Docker时确实看了很多很多(真的是很多)文章,现在写这篇博客,除了自己的经验心得,自然还得去看那些文章。但我能保证的是,我写的都是取精华去糟粕,不然这篇文章就不必存在了你说是不是?
            • 参考资料会在文后统一附上,谢谢!

          好了,闲话说完,我们开始吧!

           

          入门Docker,你要下载什么?注册什么?

          + Docker.app你肯定是要下载的!

          Docker for mac,这个你肯定要下载:点我下载Docker for mac的社区免费版本

           

          + 注册Docker官方账号,你需要它!

          注册一个Docker的官方账号,有利而无害,相信我!点我注册Docker Cloud官方账号

          Docker安装好,账号也注册后,点击桌面顶栏的Docker图标,点击sign in,登陆你的Docker账号。

           

          + 注册DaoCloud账号,获取加速服务!

          不可否认,有时直接从Docker官方往本地pull镜像会十分缓慢。。。这时我们可以通过国内的Docker服务提供商免费获取加速pull镜像服务,阿里网易好像都有这种服务,我选择的是DaoCloud:点我注册DaoCloud账号

          注册后登陆DaoCloud,找到这个按钮:

          \"docker1.png\"

          接着,按照它的步骤,为自己添加Docker加速服务

          \"docker2\"

          + 到此准备工作结束,进入下一部分!

           

          要想入门Docker,首先你需要理解Docker!

          我也不扯淡了,直接上我对Docker的理解:(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)

          Docker,可以说是一个终端命令行的虚拟机,但更准确的说法,其实应该是一个虚拟环境。比如,你想要在PC上无缝使用Linux么?那么虚拟机并不是你唯一的出路,你还有Docker!我更愿意称Docker为一个容器,当然这只是Docker的一个狭义解释,Docker不止是一个容器。Docker包含3个重要概念:

          • 一个,是镜像(Image),镜像是静态的、可以被用户互相分享的文件。我们玩过双系统和虚拟机的人都知道,首先你需要一个.iso镜像,才能安装系统。Docker中的镜像也是这个东西,镜像是静态的,你不能对他操作,只能pull别人的镜像或者push自己的镜像
          • 还有一个,是容器(Container),前面说过,镜像是静态不可操作的,只能被分享和下载,那什么是能被操作的呢?就是容器里!容器可以理解为镜像的动态状态,也就是我们虚拟机中系统装好后的状态,其实这么说是不对的,容器最符合的描述应该是Linux的iso文件的Live CD模式,比如我们玩双系统时都进入过Live CD模式,不安装系统而直接进入系统,很神奇是吧,Docker的容器就是这个概念,只不过更加轻量更加迅速便捷。但是Live CD的害处就是你关机后作出的修改安装的软件全部gg,容器也是一样,一旦被直接推出,之前安装的gcc啊vim啊啥的就会全部gg掉。如果要保存修改,就需要将当前容器封装成一个新的镜像,这样下次启动这个新的镜像后之前作出的修改还都在
          • 最后,是仓库(Repository)。各位在前面看到我写的pull和push什么的,有没有晕?不知道各位对于git熟悉不熟悉,Docker中的仓库很像git的代码仓库,你可以pull自己之前push到自己仓库的镜像到本地,也可以pull别人push到公共仓库的镜像到自己本地。说白了就是百度云盘,你可以上传(push)自己做好环境的Docker上去,也可以下载(pull)自己云端的镜像到本地。同时,我们知道百度云最大的特点就是分享(你懂的嘿嘿嘿),类比Docker,如果你得到百度云分享链接(别人的镜像名字、标签和别人的用户名),你还可以下载(pull)别人分享的镜像到自己的本地,别人也可以下载(pull)你的镜像,因为Docker仓库都是公共的。当然,每个免费用户有一个名额把自己的一个镜像设为私有,也就是禁止被分享给别人,类比百度云上你自己保存的而没有被生成分享链接的小姐姐。

          接下来来张高大上的概念图,各位看个热闹2333

          \"docker3.png\"

          接下来就是实战了!打开你的iTerm2!(我是iTerm2党2333,打开普通terminal也行,这个不影响)

           

          + 查看Docker版本信息

          终端输入:\r\ndocker version

           

          显示的我的版本信息

           ~$ docker version\r\nClient:\r\n Version:      17.06.2-ce\r\n API version:  1.30\r\n Go version:   go1.8.3\r\n Git commit:   cec0b72\r\n Built:        Tue Sep  5 20:12:06 2017\r\n OS/Arch:      darwin/amd64\r\n ...\r\n //omitted by sgy(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)\r\n ...

          不过我一般不需要那么多信息,所以一直用的是docker -v命令

          + 是时候pull你的第一个镜像下来的!

          Docker安装好后是不会自带镜像的,你需要从仓库自己pull一个镜像下来,自己制作自己的镜像也是一个道理,你可以通过在已有的镜像基础上生成自己的镜像或者看一下这篇博客: 随便百度的如何创建自己Docker镜像得到的教程

          Docker镜像官方好像只提供Linux,这个很容易想,windows和mac是要交钱的吧大概?(否)

          搜索ubuntu的Docker镜像docker search ubuntu,这个如果你想要什么centos,直接改就行,不区分写法,我的返回结果:

          ~$ docker search ubuntu\r\nNAME                                                   DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED\r\nubuntu                                                 Ubuntu is a Debian-based Linux operating s...   6636                [OK]\r\ndorowu/ubuntu-desktop-lxde-vnc                         Ubuntu with openssh-server and NoVNC            131                                     [OK]\r\nrastasheep/ubuntu-sshd                                 Dockerized SSH service, built on top of of...   105                                     [OK]\r\nansible/ubuntu14.04-ansible                            Ubuntu 14.04 LTS with ansible                   86                                      [OK]\r\nubuntu-upstart                                         Upstart is an event-based replacement for ...   80                  [OK]\r\nneurodebian                                            NeuroDebian provides neuroscience research...   40                  [OK]\r\nubuntu-debootstrap                                     debootstrap --variant=minbase --components...   31                  [OK]\r\n...\r\n//omitted by sgy(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)\r\n...

           

          拉取官方最新版的ubuntu镜像:docker pull ubuntu:latest,其中的latest是一个标签(tag),表示是当前最新版本。你应该得到的信息,类似这样的

          ~$ docker pull ubuntu:latest\r\nTrying to pull repository docker.io/library/ubuntu ... \r\nlatest: Pulling from docker.io/library/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 for docker.io/ubuntu:latest

          你输入的命令实际上相当于docker pull registry.hub.docker.com/ubuntu:latest命令,即从注册服务器registry.hub.docker.com中的名为ubuntu的仓库中下载标签为latest的镜像

           

          由于我的lab统一要求,ubuntu需要14.04版本,所以我在hub里面搜了搜,找到了一个用户分享的14.04 64位纯净镜像(base image),下面将他的镜像扒下来

          ~$ docker pull chug/ubuntu14.04x64

          这个用户还有很多其他版本的ubuntu系统,12 13 14的32位64位都有,全是纯净镜像。

          + 查看你本地的镜像仓库!

          把初始镜像拉下来后,就可以启动它了,不过,可以先使用docker images命令查看你自己的本地镜像,我随便找了个例子,你的也应该是类似这样的:

          ~$ docker images\r\nREPOSITORY          TAG                 IMAGE ID            CREATED             SIZE\r\ndocker.io/ubuntu    16.04               e4415b714b62        11 days ago         128.1 MB\r\ndocker.io/ubuntu    latest              e4415b714b62        11 days ago         128.1 MB\r\ndocker.io/ubuntu    12.04               aefa163f7a7e        11 days ago         103.5 MB\r\ndocker.io/centos    latest              0584b3d2cf6d        3 weeks ago         196.5 MB

           

          从网上一个教程中找到以下说法,比我说的好,那就看这个吧!

          在列出信息中,可以看到几个字段信息:

          • 来自于哪个仓库,比如 ubuntu
          • 镜像的标记,比如 16.04
          • 它的 ID 号(唯一),比如e4415b714b62
          • 创建时间
          • 镜像大小

          其中镜像的 ID 唯一标识了镜像,注意到 ubuntu:16.04 和 ubuntu:latest具有相同的镜像 ID ,说明它们实际上是同一镜像。 TAG 信息用来标记来自同一个仓库的不同镜像。例如 ubuntu 仓库中有多个镜像,通过 TAG 信息来区分发行版本,例如10.04 、 12.04 、 12.10 、 13.04 、 14.04 等。例如可以使用docker run -t -i ubuntu:16.04 /bin/bash命令指定使用镜像ubuntu:16.04来启动一个容器。如果不指定具体的标记,则默认使用latest标记信息。

          + 启动你的镜像并尽情安装软件吧!

          以下内容均以我自己pull下来的chug的初始镜像为例:

          现在你已经有一个初始的镜像了,注意这个里面是什么都没有的,连vim都没装,是精简到不能再精简的镜像了。

          首先启动它:

          $ docker run -it chug/ubuntu14.04x64 /bin/bash\r\nroot@aa97ba3292ce:/#

          -it 表示运行在交互模式,是-i -t的缩写,即-it是两个参数:-i和-t。前者表示打开并保持stdout,后者表示分配一个终端(pseudo-tty)一般这个模式就是可以启动bash,然后和容器有命令行的交互

          可以看到我们终端的字头变成root@aa97ba3292ce:/#了,这就意味着我们的镜像已经创建了一个容器实例。现在我们可以对这个“ubuntu系统”进行操作了

           

          比如安装vim:

          root@aa97ba3292ce:/# apt-get install vim\r\nReading package lists... Done\r\nBuilding dependency tree... Done\r\nThe following extra packages will be installed:\r\n  file libexpat1 libffi6 libgpm2 libmagic1 libpython2.7 libpython2.7-minimal libpython2.7-stdlib libsqlite3-0 libssl1.0.0 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  file libexpat1 libffi6 libgpm2 libmagic1 libpython2.7 libpython2.7-minimal libpython2.7-stdlib libsqlite3-0 libssl1.0.0 mime-support vim vim-common vim-runtime\r\n0 upgraded, 14 newly installed, 0 to remove and 0 not upgraded.\r\nNeed to get 10.7 MB of archives.\r\nAfter this operation, 50.7 MB of additional disk space will be used.\r\nDo you want to continue? [Y/n]

          看到我没有用sudo,是因为本来就已经是超级用户(root)状态了。

          同理按照我自己的需求,我安装了gcc和gdb

          + 想退出容器?很简单!

          如果使用exit,命令退出,则容器的状态处于Exit,而不是后台运行。如果想让容器一直运行,而不是停止,可以使用快捷键 ctrl+p ctrl+q 退出,此时容器的状态为Up。

          查看当前正在运行的容器:

          ~$ docker ps\r\nCONTAINER ID        IMAGE                 COMMAND             CREATED             STATUS              PORTS               NAMES\r\naa97ba3292ce        chug/ubuntu14.04x64   \"/bin/bash\"         7 minutes ago       Up 7 minutes                           relaxed_hoover

           

          看到当前有一个ID为aa97ba3292ce的容器(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)

          启动、停止、重启容器aa97ba3292ce的命令:

          ~$ docker start aa97ba3292ce\r\n~$ docker stop aa97ba3292ce\r\n~$ docker restart aa97ba3292ce

           

          后台启动一个容器后,如果想进入到这个容器,可以使用attach命令

          ~$ docker attach aa97ba3292ce

          + 软件装完,想保存环境?commit帮你!

          将容器转化为一个镜像,即执行commit操作,完成后可使用docker images查看

          root@aa97ba3292ce:/#exit //先退出容器\r\n~$ docker commit -m \"ubuntu with vim\" -a \"sgy\" aa97ba3292ce sgy/ubuntu:vim\r\n~$ docker images\r\nREPOSITORY                    TAG    IMAGE ID         CREATED             SIZE\r\nsgy/ubuntu                    vim    52166e4475ed     5 seconds ago       358.1 MB\r\nchug/ubuntu14.04x64           latest 0584b3d2cf6d     9 days ago          196.5 MB

           

          其中,-m指定说明信息;-a指定用户信息;aa97ba3292ce代表容器的id;sgy/ubuntu:vim指定目标镜像的用户名、仓库名和 tag 信息。我这里都是为了博客瞎编的用户名,我自己的用户名也不是sgy,你运行命令的时候使用自己注册Docker时的用户名。

          此时Docker中就有了我们新建的镜像sgy/ubuntu:vim,此镜像和原有的ubuntu镜像区别在于多了个vim工具。此时我们利用新镜像创建的容器,本身就自带vim了。

          启动新创建的镜像,可以看到vim已经自带了。

          ~$ docker run -it sgy/ubuntu:vim /bin/bash\r\nroot@520afc596c51:/# vim --version\r\nVIM - Vi IMproved 7.4 (2013 Aug 10, compiled Apr  4 2017 18:14:54)\r\n...\r\n//omitted by sgy(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)\r\n...

          利用exit退出容器。此时Docker引擎中就有了两个容器,可使用docker ps -a查看。

          + 想要删除容器或者镜像?用这个!

          如果想删除容器或者镜像,可以使用rm命令,注意:删除镜像前必须先删除以此镜像为基础的容器(哪怕是已经停止的容器),否则无法删除该镜像,会报错Failed to remove image (e4415b714b62): Error response from daemon: conflict: unable to delete e4415b714b62 (cannot be forced) - image has dependent child images类似这种

          ~$ docker rm container_id\r\n~$ docker rmi image_id

           

          有的时候尽管删除了全部容器,镜像还是无法删除,这时点击mac顶栏中的docker logo,选择restart,然后再试一次rmi,应该就没问题了。(Copyright © http://blog.csdn.net/s_gy_zetrov. All Rights Reserved)

          + 附上一张高大上的Docker命令图

          \"docker4.png\"

          + 一次配置,到处使用?那就push到hub上吧!

          因为之前已经在Docker.app中登陆了Docker账号,所以现在直接

          ~$ docker push sgy/ubuntu:vim

          就可以了!

          下次到了机房,在ubuntu系统中安装Docker,配置好加速器,启动Docker,docker login登陆自己的账号,然后直接~$ docker pull sgy/ubuntu:vim就可以把你push到hub的已经配置好的环境的镜像给扒下来。做完实验,再push上去就ok了!

          Docker中安装gcc、gdb时遇到的问题

          + add-apt-repository: command not found

          14.04系统解决办法:

          apt-get install software-properties-common

           

          older版本的系统:

          apt-get install python-software-properties

          + Docker中使用gdb无法进入断点,无法调试

          加上--privileged参数

          ~$ docker run -it --privileged sgy/ubuntu:vim /bin/bash
          ', 14, 10, 0, 1, 1, 1, '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软件,对于怎么配置,怎么玩,第一步干什么,', '/img/thumbnail/random/img_2.jpg'); COMMIT; -- ---------------------------- --- Table structure for `article_category_ref` +-- Table structure for article_category_ref -- ---------------------------- DROP TABLE IF EXISTS `article_category_ref`; CREATE TABLE `article_category_ref` ( - `article_id` int(11) DEFAULT NULL, - `category_id` int(11) DEFAULT NULL + `article_id` int(11) NOT NULL COMMENT '文章ID', + `category_id` int(11) NOT NULL COMMENT '分类ID', + PRIMARY KEY (`article_id`,`category_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- --- Records of `article_category_ref` +-- Records of article_category_ref -- ---------------------------- BEGIN; -INSERT INTO `article_category_ref` VALUES ('18', null), ('25', '10'), ('25', '12'), ('24', '10'), ('24', '13'), ('26', '15'), ('26', '100000004'), ('14', '1'), ('14', '9'), ('17', '1'), ('17', '2'), ('16', '1'), ('16', '2'), ('15', '1'), ('15', '8'), ('13', '10'), ('13', '13'), ('12', '1'), ('12', '2'), ('11', '1'), ('11', '2'), ('4', '10'), ('4', '14'), ('5', '1'), ('5', '8'), ('1', '1'), ('1', '9'), ('2', '1'), ('2', '9'), ('6', '1'), ('6', '9'), ('10', '1'), ('10', '2'), ('9', '1'), ('9', '9'), ('8', '1'), ('8', '9'), ('3', '1'), ('3', '8'), ('7', '1'), ('7', '9'), ('23', '1'), ('23', '7'), ('34', '10'), ('34', '13'), ('33', '10'), ('33', '13'), ('32', '15'), ('32', '100000006'), ('31', '15'), ('31', '16'), ('30', '10'), ('30', '13'), ('29', '15'), ('29', '100000003'), ('28', '15'), ('28', '100000003'); +INSERT INTO `article_category_ref` VALUES (1, 1); +INSERT INTO `article_category_ref` VALUES (1, 9); +INSERT INTO `article_category_ref` VALUES (2, 1); +INSERT INTO `article_category_ref` VALUES (2, 9); +INSERT INTO `article_category_ref` VALUES (3, 1); +INSERT INTO `article_category_ref` VALUES (3, 8); +INSERT INTO `article_category_ref` VALUES (4, 10); +INSERT INTO `article_category_ref` VALUES (4, 14); +INSERT INTO `article_category_ref` VALUES (5, 1); +INSERT INTO `article_category_ref` VALUES (5, 8); +INSERT INTO `article_category_ref` VALUES (6, 1); +INSERT INTO `article_category_ref` VALUES (6, 9); +INSERT INTO `article_category_ref` VALUES (7, 1); +INSERT INTO `article_category_ref` VALUES (7, 9); +INSERT INTO `article_category_ref` VALUES (8, 1); +INSERT INTO `article_category_ref` VALUES (8, 9); +INSERT INTO `article_category_ref` VALUES (9, 1); +INSERT INTO `article_category_ref` VALUES (9, 9); +INSERT INTO `article_category_ref` VALUES (10, 1); +INSERT INTO `article_category_ref` VALUES (10, 2); +INSERT INTO `article_category_ref` VALUES (11, 1); +INSERT INTO `article_category_ref` VALUES (11, 2); +INSERT INTO `article_category_ref` VALUES (12, 1); +INSERT INTO `article_category_ref` VALUES (12, 2); +INSERT INTO `article_category_ref` VALUES (13, 10); +INSERT INTO `article_category_ref` VALUES (13, 13); +INSERT INTO `article_category_ref` VALUES (14, 1); +INSERT INTO `article_category_ref` VALUES (14, 9); +INSERT INTO `article_category_ref` VALUES (15, 1); +INSERT INTO `article_category_ref` VALUES (15, 8); +INSERT INTO `article_category_ref` VALUES (16, 1); +INSERT INTO `article_category_ref` VALUES (16, 2); +INSERT INTO `article_category_ref` VALUES (17, 1); +INSERT INTO `article_category_ref` VALUES (17, 2); +INSERT INTO `article_category_ref` VALUES (23, 1); +INSERT INTO `article_category_ref` VALUES (23, 7); +INSERT INTO `article_category_ref` VALUES (24, 10); +INSERT INTO `article_category_ref` VALUES (24, 13); +INSERT INTO `article_category_ref` VALUES (25, 10); +INSERT INTO `article_category_ref` VALUES (25, 12); +INSERT INTO `article_category_ref` VALUES (26, 15); +INSERT INTO `article_category_ref` VALUES (26, 100000004); +INSERT INTO `article_category_ref` VALUES (28, 15); +INSERT INTO `article_category_ref` VALUES (28, 100000003); +INSERT INTO `article_category_ref` VALUES (29, 15); +INSERT INTO `article_category_ref` VALUES (29, 100000003); +INSERT INTO `article_category_ref` VALUES (30, 10); +INSERT INTO `article_category_ref` VALUES (30, 13); +INSERT INTO `article_category_ref` VALUES (31, 15); +INSERT INTO `article_category_ref` VALUES (31, 16); +INSERT INTO `article_category_ref` VALUES (32, 15); +INSERT INTO `article_category_ref` VALUES (32, 100000006); +INSERT INTO `article_category_ref` VALUES (33, 10); +INSERT INTO `article_category_ref` VALUES (33, 13); +INSERT INTO `article_category_ref` VALUES (34, 10); +INSERT INTO `article_category_ref` VALUES (34, 13); +INSERT INTO `article_category_ref` VALUES (35, 1); +INSERT INTO `article_category_ref` VALUES (35, 7); +INSERT INTO `article_category_ref` VALUES (36, 1); +INSERT INTO `article_category_ref` VALUES (36, 7); +INSERT INTO `article_category_ref` VALUES (38, 100000007); +INSERT INTO `article_category_ref` VALUES (38, 100000008); +INSERT INTO `article_category_ref` VALUES (40, 10); +INSERT INTO `article_category_ref` VALUES (40, 12); +INSERT INTO `article_category_ref` VALUES (44, 10); +INSERT INTO `article_category_ref` VALUES (44, 12); COMMIT; -- ---------------------------- --- Table structure for `article_tag_ref` +-- Table structure for article_tag_ref -- ---------------------------- DROP TABLE IF EXISTS `article_tag_ref`; CREATE TABLE `article_tag_ref` ( - `article_id` int(11) NOT NULL, - `tag_id` int(11) NOT NULL, + `article_id` int(11) NOT NULL COMMENT '文章ID', + `tag_id` int(11) NOT NULL COMMENT '标签ID', PRIMARY KEY (`article_id`,`tag_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- --- Records of `article_tag_ref` +-- Records of article_tag_ref -- ---------------------------- BEGIN; -INSERT INTO `article_tag_ref` VALUES ('1', '1'), ('1', '12'), ('2', '12'), ('3', '18'), ('4', '6'), ('5', '20'), ('6', '21'), ('7', '21'), ('8', '13'), ('9', '1'), ('9', '19'), ('10', '1'), ('23', '2'), ('23', '14'), ('24', '15'), ('24', '16'), ('25', '3'), ('25', '15'), ('26', '31'), ('26', '32'), ('28', '26'), ('28', '31'), ('29', '28'), ('30', '29'), ('31', '38'), ('32', '33'), ('33', '10'), ('34', '2'), ('34', '10'); +INSERT INTO `article_tag_ref` VALUES (1, 1); +INSERT INTO `article_tag_ref` VALUES (1, 12); +INSERT INTO `article_tag_ref` VALUES (2, 12); +INSERT INTO `article_tag_ref` VALUES (3, 18); +INSERT INTO `article_tag_ref` VALUES (4, 6); +INSERT INTO `article_tag_ref` VALUES (5, 20); +INSERT INTO `article_tag_ref` VALUES (6, 21); +INSERT INTO `article_tag_ref` VALUES (7, 21); +INSERT INTO `article_tag_ref` VALUES (8, 13); +INSERT INTO `article_tag_ref` VALUES (9, 1); +INSERT INTO `article_tag_ref` VALUES (9, 19); +INSERT INTO `article_tag_ref` VALUES (10, 1); +INSERT INTO `article_tag_ref` VALUES (23, 2); +INSERT INTO `article_tag_ref` VALUES (23, 14); +INSERT INTO `article_tag_ref` VALUES (24, 15); +INSERT INTO `article_tag_ref` VALUES (24, 16); +INSERT INTO `article_tag_ref` VALUES (25, 3); +INSERT INTO `article_tag_ref` VALUES (25, 15); +INSERT INTO `article_tag_ref` VALUES (26, 31); +INSERT INTO `article_tag_ref` VALUES (26, 32); +INSERT INTO `article_tag_ref` VALUES (28, 26); +INSERT INTO `article_tag_ref` VALUES (28, 31); +INSERT INTO `article_tag_ref` VALUES (29, 28); +INSERT INTO `article_tag_ref` VALUES (30, 29); +INSERT INTO `article_tag_ref` VALUES (31, 38); +INSERT INTO `article_tag_ref` VALUES (32, 33); +INSERT INTO `article_tag_ref` VALUES (33, 10); +INSERT INTO `article_tag_ref` VALUES (34, 2); +INSERT INTO `article_tag_ref` VALUES (34, 10); +INSERT INTO `article_tag_ref` VALUES (35, 1); +INSERT INTO `article_tag_ref` VALUES (35, 26); +INSERT INTO `article_tag_ref` VALUES (36, 1); +INSERT INTO `article_tag_ref` VALUES (36, 13); +INSERT INTO `article_tag_ref` VALUES (36, 14); +INSERT INTO `article_tag_ref` VALUES (36, 15); +INSERT INTO `article_tag_ref` VALUES (40, 1); +INSERT INTO `article_tag_ref` VALUES (44, 14); COMMIT; -- ---------------------------- --- Table structure for `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 '1', - `category_icon` varchar(20) DEFAULT NULL, + `category_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '分类ID', + `category_pid` int(11) DEFAULT NULL COMMENT '分类父ID', + `category_name` varchar(50) DEFAULT NULL COMMENT '分类名称', + `category_description` varchar(255) DEFAULT NULL COMMENT '描述', + `category_order` int(11) unsigned DEFAULT '1' COMMENT '排序值', + `category_icon` varchar(20) DEFAULT NULL COMMENT '图标', PRIMARY KEY (`category_id`), UNIQUE KEY `category_name` (`category_name`) -) ENGINE=InnoDB AUTO_INCREMENT=100000007 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=100000009 DEFAULT CHARSET=utf8; -- ---------------------------- --- Records of `category` +-- Records of category -- ---------------------------- BEGIN; -INSERT INTO `category` VALUES ('1', '0', 'Java', 'Java语言', '1', 'fa fa-coffee'), ('2', '1', 'Java基础', '', '1', ''), ('3', '1', 'Core Java', '', '1', ''), ('4', '1', '多线程并发编程', '', '1', ''), ('5', '1', 'Sockets和IO', '', '1', ''), ('6', '1', '设计模式和反射', '', '1', ''), ('7', '1', 'JVM', '', '1', ''), ('8', '1', 'JavaWeb', '', '1', ''), ('9', '1', 'Java框架', '', '1', ''), ('10', '0', '计算机科学', '', '1', 'fa fa-cubes'), ('11', '10', '数据结构和算法', '', '1', ''), ('12', '10', '操作系统', '', '1', ''), ('13', '10', '数据库', '', '1', ''), ('14', '10', '计算机网络', '', '1', ''), ('15', '0', '其他技术', '', '1', 'fa-snowflake-o fa'), ('16', '15', '消息服务', '', '1', ''), ('17', '15', '缓存服务', '', '1', ''), ('19', '100000000', 'Hello', '1111', '1', '11'), ('100000003', '15', '微服务', '', null, ''), ('100000004', '15', '搜索引擎', '', null, ''), ('100000005', '15', '权限框架', '', null, ''), ('100000006', '15', '开发利器', '', null, ''); +INSERT INTO `category` VALUES (1, 0, 'Java', 'Java语言', 1, 'fa fa-coffee'); +INSERT INTO `category` VALUES (2, 1, 'Java基础', '', 1, ''); +INSERT INTO `category` VALUES (3, 1, 'Core Java', '', 1, ''); +INSERT INTO `category` VALUES (4, 1, '多线程并发编程', '', 1, ''); +INSERT INTO `category` VALUES (5, 1, 'Sockets和IO', '', 1, ''); +INSERT INTO `category` VALUES (6, 1, '设计模式和反射', '', 1, ''); +INSERT INTO `category` VALUES (7, 1, 'JVM', '', 1, ''); +INSERT INTO `category` VALUES (8, 1, 'JavaWeb', '', 1, ''); +INSERT INTO `category` VALUES (9, 1, 'Java框架', '', 1, ''); +INSERT INTO `category` VALUES (10, 0, '计算机科学', '', 1, 'fa fa-cubes'); +INSERT INTO `category` VALUES (11, 10, '数据结构和算法', '', 1, ''); +INSERT INTO `category` VALUES (12, 10, '操作系统', '', 1, ''); +INSERT INTO `category` VALUES (13, 10, '数据库', '', 1, ''); +INSERT INTO `category` VALUES (14, 10, '计算机网络', '', 1, ''); +INSERT INTO `category` VALUES (15, 0, '其他技术', '', 1, 'fa-snowflake-o fa'); +INSERT INTO `category` VALUES (16, 15, '消息服务', '', 1, ''); +INSERT INTO `category` VALUES (17, 15, '缓存服务', '', 1, ''); +INSERT INTO `category` VALUES (19, 100000000, 'Hello', '1111', 1, '11'); +INSERT INTO `category` VALUES (100000003, 15, '微服务', '', NULL, ''); +INSERT INTO `category` VALUES (100000004, 15, '搜索引擎', '', NULL, ''); +INSERT INTO `category` VALUES (100000005, 15, '权限框架', '', NULL, ''); +INSERT INTO `category` VALUES (100000006, 15, '开发利器', '', NULL, ''); +INSERT INTO `category` VALUES (100000007, 0, 'test', '', NULL, ''); +INSERT INTO `category` VALUES (100000008, 100000007, 'test2', '', NULL, ''); COMMIT; -- ---------------------------- --- Table structure for `comment` +-- Table structure for comment -- ---------------------------- DROP TABLE IF EXISTS `comment`; CREATE TABLE `comment` ( - `comment_id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `comment_pid` int(11) unsigned DEFAULT '0', - `comment_pname` varchar(255) DEFAULT NULL, - `comment_article_id` int(11) unsigned DEFAULT NULL, - `comment_author_name` varchar(50) DEFAULT NULL, - `comment_author_email` varchar(50) DEFAULT NULL, - `comment_author_url` varchar(50) DEFAULT NULL, - `comment_author_avatar` varchar(100) DEFAULT NULL, - `comment_content` varchar(1000) DEFAULT NULL, - `comment_agent` varchar(200) DEFAULT NULL, - `comment_ip` varchar(50) DEFAULT NULL, - `comment_create_time` datetime DEFAULT NULL, - `comment_role` int(1) DEFAULT NULL, + `comment_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '评论ID', + `comment_pid` int(11) unsigned DEFAULT '0' COMMENT '上级评论ID', + `comment_pname` varchar(255) DEFAULT NULL COMMENT '上级评论名称', + `comment_article_id` int(11) unsigned NOT NULL COMMENT '文章ID', + `comment_author_name` varchar(50) DEFAULT NULL COMMENT '评论人名称', + `comment_author_email` varchar(50) DEFAULT NULL COMMENT '评论人邮箱', + `comment_author_url` varchar(50) DEFAULT NULL COMMENT '评论人个人主页', + `comment_author_avatar` varchar(100) DEFAULT NULL COMMENT '评论人头像', + `comment_content` varchar(1000) DEFAULT NULL COMMENT '内容', + `comment_agent` varchar(200) DEFAULT NULL COMMENT '浏览器信息', + `comment_ip` varchar(50) DEFAULT NULL COMMENT 'IP', + `comment_create_time` datetime DEFAULT NULL COMMENT '评论时间', + `comment_role` int(1) DEFAULT NULL COMMENT '角色,是否管理员', + `comment_user_id` int(11) DEFAULT NULL COMMENT '评论ID,可能为空', PRIMARY KEY (`comment_id`) -) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=utf8; -- ---------------------------- --- Records of `comment` +-- Records of comment -- ---------------------------- BEGIN; -INSERT INTO `comment` VALUES ('1', '0', '', '17', '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', null, '评论测试', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:06:34', '1'), ('2', '1', '言曌', '17', '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', null, '再次测试22', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:06:43', '1'), ('3', '0', '', '8', '张三', '123@qq.com', '', null, '评论测试', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:08:06', '0'), ('4', '0', '', '8', 'Tom', '123@126.com', '', null, 'Tom来测试', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:08:46', '0'), ('5', '0', '', '14', 'Jack', '666@qq.com', '', null, 'Jack来此一游', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:09:27', '0'), ('6', '5', 'Jack', '14', 'Jack', '666@qq.com', '', null, 'Jack回复Jack测试', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:10:10', '0'), ('7', '5', 'Jack', '14', '娜美', '123456@sina.com', '', null, '娜美回复Jack测试', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:10:53', '0'), ('8', '0', '', '9', '苏珊', '1212@sina.com', '', null, '苏珊来过', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:11:36', '0'), ('9', '8', '苏珊', '9', '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', null, '欢迎苏珊', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:12:00', '1'), ('10', '0', '', '10', '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', null, '博主测试', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:12:15', '1'), ('11', '0', '', '13', '多伦', 'duolun@gmail.com', '', null, '多伦路过', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:13:09', '0'), ('12', '0', '', '13', '贾克斯', 'jax@sohu.com', '', null, '我是贾克斯', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:13:44', '0'), ('13', '12', '贾克斯', '13', '瑞克斯', 'ruikou@126.com', '', null, '贾克斯你好,我是瑞克斯', null, '0:0:0:0:0:0:0:1', '2017-10-07 16:14:32', '0'), ('14', '0', '', '11', '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', null, '评论测试', null, '0:0:0:0:0:0:0:1', '2017-10-10 11:44:28', '1'), ('15', '14', '言曌', '11', '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', null, '再次测试', null, '0:0:0:0:0:0:0:1', '2017-10-10 11:44:36', '1'), ('16', '0', '', '2', 'xxxxx', '847064370@qq.com', 'http://liuyanzhao.com', null, 'xxxxx', null, '0:0:0:0:0:0:0:1', '2018-11-24 00:20:09', '0'), ('17', '0', '', '2', 'saysky', '847064370@qq.com', 'http://liuyanzhao.com', null, '11111', null, '0:0:0:0:0:0:0:1', '2018-11-24 00:20:30', '0'), ('18', '0', '', '2', 'saysky', '847064370@qq.com', 'http://liuyanzhao.com', 'http://cn.gravatar.com/avatar/3ae8728fec3cd5cbfe99c4b966695f03?s=128&d=identicon&r=PG', 'ssssss', null, '0:0:0:0:0:0:0:1', '2018-11-24 23:44:03', '0'), ('19', '17', 'saysky', '2', 'saysky2', '847064370@qq.com', '', 'http://cn.gravatar.com/avatar/3ae8728fec3cd5cbfe99c4b966695f03?s=128&d=identicon&r=PG', 'sssssss', null, '0:0:0:0:0:0:0:1', '2018-11-24 23:44:24', '0'), ('20', '17', 'saysky2', '2', 'saysky3', '847064370@qq.com', '', 'http://cn.gravatar.com/avatar/3ae8728fec3cd5cbfe99c4b966695f03?s=128&d=identicon&r=PG', '33333', null, '0:0:0:0:0:0:0:1', '2018-11-24 23:44:45', '0'), ('21', '0', '', '6', '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', 'http://cn.gravatar.com/avatar/01459f970ce17cd9e1e783160ecc951c?s=128&d=identicon&r=PG', 'ssssss', null, '0:0:0:0:0:0:0:1', '2018-11-25 13:52:29', '1'), ('22', '0', '', '6', '你好', '123@qq.com', '', 'http://cn.gravatar.com/avatar/487f87505f619bf9ea08f26bb34f8118?s=128&d=identicon&r=PG', 'ssss', null, '0:0:0:0:0:0:0:1', '2018-11-25 13:52:57', '0'), ('23', '22', '你好', '6', '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', null, '1111111', null, '0:0:0:0:0:0:0:1', '2018-11-25 13:53:19', '1'), ('24', '22', '你好', '6', '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', null, 'sss', null, '0:0:0:0:0:0:0:1', '2018-11-25 13:58:18', '1'), ('25', '1', '言曌', '17', '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', null, '11111111', null, '0:0:0:0:0:0:0:1', '2018-11-25 14:27:26', '1'), ('28', '25', '言曌', '17', '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', null, '', null, '0:0:0:0:0:0:0:1', '2018-12-21 21:46:37', '1'), ('29', '21', '言曌', '6', '1111', '1234@qq.com', '', 'http://cn.gravatar.com/avatar/88d5cb704d88bdad67d000eee4782000?s=128&d=identicon&r=PG', '1', null, '0:0:0:0:0:0:0:1', '2018-12-24 10:34:37', '0'), ('30', '21', '1111', '6', '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', 'http://cn.gravatar.com/avatar/01459f970ce17cd9e1e783160ecc951c?s=128&d=identicon&r=PG', '2', null, '0:0:0:0:0:0:0:1', '2018-12-24 10:34:56', '1'), ('31', '22', '你好', '6', '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', 'http://cn.gravatar.com/avatar/01459f970ce17cd9e1e783160ecc951c?s=128&d=identicon&r=PG', '22222', null, '0:0:0:0:0:0:0:1', '2018-12-24 10:35:13', '1'); +INSERT INTO `comment` VALUES (1, 0, '', 17, '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '/img/avatar/avatar1.jpg', '评论测试', NULL, '0:0:0:0:0:0:0:1', '2017-10-07 16:06:34', 1, 1); +INSERT INTO `comment` VALUES (2, 1, '言曌', 17, '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '/img/avatar/avatar1.jpg', '再次测试22', NULL, '0:0:0:0:0:0:0:1', '2017-10-07 16:06:43', 1, 1); +INSERT INTO `comment` VALUES (3, 0, '', 8, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', '评论测试', NULL, '0:0:0:0:0:0:0:1', '2017-10-07 16:08:06', 0, 3); +INSERT INTO `comment` VALUES (4, 0, '', 8, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', 'Tom来测试', NULL, '0:0:0:0:0:0:0:1', '2017-10-07 16:08:46', 0, 3); +INSERT INTO `comment` VALUES (5, 0, '', 14, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', 'Jack来此一游', NULL, '0:0:0:0:0:0:0:1', '2017-10-07 16:09:27', 0, 3); +INSERT INTO `comment` VALUES (6, 5, 'Jack', 14, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', 'Jack回复Jack测试', NULL, '0:0:0:0:0:0:0:1', '2017-10-07 16:10:10', 0, 3); +INSERT INTO `comment` VALUES (7, 5, 'Jack', 14, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', '娜美回复Jack测试', NULL, '0:0:0:0:0:0:0:1', '2017-10-07 16:10:53', 0, 3); +INSERT INTO `comment` VALUES (8, 0, '', 9, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', '苏珊来过', NULL, '0:0:0:0:0:0:0:1', '2017-10-07 16:11:36', 0, 3); +INSERT INTO `comment` VALUES (9, 8, '苏珊', 9, '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '/img/avatar/avatar1.jpg', '欢迎苏珊', NULL, '0:0:0:0:0:0:0:1', '2017-10-07 16:12:00', 1, 1); +INSERT INTO `comment` VALUES (10, 0, '', 10, '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '/img/avatar/avatar1.jpg', '博主测试', NULL, '0:0:0:0:0:0:0:1', '2017-10-07 16:12:15', 1, 1); +INSERT INTO `comment` VALUES (11, 0, '', 13, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', '多伦路过', NULL, '0:0:0:0:0:0:0:1', '2017-10-07 16:13:09', 0, 3); +INSERT INTO `comment` VALUES (12, 0, '', 13, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', '我是贾克斯', NULL, '0:0:0:0:0:0:0:1', '2017-10-07 16:13:44', 0, 3); +INSERT INTO `comment` VALUES (13, 12, '贾克斯', 13, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', '贾克斯你好,我是瑞克斯', NULL, '0:0:0:0:0:0:0:1', '2017-10-07 16:14:32', 0, 3); +INSERT INTO `comment` VALUES (14, 0, '', 11, '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '/img/avatar/avatar1.jpg', '评论测试', NULL, '0:0:0:0:0:0:0:1', '2017-10-10 11:44:28', 1, 1); +INSERT INTO `comment` VALUES (15, 14, '言曌', 11, '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '/img/avatar/avatar1.jpg', '再次测试', NULL, '0:0:0:0:0:0:0:1', '2017-10-10 11:44:36', 1, 1); +INSERT INTO `comment` VALUES (16, 0, '', 2, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', 'xxxxx', NULL, '0:0:0:0:0:0:0:1', '2018-11-24 00:20:09', 0, 3); +INSERT INTO `comment` VALUES (17, 0, '', 2, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', '11111', NULL, '0:0:0:0:0:0:0:1', '2018-11-24 00:20:30', 0, 3); +INSERT INTO `comment` VALUES (18, 0, '', 2, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', 'ssssss', NULL, '0:0:0:0:0:0:0:1', '2018-11-24 23:44:03', 0, 3); +INSERT INTO `comment` VALUES (19, 17, 'saysky', 2, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', 'sssssss', NULL, '0:0:0:0:0:0:0:1', '2018-11-24 23:44:24', 0, 3); +INSERT INTO `comment` VALUES (20, 17, 'saysky2', 2, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', '33333', NULL, '0:0:0:0:0:0:0:1', '2018-11-24 23:44:45', 0, 3); +INSERT INTO `comment` VALUES (21, 0, '', 6, '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '/img/avatar/avatar1.jpg', 'ssssss', NULL, '0:0:0:0:0:0:0:1', '2018-11-25 13:52:29', 1, 1); +INSERT INTO `comment` VALUES (22, 0, '', 6, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', 'ssss', NULL, '0:0:0:0:0:0:0:1', '2018-11-25 13:52:57', 0, 3); +INSERT INTO `comment` VALUES (23, 22, '你好', 6, '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '/img/avatar/avatar1.jpg', '1111111', NULL, '0:0:0:0:0:0:0:1', '2018-11-25 13:53:19', 1, 1); +INSERT INTO `comment` VALUES (24, 22, '你好', 6, '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '/img/avatar/avatar1.jpg', 'sss', NULL, '0:0:0:0:0:0:0:1', '2018-11-25 13:58:18', 1, 1); +INSERT INTO `comment` VALUES (25, 1, '言曌', 17, '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '/img/avatar/avatar1.jpg', '11111111', NULL, '0:0:0:0:0:0:0:1', '2018-11-25 14:27:26', 1, 1); +INSERT INTO `comment` VALUES (28, 25, '言曌', 17, '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '/img/avatar/avatar1.jpg', '', NULL, '0:0:0:0:0:0:0:1', '2018-12-21 21:46:37', 1, 1); +INSERT INTO `comment` VALUES (29, 21, '言曌', 6, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', '1', NULL, '0:0:0:0:0:0:0:1', '2018-12-24 10:34:37', 0, 3); +INSERT INTO `comment` VALUES (30, 21, '1111', 6, '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '/img/avatar/avatar1.jpg', '2', NULL, '0:0:0:0:0:0:0:1', '2018-12-24 10:34:56', 1, 1); +INSERT INTO `comment` VALUES (31, 22, '你好', 6, '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '/img/avatar/avatar1.jpg', '22222', NULL, '0:0:0:0:0:0:0:1', '2018-12-24 10:35:13', 1, 1); +INSERT INTO `comment` VALUES (32, 0, '', 32, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', '111', NULL, '0:0:0:0:0:0:0:1', '2020-01-31 19:18:00', 0, 3); +INSERT INTO `comment` VALUES (33, 0, '', 32, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', '666', NULL, '0:0:0:0:0:0:0:1', '2020-01-31 19:20:38', 0, 3); +INSERT INTO `comment` VALUES (34, 0, '', 32, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', '1122', NULL, '0:0:0:0:0:0:0:1', '2020-01-31 19:21:56', 0, 3); +INSERT INTO `comment` VALUES (35, 0, '', 32, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', '1112', NULL, '0:0:0:0:0:0:0:1', '2020-01-31 19:22:14', 0, 3); +INSERT INTO `comment` VALUES (36, 0, '', 32, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', '6666', NULL, '0:0:0:0:0:0:0:1', '2020-01-31 19:22:19', 0, 3); +INSERT INTO `comment` VALUES (37, 36, 'zhangsan', 32, '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '/img/avatar/avatar1.jpg', '222', NULL, '0:0:0:0:0:0:0:1', '2020-04-18 21:32:14', 1, 1); +INSERT INTO `comment` VALUES (38, 0, '', 27, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', '22222233', NULL, '0:0:0:0:0:0:0:1', '2020-04-18 21:33:16', 0, 3); +INSERT INTO `comment` VALUES (39, 38, '222', 27, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', '333222', NULL, '0:0:0:0:0:0:0:1', '2020-04-18 21:33:58', 0, 3); +INSERT INTO `comment` VALUES (40, 0, '', 27, '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', '中文', NULL, '0:0:0:0:0:0:0:1', '2021-02-25 07:01:46', 0, 3); +INSERT INTO `comment` VALUES (41, 0, '', 27, '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '/img/avatar/avatar1.jpg', '中文', NULL, '0:0:0:0:0:0:0:1', '2021-02-25 07:02:12', 1, 1); +INSERT INTO `comment` VALUES (42, 0, '', 32, '张三', 'zhangsan@china.com', '', 'http://cn.gravatar.com/avatar/null?s=128&d=identicon&r=PG', '!!!', NULL, '0:0:0:0:0:0:0:1', '2021-02-25 07:43:51', 1, 2); COMMIT; -- ---------------------------- --- Table structure for `link` +-- Table structure for link -- ---------------------------- DROP TABLE IF EXISTS `link`; CREATE TABLE `link` ( - `link_id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `link_url` varchar(255) DEFAULT NULL, - `link_name` varchar(255) DEFAULT NULL, - `link_image` varchar(255) DEFAULT NULL, - `link_description` varchar(255) DEFAULT NULL, - `link_owner_nickname` varchar(40) DEFAULT NULL, - `link_owner_contact` varchar(255) DEFAULT NULL, - `link_update_time` datetime DEFAULT NULL, - `link_create_time` datetime DEFAULT NULL, - `link_order` int(2) unsigned DEFAULT '1', - `link_status` int(1) unsigned DEFAULT '1', + `link_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '链接ID', + `link_url` varchar(255) DEFAULT NULL COMMENT 'URL', + `link_name` varchar(255) DEFAULT NULL COMMENT '姓名', + `link_image` varchar(255) DEFAULT NULL COMMENT '头像', + `link_description` varchar(255) DEFAULT NULL COMMENT '描述', + `link_owner_nickname` varchar(40) DEFAULT NULL COMMENT '所属人名称', + `link_owner_contact` varchar(255) DEFAULT NULL COMMENT '联系方式', + `link_update_time` datetime DEFAULT NULL COMMENT '更新时间', + `link_create_time` datetime DEFAULT NULL COMMENT '创建时间', + `link_order` int(2) unsigned DEFAULT '1' COMMENT '排序号', + `link_status` int(1) unsigned DEFAULT '1' COMMENT '状态', PRIMARY KEY (`link_id`), UNIQUE KEY `link_name` (`link_name`) -) ENGINE=MyISAM AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; +) ENGINE=MyISAM AUTO_INCREMENT=10 DEFAULT CHARSET=utf8; -- ---------------------------- --- Records of `link` +-- Records of link -- ---------------------------- BEGIN; -INSERT INTO `link` VALUES ('1', 'http://liuyanzhao.com', '言曌博客', null, '一个码农的成长之路', null, '你好,我的丘丘是847064370', '2017-10-07 16:51:03', '2017-10-07 16:29:35', '1', '1'), ('6', 'http://codergroup.cn/', '开发者社区', null, '', null, 'admin@liuyanzhao.com', '2018-11-25 15:03:26', '2017-10-07 16:51:49', '1', '1'); +INSERT INTO `link` VALUES (1, 'http://liuyanzhao.com', '言曌博客', NULL, '一个码农的成长之路', NULL, '你好,我的丘丘是847064370', '2017-10-07 16:51:03', '2017-10-07 16:29:35', 1, 1); +INSERT INTO `link` VALUES (2, 'https://liuyanzhao.com/shop.html', '付费商品', NULL, '言曌博客付费商品', NULL, '言曌博客付费商品', '2021-03-19 18:01:28', '2021-03-19 18:01:28', 1, 1); +INSERT INTO `link` VALUES (3, 'https://liuyanzhao.com/my-service.html', '人工服务', NULL, '人工服务', NULL, NULL, '2021-03-19 18:02:31', '2021-03-19 18:02:31', 1, 1); +INSERT INTO `link` VALUES (4, 'https://github.com/saysky', 'GitHub主页', NULL, NULL, NULL, NULL, '2021-03-19 18:02:31', '2021-03-19 18:02:31', 1, 1); +INSERT INTO `link` VALUES (5, 'https://shop250529942.world.taobao.com/', '淘宝店', NULL, NULL, NULL, NULL, NULL, NULL, 1, 1); COMMIT; -- ---------------------------- --- Table structure for `menu` +-- Table structure for menu -- ---------------------------- DROP TABLE IF EXISTS `menu`; CREATE TABLE `menu` ( - `menu_id` int(11) NOT NULL AUTO_INCREMENT, - `menu_name` varchar(255) DEFAULT NULL, - `menu_url` varchar(255) DEFAULT NULL, - `menu_level` int(11) DEFAULT NULL, - `menu_icon` varchar(255) DEFAULT NULL, - `menu_order` int(11) DEFAULT NULL, + `menu_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '菜单ID', + `menu_name` varchar(255) DEFAULT NULL COMMENT '名称', + `menu_url` varchar(255) DEFAULT NULL COMMENT 'URL', + `menu_level` int(11) DEFAULT NULL COMMENT '等级', + `menu_icon` varchar(255) DEFAULT NULL COMMENT '图标', + `menu_order` int(11) DEFAULT NULL COMMENT '排序值', PRIMARY KEY (`menu_id`), UNIQUE KEY `menu_name` (`menu_name`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; -- ---------------------------- --- Records of `menu` +-- Records of menu -- ---------------------------- BEGIN; -INSERT INTO `menu` VALUES ('1', '留言板', '/message', '2', 'fa fa-comment', '1'), ('2', 'LeetCode', 'https://leetcode.com/problemset/all', '2', 'ssss', '3'), ('3', '关于本站', '/aboutSite', '1', 'fa fa-info', '1'), ('4', '文章归档', '/articleFile', '1', 'fa-list-alt fa', '2'), ('5', '申请友链', '/applyLink', '1', 'fa fa-link', '3'); +INSERT INTO `menu` VALUES (1, '留言板', '/message', 2, 'fa fa-comment', 1); +INSERT INTO `menu` VALUES (2, 'LeetCode', 'https://leetcode.com/problemset/all', 2, 'ssss', 3); +INSERT INTO `menu` VALUES (3, '关于本站', '/aboutSite', 1, 'fa fa-info', 1); +INSERT INTO `menu` VALUES (4, '文章归档', '/articleFile', 1, 'fa-list-alt fa', 2); +INSERT INTO `menu` VALUES (5, '申请友链', '/applyLink', 1, 'fa fa-link', 3); COMMIT; -- ---------------------------- --- Table structure for `notice` +-- Table structure for notice -- ---------------------------- DROP TABLE IF EXISTS `notice`; CREATE TABLE `notice` ( - `notice_id` int(11) NOT NULL AUTO_INCREMENT, - `notice_title` varchar(255) DEFAULT NULL, - `notice_content` varchar(10000) DEFAULT NULL, - `notice_create_time` datetime DEFAULT NULL, - `notice_update_time` datetime DEFAULT NULL, - `notice_status` int(1) unsigned DEFAULT '1', - `notice_order` int(2) DEFAULT NULL, + `notice_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '公告ID', + `notice_title` varchar(255) DEFAULT NULL COMMENT '公告标题', + `notice_content` varchar(10000) DEFAULT NULL COMMENT '内容', + `notice_create_time` datetime DEFAULT NULL COMMENT '创建时间', + `notice_update_time` datetime DEFAULT NULL COMMENT '更新时间', + `notice_status` int(1) unsigned DEFAULT '1' COMMENT '状态', + `notice_order` int(2) DEFAULT NULL COMMENT '排序值', PRIMARY KEY (`notice_id`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; -- ---------------------------- --- Records of `notice` +-- Records of notice -- ---------------------------- BEGIN; -INSERT INTO `notice` VALUES ('1', '本站下载地址', '

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

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


          ', '2017-10-06 23:44:35', '2018-11-25 15:02:01', '1', '1'), ('3', '终于开始更新了', '

          距离上一次更新有一年了

          最近抽时间更新一波


          主要内容

          1、去除 Custom 包

          2、规范 MyBatis 语句

          3、规范注释

          4、删除不必要的字段

          5、修改分类和标签关联关系

          6、添加Redis,加快访问速度


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

          谢谢大家鼓励,帮忙点 star 和 fork 哦


          更新时间:2018年11月25日

          ', '2018-11-25 20:28:33', '2018-11-25 20:28:43', '1', '2'); +INSERT INTO `notice` VALUES (4, '请勿转载本站文章', '

          未经博主同意,请勿转载本站文章

          ', '2018-05-26 20:40:50', '2018-05-26 20:40:50', 1, 1); +INSERT INTO `notice` VALUES (5, '博主提供有偿服务', '

          博主提供有偿服务

          具体查看:https://liuyanzhao.com/shop.html

          ', '2018-05-26 20:40:50', '2021-02-25 10:20:44', 1, 1); COMMIT; -- ---------------------------- --- Table structure for `options` +-- Table structure for options -- ---------------------------- DROP TABLE IF EXISTS `options`; CREATE TABLE `options` ( @@ -225,82 +404,126 @@ CREATE TABLE `options` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- --- Records of `options` +-- Records of options -- ---------------------------- BEGIN; -INSERT INTO `options` VALUES ('1', '风吟博客', '莫问收获,但问耕耘。', '风吟博客,一个简洁的Java博客,言曌程序学习的一个新起点。', '风吟,风吟博客,Java博客,SSM博客,言曌,言曌博客', '/uploads/2018/11/avatar(3).jpg', '博客初心', '程序人生,永不止步', '/uploads/2017/10/20171006224906122.jpg', '847064370', 'saysky', '5936412667', null, '1'); +INSERT INTO `options` VALUES (1, '风吟博客', '莫问收获,但问耕耘。', '风吟博客,一个简洁的Java博客,言曌程序学习的一个新起点。', '风吟,风吟博客,Java博客,SSM博客,言曌,言曌博客', '/img/logo.png', '博客初心', '程序人生,永不止步', '/uploads/2017/10/20171006224906122.jpg', '847064370', 'saysky', '5936412667', NULL, 1); COMMIT; -- ---------------------------- --- Table structure for `page` +-- Table structure for page -- ---------------------------- DROP TABLE IF EXISTS `page`; CREATE TABLE `page` ( - `page_id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `page_key` varchar(50) DEFAULT NULL, - `page_title` varchar(50) DEFAULT NULL, - `page_content` mediumtext, - `page_create_time` datetime DEFAULT NULL, - `page_update_time` datetime DEFAULT NULL, - `page_view_count` int(10) unsigned DEFAULT '0', - `page_comment_count` int(5) unsigned DEFAULT '0', - `page_status` int(1) unsigned DEFAULT '1', + `page_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自定义页面ID', + `page_key` varchar(50) DEFAULT NULL COMMENT 'key', + `page_title` varchar(50) DEFAULT NULL COMMENT '标题', + `page_content` mediumtext COMMENT '内容', + `page_create_time` datetime DEFAULT NULL COMMENT '创建时间', + `page_update_time` datetime DEFAULT NULL COMMENT '更新时间', + `page_view_count` int(10) unsigned DEFAULT '0' COMMENT '访问量', + `page_comment_count` int(5) unsigned DEFAULT '0' COMMENT '评论数', + `page_status` int(1) unsigned DEFAULT '1' COMMENT '状态', PRIMARY KEY (`page_id`), UNIQUE KEY `page_key` (`page_key`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8; -- ---------------------------- --- Records of `page` +-- Records of page -- ---------------------------- BEGIN; -INSERT INTO `page` VALUES ('1', 'map', '站点地图', null, null, null, '0', '0', '2'), ('2', 'articleFile', '文章归档', null, null, null, '0', '0', '2'), ('3', 'message', '留言板', null, null, null, '0', '0', '2'), ('4', 'applyLink', '申请友链', null, null, null, '0', '0', '2'), ('5', 'aboutSite', '关于本站', '

          关于项目

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

          效果预览

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

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

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

           

          后台地址:/admin 或者 /login

          用户名为admin,密码为123456

           

          使用注意

          1、使用IDE导入项目

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

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

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

          2、 导入数据库

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

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

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

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

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

           

          4、修改上传文件路径

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

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

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

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

           

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

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

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

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

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

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

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

           

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

          跟第5步相似

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

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

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

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

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

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

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

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

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

           

          下载地址:

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

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

           

          更新

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

          ', '2017-10-06 23:40:35', '2017-10-10 14:58:12', null, null, '1'), ('7', 'hello', '11', '11111111', '2018-12-21 21:46:50', '2018-12-21 21:46:50', null, null, '1'); +INSERT INTO `page` VALUES (1, 'map', '站点地图', NULL, NULL, NULL, 0, 0, 2); +INSERT INTO `page` VALUES (2, 'articleFile', '文章归档', NULL, NULL, NULL, 0, 0, 2); +INSERT INTO `page` VALUES (3, 'message', '留言板', NULL, NULL, NULL, 0, 0, 2); +INSERT INTO `page` VALUES (4, 'applyLink', '申请友链', NULL, NULL, NULL, 0, 0, 2); +INSERT INTO `page` VALUES (5, 'aboutSite', '关于本站', '

          关于项目

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

          效果预览

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

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

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

           

          后台地址:/admin 或者 /login

          用户名为admin,密码为123456

           

          使用注意

          1、使用IDE导入项目

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

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

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

          2、 导入数据库

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

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

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

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

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

           

          4、修改上传文件路径

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

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

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

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

           

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

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

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

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

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

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

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

           

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

          跟第5步相似

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

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

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

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

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

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

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

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

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

           

          下载地址:

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

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

           

          更新

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

          ', '2017-10-06 23:40:35', '2017-10-10 14:58:12', NULL, NULL, 1); +INSERT INTO `page` VALUES (7, 'hello', '11', '11111111', '2018-12-21 21:46:50', '2018-12-21 21:46:50', NULL, NULL, 1); COMMIT; -- ---------------------------- --- Table structure for `tag` +-- Table structure for tag -- ---------------------------- DROP TABLE IF EXISTS `tag`; CREATE TABLE `tag` ( - `tag_id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `tag_name` varchar(50) DEFAULT NULL, - `tag_description` varchar(255) DEFAULT NULL, + `tag_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '标签ID', + `tag_name` varchar(50) DEFAULT NULL COMMENT '标签名称', + `tag_description` varchar(100) DEFAULT NULL COMMENT '描述', PRIMARY KEY (`tag_id`), UNIQUE KEY `tag_name` (`tag_name`) ) ENGINE=InnoDB AUTO_INCREMENT=39 DEFAULT CHARSET=utf8; -- ---------------------------- --- Records of `tag` +-- Records of tag -- ---------------------------- BEGIN; -INSERT INTO `tag` VALUES ('1', 'Java', 'Java'), ('2', '算法', ''), ('3', '数据结构', ''), ('4', 'C', ''), ('5', '操作系统', ''), ('6', '计算机网络', ''), ('8', '面试题', ''), ('9', '数据库', ''), ('10', 'MySQL', ''), ('11', 'Spring', ''), ('12', 'SpringMVC', ''), ('13', 'MyBatis', ''), ('14', 'JVM', ''), ('15', '设计模式', ''), ('16', '网络编程', ''), ('17', 'IO', ''), ('18', 'JSP', ''), ('19', 'Servlet', ''), ('20', 'JavaWeb', ''), ('21', 'Hibernate', ''), ('22', 'SQL', ''), ('24', 'Redis', 'redis'), ('25', 'SPA', ''), ('26', 'SpringCloud', ''), ('27', 'Dubbo', ''), ('28', 'EDAS', ''), ('29', 'MongoDB', ''), ('31', 'SpringBoot', ''), ('32', 'ElasticSearch', ''), ('33', 'Docker', ''), ('34', 'Jenkins', ''), ('35', 'Vue', ''), ('36', 'IDEA', ''), ('37', 'Shiro', ''), ('38', 'RocketMQ', ''); +INSERT INTO `tag` VALUES (1, 'Java', 'Java'); +INSERT INTO `tag` VALUES (2, '算法', ''); +INSERT INTO `tag` VALUES (3, '数据结构', ''); +INSERT INTO `tag` VALUES (4, 'C', ''); +INSERT INTO `tag` VALUES (5, '操作系统', ''); +INSERT INTO `tag` VALUES (6, '计算机网络', ''); +INSERT INTO `tag` VALUES (8, '面试题', ''); +INSERT INTO `tag` VALUES (9, '数据库', ''); +INSERT INTO `tag` VALUES (10, 'MySQL', ''); +INSERT INTO `tag` VALUES (11, 'Spring', ''); +INSERT INTO `tag` VALUES (12, 'SpringMVC', ''); +INSERT INTO `tag` VALUES (13, 'MyBatis', ''); +INSERT INTO `tag` VALUES (14, 'JVM', ''); +INSERT INTO `tag` VALUES (15, '设计模式', ''); +INSERT INTO `tag` VALUES (16, '网络编程', ''); +INSERT INTO `tag` VALUES (17, 'IO', ''); +INSERT INTO `tag` VALUES (18, 'JSP', ''); +INSERT INTO `tag` VALUES (19, 'Servlet', ''); +INSERT INTO `tag` VALUES (20, 'JavaWeb', ''); +INSERT INTO `tag` VALUES (21, 'Hibernate', ''); +INSERT INTO `tag` VALUES (22, 'SQL', ''); +INSERT INTO `tag` VALUES (24, 'Redis', 'redis'); +INSERT INTO `tag` VALUES (25, 'SPA', ''); +INSERT INTO `tag` VALUES (26, 'SpringCloud', ''); +INSERT INTO `tag` VALUES (27, 'Dubbo', ''); +INSERT INTO `tag` VALUES (28, 'EDAS', ''); +INSERT INTO `tag` VALUES (29, 'MongoDB', ''); +INSERT INTO `tag` VALUES (31, 'SpringBoot', ''); +INSERT INTO `tag` VALUES (32, 'ElasticSearch', ''); +INSERT INTO `tag` VALUES (33, 'Docker', ''); +INSERT INTO `tag` VALUES (34, 'Jenkins', ''); +INSERT INTO `tag` VALUES (35, 'Vue', ''); +INSERT INTO `tag` VALUES (36, 'IDEA', ''); +INSERT INTO `tag` VALUES (37, 'Shiro', ''); +INSERT INTO `tag` VALUES (38, 'RocketMQ', ''); COMMIT; -- ---------------------------- --- Table structure for `user` +-- Table structure for user -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( - `user_id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `user_name` varchar(255) NOT NULL DEFAULT '', - `user_pass` varchar(255) NOT NULL DEFAULT '', - `user_nickname` varchar(255) NOT NULL DEFAULT '', - `user_email` varchar(100) DEFAULT '', - `user_url` varchar(100) DEFAULT '', - `user_avatar` varchar(255) DEFAULT NULL, - `user_last_login_ip` varchar(255) DEFAULT NULL, - `user_register_time` datetime DEFAULT NULL, - `user_last_login_time` datetime DEFAULT NULL, - `user_status` int(1) unsigned DEFAULT '1', + `user_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户ID', + `user_name` varchar(30) NOT NULL DEFAULT '' COMMENT '用户名', + `user_pass` varchar(100) NOT NULL DEFAULT '' COMMENT '密码', + `user_nickname` varchar(100) NOT NULL DEFAULT '' COMMENT '昵称', + `user_email` varchar(100) DEFAULT '' COMMENT '邮箱', + `user_url` varchar(100) DEFAULT '' COMMENT '个人主页', + `user_avatar` varchar(100) DEFAULT NULL COMMENT '头像', + `user_last_login_ip` varchar(30) DEFAULT NULL COMMENT '上传登录IP', + `user_register_time` datetime DEFAULT NULL COMMENT '注册时间', + `user_last_login_time` datetime DEFAULT NULL COMMENT '上传登录时间', + `user_status` int(1) unsigned DEFAULT '1' COMMENT '状态', + `user_role` varchar(20) NOT NULL DEFAULT 'user' COMMENT '角色', PRIMARY KEY (`user_id`), UNIQUE KEY `user_name` (`user_name`), UNIQUE KEY `user_email` (`user_email`) -) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; +) ENGINE=MyISAM AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; -- ---------------------------- --- Records of `user` +-- Records of user -- ---------------------------- BEGIN; -INSERT INTO `user` VALUES ('1', 'admin', '123456', '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '/uploads/2017/10/20171006225356181.jpg', '0:0:0:0:0:0:0:1', '2017-10-06 21:56:33', '2019-04-25 21:55:53', '1'), ('2', 'zhangsna', '123456', '张三', 'zhangsan@china.com', '', '/uploads/2018/11/avatar(1).jpg', null, '2018-11-25 14:45:08', null, '1'); +INSERT INTO `user` VALUES (1, 'admin', '123456', '言曌', 'admin@liuyanzhao.com', 'https://liuyanzhao.com', '/img/avatar/avatar1.jpg', '0:0:0:0:0:0:0:1', '2017-10-06 21:56:33', '2021-04-13 05:16:59', 1, 'admin'); +INSERT INTO `user` VALUES (5, 'wangwu', '123456', '王五', 'wangwu@qq.com', NULL, '/img/avatar/avatar.png', '0:0:0:0:0:0:0:1', '2021-02-25 09:12:33', '2021-02-25 09:13:57', 1, 'user'); +INSERT INTO `user` VALUES (2, 'zhangsan', '123456', '张三', 'zhangsan@china.com', '', '/img/avatar/avatar2.jpeg', '0:0:0:0:0:0:0:1', '2018-11-25 14:45:08', '2021-02-25 10:19:30', 1, 'user'); +INSERT INTO `user` VALUES (3, 'youke', '123456', '游客', 'youke@aa.com', '', '/img/avatar/avatar3.jpeg', '0:0:0:0:0:0:0:1', '2018-11-25 14:45:08', '2020-04-18 21:41:06', 1, 'user'); +INSERT INTO `user` VALUES (4, 'lisi', '123456', '李四', 'lisi@qq.com', '', '/img/avatar/avatar.png', NULL, '2021-02-25 09:11:42', NULL, 1, 'user'); COMMIT; SET FOREIGN_KEY_CHECKS = 1; diff --git a/uploads/2017/9/20170914201951167.png b/uploads/2017/9/20170914201951167.png deleted file mode 100644 index 55c4c474..00000000 Binary files a/uploads/2017/9/20170914201951167.png and /dev/null differ diff --git a/uploads/2017/9/20170914202218330.png b/uploads/2017/9/20170914202218330.png deleted file mode 100644 index 55c4c474..00000000 Binary files a/uploads/2017/9/20170914202218330.png and /dev/null differ diff --git a/uploads/2017/9/20170919213840682.png b/uploads/2017/9/20170919213840682.png deleted file mode 100644 index 294185b2..00000000 Binary files a/uploads/2017/9/20170919213840682.png and /dev/null differ diff --git a/uploads/2017/9/20170920210412778.png b/uploads/2017/9/20170920210412778.png deleted file mode 100644 index 294185b2..00000000 Binary files a/uploads/2017/9/20170920210412778.png and /dev/null differ diff --git a/uploads/2017/9/20170922143841757.png b/uploads/2017/9/20170922143841757.png deleted file mode 100644 index 294185b2..00000000 Binary files a/uploads/2017/9/20170922143841757.png and /dev/null differ diff --git a/uploads/2018/11/avatar(1).jpg b/uploads/2018/11/avatar(1).jpg deleted file mode 100644 index cf314e96..00000000 Binary files a/uploads/2018/11/avatar(1).jpg and /dev/null differ diff --git a/uploads/2018/11/avatar(2).jpg b/uploads/2018/11/avatar(2).jpg deleted file mode 100644 index cf314e96..00000000 Binary files a/uploads/2018/11/avatar(2).jpg and /dev/null differ diff --git a/uploads/2018/11/avatar(3).jpg b/uploads/2018/11/avatar(3).jpg deleted file mode 100644 index cf314e96..00000000 Binary files a/uploads/2018/11/avatar(3).jpg and /dev/null differ diff --git a/uploads/2018/8/avatar(1).jpg b/uploads/2018/8/avatar(1).jpg deleted file mode 100644 index cf314e96..00000000 Binary files a/uploads/2018/8/avatar(1).jpg and /dev/null differ diff --git a/uploads/2018/8/avatar(2).jpg b/uploads/2018/8/avatar(2).jpg deleted file mode 100644 index cf314e96..00000000 Binary files a/uploads/2018/8/avatar(2).jpg and /dev/null differ diff --git a/uploads/2018/8/logo(1).png b/uploads/2018/8/logo(1).png deleted file mode 100644 index 35746cdc..00000000 Binary files a/uploads/2018/8/logo(1).png and /dev/null differ