很多年没有从事编程,之前的经验也在C#,从头开始学习Java也是万般无奈。好在经过一个月的学习也算是稍有收获,希望经过这次历程能让自己浴火重生。

一、概述

在网上看了一段时间的SpringBoot学习视频,感觉不如自己脱离教学独立写一遍CRUD应该会有更多收获,有困难就百度,虽然会慢一些但是效果更好一些。经过一周多的折腾,终于把增删改查写了一遍,页面和样式是网上找的模板。涉及的主要技术点有

  • SpringBoot
  • Mybatis
  • Thymeleaf
  • Ajax
  • Bootstrap模态框(蒙版)
  • Bootstrap验证插件
  • Jquery的分页插件

以前开发经验主要在后端,前端是依样画葫芦,遇到样式完全就是两眼一抹黑,前端自己写的代码简直没法看,暂时没打算整理,后续有机会再说。
(1)登录

(2)列表

(3)新增/修改

(4)验证插件

(5)删除

二、服务端

1、初始化

(1)idea初始化springboot项目,选择web、mybatis、thymeleaf、mysql,pom基本不需要去修改就可以生成;

(2)生成表结构

SET FOREIGN_KEY_CHECKS=0;-- ----------------------------
-- Table structure for department
-- ----------------------------
DROP TABLE IF EXISTS `department`;
CREATE TABLE `department` (`id` int(11) NOT NULL AUTO_INCREMENT,`departmentName` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;-- ----------------------------
-- Table structure for employee
-- ----------------------------
DROP TABLE IF EXISTS `employee`;
CREATE TABLE `employee` (`id` int(11) NOT NULL AUTO_INCREMENT,`lastName` varchar(255) DEFAULT NULL,`email` varchar(255) DEFAULT NULL,`gender` int(2) DEFAULT NULL,`d_id` int(11) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(50) NOT NULL,`login` varchar(50) NOT NULL,`password` varchar(50) NOT NULL,`tel` varchar(50) DEFAULT NULL,`status` tinyint(6) unsigned zerofill DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8;

(3)mybatis生成器

生成对应的xml、mapper接口和entity,代码生成器插件

  • 在idea插件系统里安装 | Using IDE built-in plugin system on Windows:

    • File > Settings > Plugins > Browse repositories… > Search for “better-mybatis-generator” > Install Plugin
  • 手动zip安装 | Manually:
    • Download the latest release and install it manually using Preferences > Plugins > Install plugin from disk…

mybatis-generator会生成每个表的criterion查询,对应一个example的类。通过这个类可以构造任意条件的查询。

UserExample example = new UserExample();
UserExample.Criteria criteria = example.createCriteria();example.setOrderByClause("age asc");//升序
example.setDistinct(false);//不去重if(!StringUtils.isNotBlank(user.getName())){Criteria.andNameEqualTo(user.getName());
}if(!StringUtils.isNotBlank(user.getSex())){Criteria.andSexEqualTo(user.getSex());
}List<User> userList=userMapper.selectByExample(example);
//等同:select * from user where name={#user.name} and sex={#user.sex} order by age asc;

按名字模糊查询

      if(!StringUtils.isNotBlank(user.getName())){criteria.andNameLIke(‘%’+name+’%’);}List<User>  userList=userMapper.selectByExample(example);

2、配置

(1)mapper.xml,命名空间检查

如果是自动生成的xml,一般情况下是没有问题,检查以下4点;

另外,就是方法名要和mapper里的接口名称要一模一样,包括大小写

(2)properties文件

数据库连接,还有xml所在路径(mybatis.mapper-locations)

#tomcat
server.port=8080
server.tomcat.uri-encoding=utf-8#http
spring.http.encoding.force=true
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true#datasource
spring.datasource.tomcat.driver-class-name = com.mysql.jdbc.Driver#mybatis
spring.datasource.url=jdbc:mysql://192.168.73.128:3306/mydb?useUnicode=true&characterEncoding=UTF-8&transformedBitIsBoolean=true&autoReconnect=true&failOverReadOnly=false&allowMultiQueries=true&useSSL=false
spring.datasource.username=dyhu
spring.datasource.password=123456
spring.datasource.tomcat.default-auto-commit=truemybatis.typeAliasesPackage=com.demo.crud.dao
mybatis.mapper-locations=classpath:mapping/*.xml

(3)Application入口,要加上@MappScan

@MapperScan("com.demo.crud.dao")
@SpringBootApplication
public class CrudApplication {public static void main(String[] args) {SpringApplication.run(CrudApplication.class, args);}
}

(4)注入mybatis的dao时idea报错的问题

一种是直接修改setting +++>Editor+++>Inspections+++>Spring+++>SpringCore+++>Code+++>Autowiring For Bean Class

把报警去掉。不过不建议这么做,如果万一真的代码写错,一直到运行时才报错就麻烦了。

另一种解决方案:

如果是mapping的接口在注入时,加上required=false即可

    @Autowired(required = false)private UserDao userDao;

(5)测试类注解

头部需要加入注解,否则有可能会导致空指针错误,无法注入mapper接口

@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {@AutowiredUserService service;@Testpublic void getUsersByPages() {List<User> list = service.getUsersByPages(1, 2);String result = list.toString();System.out.println(result);}
}

(6)关于@Service注解

mybatis中的mapper接口,没法做实例化,只能通过@Autowired进行注入到spring容器,前提是该类用@Service注解

另外,如果有实例对象中的方法用到了其他对象,该对象也同事用到了mybatis的mapper接口,该对象也需要用@Autowired注入,用new方法,会导致空指针。

比如

@Service
public class EmployeeService {@Autowired(required = false)EmployeeDao employeeDao;@AutowiredEmployeeBiz employeeBiz;//部门列表查询public PageInfo<EmployeeBO> selectByPage(int pageNum, int pageSize) {//Example、Dao,包括selectByExample方法都是代码生成器生成EmployeeExample example = new EmployeeExample();PageHelper.startPage(pageNum, pageSize);//selectByExample(example),因为没有指定任何查询条件,正常是返回所有数据//在这里指定PageHelper拦截了sql,加上了limit参数List<Employee> emps = employeeDao.selectByExample(example);//需要更新部门,!注意这个employeeBiz对象,里面也用到了mapper接口List<EmployeeBO> empBOs = employeeBiz.getEmpBOsByPO(emps);//如果直接返回以上list,得到了分页的数据// 如果添加下面步骤,则返回pageInfo,则能得到包括list在内的分页信息PageInfo<EmployeeBO> pageInfo = new PageInfo<>(empBOs);return pageInfo;}

employeeBiz.getEmpBOsByPO

@Service
public class EmployeeBiz {@AutowiredDepartmentBiz departmentBiz;public List<EmployeeBO> getEmpBOsByPO(List<Employee> emps) {if (emps == null || emps.size() == 0) return null;List<Integer> ids = new ArrayList<>();for (Employee emp : emps) {ids.add(emp.getdId());}Map<Integer, Department> deptlist = departmentBiz.getDeptlistByIds(ids);

而这个departmentBiz.getDeptlistByIds(ids)就用到了mapper的接口

    public Map<Integer, Department> getDeptlistByIds(List<Integer> ids) {DepartmentExample example = new DepartmentExample();if (ids.size() == 0)  return null;example.createCriteria().andIdIn(ids);List<Department> departments = deptDao.selectByExample(example);Map<Integer, Department> depts = new HashMap<>();for (Department dept: departments) {depts.put(dept.getId(), dept);}return depts;}

@Service的说明

主要是针对@Autowired,注入的是接口类,不需要再new实现类

把DAO实现类注入到action的service接口(注意不要是service的实现类)中,注入时不要new 这个注入的类,因为spring会自动注入,如果手动再new的话会出现错误,然后属性加上@Autowired后不需要getter()和setter()方法,Spring也会自动注入。

在接口前面标上==@Autowired注释使得接口可以被容器注入==,如:

@Autowired
@Qualifier("cn")
private User user;

当接口存在两个实现类的时候必须使用@Qualifier指定注入哪个实现类,否则可以省略,只写@Autowired。

3、PageHelper

(1)引入依赖

        <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.2.7</version></dependency>

(2)控制台打印SQL

properies文件加上:

# 将mapper接口所在包的日志级别改成debug,可以在控制台打印sql
logging.level.com.demo.crud.dao: debug

(3)代码测试

    @AutowiredUserDao userDao;@GetMapping("/page/{pageNum}/{pageSize}")public PageInfo<User> selectByPage(@PathVariable int pageNum, @PathVariable int pageSize) {//Example、Dao,包括selectByExample方法都是代码生成器生成UserExample example = new UserExample();PageHelper.startPage(pageNum, pageSize);//selectByExample(example),因为没有指定任何查询条件,正常是返回所有数据//在这里指定PageHelper拦截了sql,加上了limit参数List<User> list = userDao.selectByExample(example);//如果直接返回以上list,得到了分页的数据// 如果添加下面步骤,则返回pageInfo,则能得到包括list在内的分页信息PageInfo<User> pageInfo = new PageInfo<>(list);return pageInfo;}

(4)返回结果

返回的PageInfo

{"total": 6,"list": [{"id": 6,"lastname": "鬼脚七","email": "gu@gu.com","gender": 1,"dId": 4},{"id": 5,"lastname": "李娜","email": "ln@ab.cn","gender": 0,"dId": 2},{"id": 4,"lastname": "赵柳","email": "zl@tp.com","gender": 1,"dId": 3},{"id": 3,"lastname": "王五","email": "ww@de.com","gender": 0,"dId": 2},{"id": 2,"lastname": "李四","email": "ls@de.com","gender": 1,"dId": 1},{"id": 1,"lastname": "张三","email": "zs@de.com","gender": 1,"dId": 1}],"pageNum": 1,"pageSize": 20,"size": 6,"startRow": 1,"endRow": 6,"pages": 1,"prePage": 0,"nextPage": 0,"isFirstPage": true,"isLastPage": true,"hasPreviousPage": false,"hasNextPage": false,"navigatePages": 8,"navigatepageNums": [1],"navigateFirstPage": 1,"navigateLastPage": 1
}

这些返回信息对应前端意义还是很大的

还有Debug打印出的SQL信息

4、系统分层

(1)controller

① 前端或者restful api入口

②直接指向模板页(也可以在config中,定义controller拦截器,直接定义模板入口);

③参数处理和非空判断;

④权限判断/or拦截器处理;

⑤按照前端要求返回数据处理(比如把service传过来的bo对象,转换成vo);

(2)service

系统简单的话,可以和biz合并。实质上可以进行细分

只对controller开放的方法

包括数据组装,获取业务对象(bo),分页处理

查询条件的组装

可以直接访问dal层

(3)domain

具体业务逻辑的实现,业务规则细化,简单的系统完全可以在service层面实现。

简单的crud无需存在。

可以细分

  • bo包,专门存放bo对象
  • biz包,存放具体业务逻辑的实现

(4)dal

又可以细分为

  • dao,在mybatis中,存放mapper接口,mybatis可以自动生成;
  • entity,又可以成为po,和数据表一对一存在,mybatis可以自动生成;
  • criterion,查询条件对象,mybatis可以自动生成

5、Thymeleaf

(1)命名空间

html需要导入thymeleaf命名空间,支持thymeleaf语法

<html lang="en" xmlns:th="http://www.thymeleaf.org">

(2)thymeleaf语法

@Controller
public class HelloController {@ResponseBody@RequestMapping("/hello")public String hello() {return "hello world!";}
/*    @RequestMapping("/success")public Map success(Map<String, Object> map) {map.put("hello", "haha .....!");return map;}*/@RequestMapping("/success")public String success(Map<String,Object> map){map.put("hello","<h1>------你好------------</h1>");//map.put("users", Arrays.asList("zhangsan","lisi","wangwu"));return "success";}
}
<!DOCTYPE html>
<html lang="en"  xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>success</title>
</head>
<body><h1>成功!</h1><!--th:text 将div里面的文本内容设置为 --><div th:text="${hello}">这是显示欢迎信息</div><div th:utext="${hello}">这是显示欢迎信息</div>
</body>
</html>

(3)Controller和html

controller中方法返回值要和thymeleaf的html对应

    @PostMapping("/login/check")public String doLogin(@RequestParam("userName") String userName,@RequestParam("password") String password,Map<String, Object> result) {if (!StringUtils.isEmpty(userName) && !StringUtils.isEmpty(password)) {User user = new User();user.setLogin(userName);user.setPassword(password);if (userService.doLogin(user)) {return "redirect:/users/1";}}result.put("errmsg", "用户名或者密码错误");return "login";}

其中return “login”,在resouses/templates下有一个login.html对应

6、视图解析器

目的是自定义解析html入口,如果入口仅仅是一个简单路由或者一个转发,不需要在controller定义方法

@Configuration
public class WebMVCAutoConfigure implements WebMvcConfigurer {//必要!@Beanpublic InternalResourceViewResolver viewResolver() {InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();// 设置thymeleaf的解析器viewResolver.setPrefix("classpath:/templates/");viewResolver.setSuffix(".html");return viewResolver;}//必要!定义statics可以扫描的路径,否则系统无法访问该目录404@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/statics/**").addResourceLocations("classpath:/statics/");}@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("login");registry.addViewController("index.html").setViewName("login");registry.addViewController("index").setViewName("login");registry.addViewController("login").setViewName("login");registry.addViewController("myvalid").setViewName("myvalid");registry.addViewController("validator").setViewName("validator");}}

三、前端

1、Bootstrap准备

pom引入

     <!--引入bootstrap--><dependency><groupId>org.webjars</groupId><artifactId>bootstrap</artifactId><version>4.0.0</version></dependency>

css样式引入

<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

js引入

<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdn.bootcss.com/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://cdn.bootcss.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>

2、Html中的thymeleaf

说实话,实际项目中的应用到thymeleaf场景不多,即使有也只是html初始渲染会用到后端提供的数据,毕竟现在大部分都是异步无刷新查询,还有大量的前端框架可以使用。个人觉得只要了解一些基本语法和使用方法就可以了。

我在这个demo用到的有

  • th属性

    • th:text,设置当前html元素的文本;
    • th:value,设置当前html元素的value值,类似还有th:src,th:href;
    • th:each,根据集合循环,用于生成列表、option等元素;
    • th:fragment,定义代码块,常用于公共html(比如页面头部、尾部、通用菜单等)的引入;
    • th:if,条件判断,常用在判断是否显示当前元素
  • 实例

    • 引入js和css

          <script src="js/jquery.min.js" th:src="@{/statics/js/jquery.min.js}"></script><script src="js/bootstrap.min.js" th:src="@{/statics/js/bootstrap.min.js}"></script>
      

      @{},用于th:src或者th:href,替换链接,会自动加上context

    • 引入公共页面

          <!--引入左侧菜单--><div th:replace="common/leftmenu::leftmenu"></div>
      

      common/leftmenu,是指公共页面所在目录和名称

      ::leftmenu,双冒号后面就是要引入的代码块

      <!-- 左侧菜单栏目块 -->
      <div class="leftMeun" id="leftMeun" th:fragment="leftmenu"><div id="logoDiv"><p id="logoP"><img id="logo" alt="CRUD-Test" src="data:images/logo.png" th:src="@{/statics/images/logo.png}"><span>CRUD-Test-Demo</span></p></div><div id="personInfor"><p><p id="userName"></p><p></p><p><a>退出登录</a></p></p></div><div class="meun-title">账号管理</div><div class="meun-item  meun-item-active" href="#user" aria-controls="user" role="tab" data-toggle="tab"><img src="data:images/icon_user_grey.png" th:src="@{/statics/images/icon_user_grey.png}">用户管理</div><div class="meun-item" href="#chan" aria-controls="chan" role="tab" data-toggle="tab"><img src="data:images/icon_change_grey.png" th:src="@{/statics/images/icon_change_grey.png}">修改密码</div>
      </div>
    • 循环

                  <div class="tablebody" th:each="user:${users.list}"><div class="row"><div class="col-lg-3 col-md-3 col-sm-3 col-xs-3 " th:text="${user.login}"></div><div class="col-lg-3 col-md-3 col-sm-3 col-xs-3" th:text="${user.name}">李豆豆</div><div class="col-lg-2 col-md-2 col-sm-2 col-xs-2" th:text="${user.tel}">13688889999</div><div class="col-lg-2 col-md-2 col-sm-2 col-xs-2" th:text="${user.status} == 1? '启用':'禁用'">状态</div><div class="col-lg-2 col-md-2 col-sm-2 col-xs-2"><button class="btn btn-success btn-xs" data-toggle="modal" data-target="#reviseUser" th:attr="userid=${user.id},currpage=${users.pageNum}">修改</button><!--delete url: /user/{id}/{currentpage} --><button class="btn btn-danger btn-xs" data-toggle="modal" data-target="#deleteUser" th:attr="del_url=@{/user/}+${user.id}+'/page/'+${users.pageNum}">删除</button></div></div></div>
      

      th:each=“user:${users.list}”,这里会根据后端提供的变量users.list进行循环,有多少行就会显示多少个div标签

      并把每个循环元素赋值一个行变量user,通过th:text="${user.属性名}",显示到对应的html标签

    • 条件判断

          <!--th:if 条件判断,类似的有th:switch,th:case,优先级仅次于th:each, 其中#strings是变量表达式的内置方法--><p th:text="${thIf}" th:if="${not #strings.isEmpty(thIf)}"></p>
      

      根据thymeleaf提供的内部函数strings来判断thIf是否为空,如果为空则当前p标签不展示,否则就显示thIf内容

3、模态框

(1)样式和调用

样式都是固定的写法,抄着写就行了,定义一个要弹出的div

        <!--弹出删除用户警告窗口--><div class="modal fade" id="deleteUser" role="dialog" aria-labelledby="gridSystemModalLabel"><div class="modal-dialog" role="document"><div class="modal-content"><div class="modal-header"><button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button><h4 class="modal-title" id="gridSystemModalLabe">提示</h4><input type="hidden" id="del_url"/></div><div class="modal-body"><div class="container-fluid">确定要删除该用户?删除后不可恢复!</div></div><div class="modal-footer"><button type="button" class="btn btn-xs btn-white" data-dismiss="modal">取 消</button><button type="button" class="btn  btn-xs btn-danger" onclick="deleteUser()">确 定</button></div></div><!-- /.modal-content --></div><!-- /.modal-dialog --></div><!-- /.modal -->

调用方,button的目标写上div的id即可

 <button class="btn btn-danger btn-xs" data-toggle="modal" data-target="#deleteUser" th:attr="del_url=@{/user/}+${user.id}+'/page/'+${users.pageNum}">删除</button>

(2) 显示和关闭模态框的事件

    $('#deleteUser').on('show.bs.modal', function (event) {//打开时的事件,比如需要做初始化});$("#deleteUser").on('hidden.bs.modal',function(e) {//关闭时的事件});

4、分页插件

(1)引入控件js

<script src="js/jqPaginator.min.js" th:src="@{/statics/js/jqPaginator.min.js}"></script>

(2)定义隐藏控件

        <!--页码块--><div><ul class="pagination  media-left pull-right" id="pagination"></ul><input type="hidden" id="PageCount" th:value="${users.pages}" /><input type="hidden" id="PageSize"  value=20 th:value="${users.pageSize}" /><input type="hidden" id="countindex"  value="10"/><input type="hidden" id="currentPage"  value=1 th:value="${users.pageNum}" /><!--设置最多显示的页码数 可以手动设置 默认为--><input type="hidden" id="visiblePages"  value="10" /></div>

(3)显示js

function loadpage() {var myPageCount = parseInt($("#PageCount").val());var myPageSize = parseInt($("#PageSize").val());var countindex = myPageCount % myPageSize > 0 ? (myPageCount / myPageSize) + 1 : (myPageCount / myPageSize);$("#countindex").val(countindex);$.jqPaginator('#pagination', {totalPages: parseInt($("#countindex").val()),visiblePages: parseInt($("#visiblePages").val()),currentPage: parseInt($("#currentPage").val()),first: '<li class="first"><a href="javascript:;">首页</a></li>',prev: '<li class="prev"><a href="javascript:;"><i class="arrow arrow2"></i>上一页</a></li>',next: '<li class="next"><a href="javascript:;">下一页<i class="arrow arrow3"></i></a></li>',last: '<li class="last"><a href="javascript:;">末页</a></li>',page: '<li class=""><a href="javascript:;">{{page}}</a></li>',onPageChange: function (num, type) {if (type == "change") {exeData(num, type);}}});
}
function loadData(num) {//非异步加载数据,不需要传入页号num$("#PageCount").val([[${users.total}]]);loadpage();
}
function exeData(num, type) {// loadData(num);// loadpage();//var context = [[@{/users/}]];location.href = "users/" + num; // [[${users.pageNum}]];
}
$(function () {//异步刷新,页面重刷传入第一页loadData(1);//loadpage();
});

5、前端验证插件

(1)插件js和css

    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jquery.bootstrapvalidator/0.5.2/css/bootstrapValidator.min.css"/><script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery.bootstrapvalidator/0.5.2/js/bootstrapValidator.min.js"></script>

(2)提交form

把需要验证的数据都放在一个form表单中

(3)验证规则

这里只是做简单验证,验证规则还可以添加

初始化验证器,验证器中的字段(field)要和form表单中的input对应:

   function initValid() {$('#updateForm').bootstrapValidator({message: '这个值没有被验证',feedbackIcons: {valid: 'glyphicon glyphicon-ok',invalid: 'glyphicon glyphicon-remove',validating: 'glyphicon glyphicon-refresh'},fields: { /*验证:规则*/login: { //验证input项:验证规则message: 'The username is not valid',validators: {notEmpty: {message: '用户名不能为空'}}},name: {message: 'name无效',validators: {notEmpty: {message: '用户名不能为空'}}},password: {message: '密码无效',validators: {notEmpty: {message: '密码不能为空'},stringLength: {min: 4,message: '密码长度须大于等于4位'}}}}});}

销毁验证器:

    function destroyValid() {var validator = $("#updateForm").data('bootstrapValidator');if (validator != undefined) {$("#updateForm").data('bootstrapValidator').destroy();$('#updateForm').data('bootstrapValidator', null);}}

我这里在模态框打开时初始化验证器,关闭模态框时销毁验证器

    $('#reviseUser').on('show.bs.modal', function (event) {var button = $(event.relatedTarget) // Button that triggered the modalvar userId = button.attr('userid');$("input:hidden[id='currpage']").val(button.attr('currpage'));$("input:hidden[id='id']").val(userId);initUser(userId, $(this));//初始化验证器initValid();});$("#reviseUser").on('hidden.bs.modal',function(e) {//关闭modal时,移除上次的校验配置destroyValid();});

Springboot+mybatis完整实现CRUD相关推荐

  1. Mybatis完整功能实现CRUD

    Mybatis完整功能实现CRUD Mybatis完整功能实现CRUD 对于刚使用的新手提供了完整的增删改查并附带测试,首先我们介绍下ORM 对象关系映射的框架 (3)JDBC有优缺点 (4)JPA的 ...

  2. javaweb 图书管理系统完整代码_Thymeleaf+SpringBoot+Mybatis实现的家庭财务管理系统

    项目简介 项目来源于: https:// gitee.com/darlingzhangs h/graduation_project 本系统是基于Thymeleaf+SpringBoot+Mybatis ...

  3. springboot+mybatis+SpringSecurity 实现用户角色数据库管理(一)

    本文使用springboot+mybatis+SpringSecurity 实现用户权限数据库管理 实现用户和角色用数据库存储,而资源(url)和权限的对应采用硬编码配置. 也就是角色可以访问的权限通 ...

  4. 47K Star 的SpringBoot+MyBatis+docker电商项目,附带超详细的文档!

    点击上方 好好学java ,选择 星标 公众号重磅资讯,干货,第一时间送达 今日推荐:推荐19个github超牛逼项目!个人原创100W +访问量博客:点击前往,查看更多 简介 该项目是一套电商系统, ...

  5. 单手撸了个springboot+mybatis+druid

    本文旨在用最通俗的语言讲述最枯燥的基本知识 最近身边的程序员掀起了学习springboot的热潮,说什么学会了springboot在大街上就可以横着走.什么有了springboot妈妈再也不担心我的编 ...

  6. springboot + mybatis + gradle项目构建过程

    1.从Spring boot官网根据需求下载脚手架或者到GitHub上去搜索对应的脚手架项目,D_iao ^0^ • 文件目录如下(此处generatorConfig.xml 和 log4j2.xml ...

  7. SpringBoot+MyBatis搭建迷你小程序

    简介:用Spring Boot框架大大简化了新Spring应用的初始搭建以及开发过程,在开发人员中越来越受到欢迎.微信小程序作为目前炙手可热的应用,很有可能在未来占据轻应用的市场.本门课程的主要目的是 ...

  8. mall整合SpringBoot+MyBatis搭建基本骨架

    本文主要讲解mall整合SpringBoot+MyBatis搭建基本骨架,以商品品牌为例实现基本的CRUD操作及通过PageHelper实现分页查询. mysql数据库环境搭建 下载并安装mysql5 ...

  9. 单手撸了个springboot+mybatis+druid 1

    本文旨在用最通俗的语言讲述最枯燥的基本知识 最近身边的程序员掀起了学习springboot的热潮,说什么学会了springboot在大街上就可以横着走.什么有了springboot妈妈再也不担心我的编 ...

最新文章

  1. 九、将cs文件快速的转换成可执行文件和响应文件(配置编译开关的文件)
  2. 关于学习Python的一点学习总结(3->标识符->if->模块->字符)
  3. 专家手把手教你写出高水平个人简历
  4. EDM营销的三个小窍门-EDM营销必看
  5. 正在直播 | 美女小编带你看2019世界人工智能大会
  6. skywalking 安装_SkyWalking全链路追踪利器
  7. 没有基础的想转行学习Python怎么学
  8. 不均衡数据集采样1——SMOTE算法(过采样)
  9. 12月数据库榜单,整体排名稳定如昨,Oracle 分数接连下降
  10. (Deep learning)深度卷积网络实战——第一部分
  11. 第一百天 how can i 坚持
  12. ORACLE11g R2数据库安装(一)
  13. 数据结构笔记(参考王道考研系列)
  14. 鸡尾酒问题,最小二乘法和范数的思考
  15. Python实现一个简单的socket服务器
  16. 【福利继续无套路】整理的一系列IT视频
  17. 什么是LoRaWAN
  18. C语言探索之旅 | 第一部分第三课:你的第一个程序
  19. disable_preempt
  20. 【知识点】Python 的np.prod函数详解

热门文章

  1. p语言是python吗-Python是什么?简单了解pythonp-入门
  2. 三种方法求最大公约数及求n个数的最小公倍数
  3. 泰山OFFICE技术讲座:中英文间隔,间隔以哪个字体为准?
  4. TIA WinCC Professional入门经典(2) 创建画面与联合仿真
  5. 项目管理工具—思维导图
  6. Win11下Clion+MSYS2(MinGW64)配置C++编译环境
  7. 深度学习目前主要有哪些研究方向?
  8. linux服务器下搭建svn服务器仓库
  9. 神经网络的三种训练方法,如何训练一个神经网络
  10. matlab中啥叫字符串,在matlab中( )用于括住字符串.