springboot之jpa支持
导入相关pom依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
application.yml文件配置
spring: jpa: hibernate: ddl-auto: update show-sql: true
自动建表相关代码
@Entity @Table(name = "t_springboot_book") public class Book { @Id @GeneratedValue private Integer bid; @Column(length = 100) private String bname; @Column private Float price;
数据库自动建表截图
会创建一个序列以及t_springboot_book表
jpa值增删改查
* 只要继承JpaRepository,通常所用的增删查改方法都有 * 第一个参数:操作的实体类 * 第二个参数:实体类对应数据表的主键 */ public interface JpaDao extends JpaRepository<Book, Integer> { }
controller层
package com.huang.springboot05.controller; import com.huang.springboot05.entity.Book; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * @auther 宇晨 * @company * @create 2019-11-30-17:15 */ @RestController @RequestMapping("/book") public class BookController { @Autowired private JpaRepository jpaDao; @RequestMapping("/add") public String add(Book book){ jpaDao.save(book); return "success"; } @RequestMapping("/edit") public String edit(Book book){ jpaDao.save(book); return "success"; } @RequestMapping("/del") public String del(Book book){ jpaDao.delete(book); return "success"; } @RequestMapping("/getOne") public Book getOne(Integer bid){ // 会出现懒加载问题:org.hibernate.LazyInitializationException: could not initialize proxy - no Session // return jpaDao.getOne(bid); return (Book)jpaDao.findById(bid).get(); } @RequestMapping("/getAll") public List<Book> getAll(){ return jpaDao.findAll(); } }
浏览器输入请求进行测试
http://localhost:8080/springboot/jpa/getOne?bid=11
http://localhost:8080/springboot/jpa/getAll
http://localhost:8080/springboot/jpa/add?bname=xxx&price=234
http://localhost:8080/springboot/jpa/add?bname=xxx&price=234&bid=11
http://localhost:8080/springboot/jpa/del?bid=11
jpa值复杂查询
dao层
* 要使用高级查询必须继承 * org.springframework.data.jpa.repository.JpaSpecificationExecutor<T>接口 */ public interface JpaDao extends JpaRepository<Book, Integer>, JpaSpecificationExecutor<Book> { }
controller层
@RequestMapping("/getCondition") public List<Book> getCondition(Book book){ return jpaDao.findAll(new Specification<Book>() { @Override public Predicate toPredicate(Root<Book> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { Predicate predicate = criteriaBuilder.conjunction(); if(book != null){ if(null != book.getBname() && !"".equals(book.getBname())){ predicate.getExpressions().add(criteriaBuilder.like(root.get("bname"),"%"+book.getBname()+"%")); } } return predicate; } }); }
Springboot+bootstrap界面版之增删改查及图片上传
本次案例采取的是spring data jpa和bootstrap3来完成的,并没有使用github提供的分页插件Pagehelper,pagehelper与SSM配合分页在前面博客已经有所讲解。
工程创建
pom依赖
<mysql.version>5.1.44</mysql.version> <version>${mysql.version}</version> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> </dependency>
application.yml文件配置
server: servlet: context-path: /springboot spring: jpa: hibernate: ddl-auto: update show-sql: true datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis_ssm?useUnicode=true&characterEncoding=utf8 username: mybatis_ssm password: xiaoli druid: initial-size: 5 min-idle: 5 max-active: 20 max-wait: 60000 time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 30000 validation-query: SELECT 1 FROM DUAL test-while-idle: true test-on-borrow: true test-on-return: false pool-prepared-statements: true max-pool-prepared-statement-per-connection-size: 20 filter: stat: merge-sql: true slow-sql-millis: 5000 web-stat-filter: enabled: true url-pattern: /* exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" session-stat-enable: true session-stat-max-count: 100 stat-view-servlet: enabled: true url-pattern: /druid/* reset-enable: true login-username: admin login-password: admin allow: 127.0.0.1 thymeleaf: cache: false # 解决图片上传大小限制问题,也可采取配置类 servlet: multipart: max-file-size: 20MB max-request-size: 60MB
Springboot05Application.java
@EnableTransactionManagement @SpringBootApplication public class Springboot05Application {
上传文件映射配置类MyWebAppConfigurer.java
/** * 资源映射路径 */ @Configuration public class MyWebAppConfigurer extends WebMvcConfigurationSupport { @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); registry.addResourceHandler("/uploadImages/**").addResourceLocations("file:E:/temp/"); super.addResourceHandlers(registry); } }
后台代码
StringUtils
package com.huang.springboot05.utils; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; public class StringUtils { // 私有的构造方法,保护此类不能在外部实例化 private StringUtils() { } /** * 如果字符串等于null或去空格后等于"",则返回true,否则返回false * * @param s * @return */ public static boolean isBlank(String s) { boolean b = false; if (null == s || s.trim().equals("")) { b = true; } return b; } /** * 如果字符串不等于null或去空格后不等于"",则返回true,否则返回false * * @param s * @return */ public static boolean isNotBlank(String s) { return !isBlank(s); } /** * set集合转string * @param hasPerms * @return */ public static String SetToString(Set hasPerms){ return Arrays.toString(hasPerms.toArray()).replaceAll(" ", "").replace("[", "").replace("]", ""); } /** * 转换成模糊查询所需参数 * @param before * @return */ public static String toLikeStr(String before){ return isBlank(before) ? null : "%"+before+"%"; } /** * 将图片的服务器访问地址转换为真实存放地址 * @param imgpath 图片访问地址(http://localhost:8080/uploadImage/2019/01/26/20190126000000.jpg) * @param serverDir uploadImage * @param realDir E:/temp/ * @return */ public static String serverPath2realPath(String imgpath, String serverDir, String realDir) { imgpath = imgpath.substring(imgpath.indexOf(serverDir)); return imgpath.replace(serverDir,realDir); } /** * 过滤掉集合里的空格 * @param list * @return */ public static List<String> filterWhite(List<String> list){ List<String> resultList=new ArrayList<String>(); for(String l:list){ if(isNotBlank(l)){ resultList.add(l); } } return resultList; } /** * 从html中提取纯文本 * @param strHtml * @return */ public static String html2Text(String strHtml) { String txtcontent = strHtml.replaceAll("</?[^>]+>", ""); //剔出<html>的标签 txtcontent = txtcontent.replaceAll("<a>\\s*|\t|\r|\n</a>", "");//去除字符串中的空格,回车,换行符,制表符 return txtcontent; } public static void main(String[] args) { } }
PageBean
package com.huang.springboot05.utils; import javax.servlet.http.HttpServletRequest; import java.util.Map; /** * 分页工具类 */ public class PageBean { private int page = 1;// 页码 private int rows = 3;// 页大小 private int total = 0;// 总记录数 private boolean pagination = true;// 是否分页 // 保存上次查询的参数 private Map<String, String[]> paramMap; // 保存上次查询的url private String url; public void setRequest(HttpServletRequest request) { String page = request.getParameter("page"); String rows = request.getParameter("offset"); String pagination = request.getParameter("pagination"); this.setPage(page); this.setRows(rows); this.setPagination(pagination); this.setUrl(request.getRequestURL().toString()); this.setParamMap(request.getParameterMap()); } public PageBean() { super(); } public Map<String, String[]> getParamMap() { return paramMap; } public void setParamMap(Map<String, String[]> paramMap) { this.paramMap = paramMap; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public int getPage() { return page; } public void setPage(int page) { this.page = page; } public void setPage(String page) { if(StringUtils.isNotBlank(page)) { this.page = Integer.parseInt(page); } } public int getRows() { return rows; } public void setRows(String rows) { if(StringUtils.isNotBlank(rows)) { this.rows = Integer.parseInt(rows); } } public int getTotal() { return total; } public void setTotal(int total) { this.total = total; } public void setTotal(String total) { if(StringUtils.isNotBlank(total)) { this.total = Integer.parseInt(total); } } public boolean isPagination() { return pagination; } public void setPagination(boolean pagination) { this.pagination = pagination; } public void setPagination(String pagination) { if(StringUtils.isNotBlank(pagination) && "false".equals(pagination)) { this.pagination = Boolean.parseBoolean(pagination); } } /** * 最大页 * @return */ public int getMaxPage() { int max = this.total/this.rows; if(this.total % this.rows !=0) { max ++ ; } return max; } /** * 下一页 * @return */ public int getNextPage() { int nextPage = this.page + 1; if(nextPage > this.getMaxPage()) { nextPage = this.getMaxPage(); } return nextPage; } /** * 上一页 * @return */ public int getPreviousPage() { int previousPage = this.page -1; if(previousPage < 1) { previousPage = 1; } return previousPage; } /** * 获得起始记录的下标 * * @return */ public int getStartIndex() { return (this.page - 1) * this.rows; } @Override public String toString() { return "PageBean [page=" + page + ", rows=" + rows + ", total=" + total + ", pagination=" + pagination + "]"; } }
PageUtil
package com.huang.springboot05.utils; import java.util.Map; import java.util.Set; /** * 基于bootstrap3生成分页代码 */ public class PageUtil { public static String createPageCode(PageBean pageBean) { StringBuffer sb = new StringBuffer(); /* * 拼接向后台提交数据的form表单 * 注意:拼接的form表单中的page参数是变化的,所以不需要保留上一次请求的值 */ sb.append("<form id='pageBeanForm' action='"+pageBean.getUrl()+"' method='post'>"); sb.append("<input type='hidden' name='page'>"); Map<String, String[]> parameterMap = pageBean.getParamMap(); if(parameterMap != null && parameterMap.size() > 0) { Set<Map.Entry<String, String[]>> entrySet = parameterMap.entrySet(); for (Map.Entry<String, String[]> entry : entrySet) { if(!"page".equals(entry.getKey())) { String[] values = entry.getValue(); for (String val : values) { sb.append("<input type='hidden' name='"+entry.getKey()+"' value='"+val+"'>"); } } } } sb.append("</form>"); if(pageBean.getTotal()==0){ return "未查询到数据"; }else{ sb.append("<li><a href='javascript:gotoPage(1)'>首页</a></li>"); if(pageBean.getPage()>1){ sb.append("<li><a href='javascript:gotoPage("+pageBean.getPreviousPage()+")'>上一页</a></li>"); }else{ sb.append("<li class='disabled'><a href='javascript:gotoPage(1)'>上一页</a></li>"); } for(int i=pageBean.getPage()-1;i<=pageBean.getPage()+1;i++){ if(i<1||i>pageBean.getMaxPage()){ continue; } if(i==pageBean.getPage()){ sb.append("<li class='active'><a href='#'>"+i+"</a></li>"); }else{ sb.append("<li><a href='javascript:gotoPage("+i+")'>"+i+"</a></li>"); } } if(pageBean.getPage()<pageBean.getMaxPage()){ sb.append("<li><a href='javascript:gotoPage("+pageBean.getNextPage()+")'>下一页</a></li>"); }else{ sb.append("<li class='disabled'><a href='javascript:gotoPage("+pageBean.getMaxPage()+")'>下一页</a></li>"); } sb.append("<li><a href='javascript:gotoPage("+pageBean.getMaxPage()+")'>尾页</a></li>"); } /* * 给分页条添加与后台交互的js代码 */ sb.append("<script type='text/javascript'>"); sb.append(" function gotoPage(page) {"); sb.append(" document.getElementById('pageBeanForm').page.value = page;"); sb.append(" document.getElementById('pageBeanForm').submit();"); sb.append(" }"); sb.append(" function skipPage() {"); sb.append(" var page = document.getElementById('skipPage').value;"); sb.append(" if(!page || isNaN(page) || parseInt(page)<1 || parseInt(page)>"+pageBean.getMaxPage()+"){"); sb.append(" alert('请输入1~N的数字');"); sb.append(" return;"); sb.append(" }"); sb.append(" gotoPage(page);"); sb.append(" }"); sb.append("</script>"); return sb.toString(); } }
实体类entity
package com.huang.springboot05.entity; import lombok.ToString; import javax.persistence.*; /** * @auther 宇晨 * @company * @create 2019-12-01-10:45 */ @Entity @Table(name = "t_springboot_teacher") @ToString public class Teacher { @Id @GeneratedValue private Integer tid; @Column(length = 16) private String tname; @Column(length = 4) private String sex; @Column(length = 100) private String description; @Column(length = 200) private String imagePath; public Integer getTid() { return tid; } public void setTid(Integer tid) { this.tid = tid; } public String getTname() { return tname; } public void setTname(String tname) { this.tname = tname; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getImagePath() { return imagePath; } public void setImagePath(String imagePath) { this.imagePath = imagePath; } }
dao层
package com.huang.springboot05.repository; import com.huang.springboot05.entity.Teacher; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; /** * @auther 宇晨 * @company * @create 2019-12-01-11:05 * * 只要继承JpaRepository,通常所用的增删查改方法都有 * 第一个参数:操作的实体类 * 第二个参数:实体类对应数据表的主键 * * 要使用高级查询必须继承 * org.springframework.data.jpa.repository.JpaSpecificationExecutor<T>接口 */ public interface TeacherDao extends JpaRepository<Teacher, Integer>, JpaSpecificationExecutor<Teacher> { }
service层
package com.huang.springboot05.service.impl; import com.huang.springboot05.entity.Teacher; import com.huang.springboot05.repository.TeacherDao; import com.huang.springboot05.service.TeacherService; import com.huang.springboot05.utils.PageBean; import com.huang.springboot05.utils.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; /** * @auther 宇晨 * @company * @create 2019-12-01-11:08 */ @Service public class TeacherServiceImpl implements TeacherService { @Autowired private TeacherDao teacherDao; @Override public Teacher save(Teacher teacher) { return teacherDao.save(teacher); } @Override public void deleteById(Integer id) { teacherDao.deleteById(id); } @Override public Teacher findById(Integer id) { return teacherDao.findById(id).get(); } @Override public Page<Teacher> listPager(Teacher teacher, PageBean pageBean) { // jpa的Pageable分页是从0页码开始 Pageable pageable = PageRequest.of(pageBean.getPage()-1, pageBean.getRows()); return teacherDao.findAll(new Specification<Teacher>() { @Override public Predicate toPredicate(Root<Teacher> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { Predicate predicate = criteriaBuilder.conjunction(); if(teacher != null){ if(StringUtils.isNotBlank(teacher.getTname())){ predicate.getExpressions().add(criteriaBuilder.like(root.get("tname"),"%"+teacher.getTname()+"%")); } } return predicate; } },pageable); } }
controler层
package com.huang.springboot05.controller; import com.huang.springboot05.entity.Teacher; import com.huang.springboot05.service.TeacherService; import com.huang.springboot05.utils.PageBean; import com.huang.springboot05.utils.PageUtil; import com.huang.springboot05.utils.StringUtils; import org.apache.commons.io.FileUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import java.io.File; import java.io.IOException; /** * @author 宇晨 * @company * @create 2019-02-20 22:15 */ @Controller @RequestMapping("/teacher") public class TeacherController { @Autowired private TeacherService teacherService; @RequestMapping("/listPager") public ModelAndView list(Teacher teacher, HttpServletRequest request){ PageBean pageBean = new PageBean(); pageBean.setRequest(request); ModelAndView modelAndView = new ModelAndView(); Page<Teacher> teachers = teacherService.listPager(teacher, pageBean); modelAndView.addObject("teachers",teachers.getContent()); pageBean.setTotal(teachers.getTotalElements()+""); modelAndView.addObject("pageCode", PageUtil.createPageCode(pageBean)/*.replaceAll("<","<").replaceAll(">:",">")*/); modelAndView.setViewName("list"); return modelAndView; } @RequestMapping("/toEdit") public ModelAndView toEdit(Teacher teacher){ ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("edit"); modelAndView.addObject("sexArr",new String[]{"男","女"}); if(!(teacher.getTid() == null || "".equals(teacher.getTid()))) { Teacher t = teacherService.findById(teacher.getTid()); modelAndView.addObject("teacher", t); } return modelAndView; } @RequestMapping("/add") public String add(Teacher teacher, MultipartFile image){ try { String diskPath = "E://temp/"+image.getOriginalFilename(); String serverPath = "http://localhost:8080/springboot/uploadImages/"+image.getOriginalFilename(); if(StringUtils.isNotBlank(image.getOriginalFilename())){ FileUtils.copyInputStreamToFile(image.getInputStream(),new File(diskPath)); teacher.setImagePath(serverPath); } teacherService.save(teacher); } catch (IOException e) { e.printStackTrace(); } return "redirect:/teacher/listPager"; } @RequestMapping("/edit") public String edit(Teacher teacher, MultipartFile image){ String diskPath = "E://temp/"+image.getOriginalFilename(); String serverPath = "http://localhost:8080/springboot/uploadImages/"+image.getOriginalFilename(); if(StringUtils.isNotBlank(image.getOriginalFilename())){ try { FileUtils.copyInputStreamToFile(image.getInputStream(),new File(diskPath)); teacher.setImagePath(serverPath); } catch (IOException e) { e.printStackTrace(); } } teacherService.save(teacher); return "redirect:/teacher/listPager"; } @RequestMapping("/del/{bid}") public String del(@PathVariable(value = "bid") Integer bid){ teacherService.deleteById(bid); return "redirect:/teacher/listPager"; } }
页面层
list.html
<!DOCTYPE html> <html lang="en"> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>书籍列表</title> <script type="text/javascript" th:src="@{/static/js/xxx.js}"></script> <link rel="stylesheet" th:href="@{/static/js/bootstrap3/css/bootstrap.min.css}"> <link rel="stylesheet" th:href="@{/static/js/bootstrap3/css/bootstrap-theme.min.css}"> <script type="text/javascript" th:src="@{/static/js/bootstrap3/js/jquery-1.11.2.min.js}"></script> <script type="text/javascript" th:src="@{/static/js/bootstrap3/js/bootstrap.min.js}"></script> </head> <body> <form th:action="@{/teacher/listPager}" method="post"> 书籍名称: <input type="text" name="tname" /> <input type="submit" value="提交"/> </form> <a th:href="@{/teacher/toEdit}">新增</a> <table border="1px" width="600px"> <thead> <tr> <td>ID</td> <td>头像</td> <td>姓名</td> <td>性别</td> <td>简介</td> <td>操作</td> </tr> </thead> <tbody> <tr th:each="teacher : ${teachers}"> <td th:text="${teacher.tid}"></td> <td><img style="width: 60px;height: 60px;" id="imgshow" th:src="${teacher.imagePath}" th:alt="${teacher.tname}"/></td> <!--<td th:text="${teacher.imagePath}"></td>--> <td th:text="${teacher.tname}"></td> <td th:text="${teacher.sex}"></td> <td th:text="${teacher.description}"></td> <td> <a th:href="@{'/teacher/del/'+${teacher.tid}}">删除</a> <a th:href="@{'/teacher/toEdit?tid='+${teacher.tid}}">修改</a> </td> </tr> </tbody> </table> <nav> <ul class="pagination pagination-sm" th:utext="${pageCode}"> </ul> <!--<ul class="pagination pagination-sm">--> <!--<form id='pageBeanForm' action='http://localhost:8080/springboot/teacher/listPager' method='post'><input type='hidden' name='page'></form>--> <!--<li><a href='javascript:gotoPage(1)'>首页</a></li>--> <!--<li class='disabled'><a href='javascript:gotoPage(1)'>上一页</a></li>--> <!--<li class='active'><a href='#'>1</a></li>--> <!--<li><a href='javascript:gotoPage(2)'>2</a></li>--> <!--<li><a href='javascript:gotoPage(2)'>下一页</a></li>--> <!--<li><a href='javascript:gotoPage(4)'>尾页</a></li>--> <!--<script type='text/javascript'> function gotoPage(page) { document.getElementById('pageBeanForm').page.value = page; document.getElementById('pageBeanForm').submit(); } function skipPage() { var page = document.getElementById('skipPage').value; if(!page || isNaN(page) || parseInt(page)<1 || parseInt(page)>4){ alert('请输入1~N的数字'); return; } gotoPage(page); }</script>--> <!--</ul>--> </nav> </body> </html>
edit.html
<!DOCTYPE html> <html lang="en"> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>用户编辑界面</title> <script type="text/javascript"> function preShow() { } </script> </head> <body> <form th:action="@{${teacher.tid} ? '/teacher/edit' : '/teacher/add'}" method="post" enctype="multipart/form-data"> <input type="hidden" name="tid" th:value="${teacher.tid}" /> <input type="hidden" name="imagePath" th:value="${teacher.imagePath}" /> <img id="imgshow" src="" alt=""/> <input type="file" name="image" onchange="preShow();"></br> 教员名称: <input type="text" name="tname" th:value="${teacher.tname}" /></br> 教员描述: <input type="text" name="description" th:value="${teacher.description}" /></br> 单选回显 <input type="radio" name="sex" th:each ="s:${sexArr}" th:value="${s}" th:text ="${s}" th:attr ="checked=${s == teacher.sex}"> <input type="submit" value="提交"/> </form> </body> </html>
thymeleaf单选回显,多选回显,下拉选回显,默认选中第一个
//默认选中第一个 <input type ="radio" name="repaymentType" th:each ="repaymentType,repaymentState:${repaymentTypeList}" th:value="${repaymentType.dictName}" th:text ="${repaymentType.dictName}" th:attr ="checked=${repaymentState.index==0?true:false}"> //单选回显 <input type="radio" name="repaymentType" th:each ="repaymentType:${repaymentTypeList}" th:value="${repaymentType.dictName}" th:text ="${repaymentType.dictName}" th:attr ="checked=${financeProductDO.repaymentType == repaymentType.dictName?true:false}"> //多选回显 <input type ="checkbox" name="companyRole" th:each ="role : ${companyRoleList}" th:value="${role.dictName}" th:text ="${role.dictName}" th:attr ="checked=${companyInformation.companyRole.contains(role.dictName)?true:false}"> //下拉选回显默认选中 前台接收到参数:user对象和businessList集合 <option th:selected="${user.businessId eq busi.businessId}" th:each="busi:${businessList}" th:value="${busi.businessId}" th:text="${busi.businessName}" > </option>