ssm-crud:SpringMVC+Spring+MyBatis
**功能**:对学生数据进行CRUD管理,分页,
数据校验(jQuery前端校验+Ajax校验+JSR303后端校验),
ajax数据传输,Rest风格的URI。
**技术点**:前端使用bootstrap构建 ,使用Ajiax传输数据;
后端使用SpringMVC + Spring + Mybatis
使用Maven进行项目管理 方便所需jar包的管理
分页:PageHelper
逆向工程:MyBatis Generator 方便实体类和mapper的创建
项目流程:
前端使用bootstrap,页面直接发送Ajax请求,由SpringMVC前端控制器进行拦截,判断需要进入哪个控制等的哪个方法体,然后控制层调用业务层的方法,业务层再调用Dao层,Dao通过Mybatis的mapper接口执行需要的sql对数据库进行操作。操作结束后返回JSON字符串,然后由js进行解析,使其在前端页面显示。
SpringMVC的Controller,Service,Dao由Springioc容器管理。通过@AutoWied进行自动注入。
Mybatis的mapper在项目启动时,mapper的实现类被扫描到ioc容器中。
Mybatis可以使用逆向工程Generator 生成Mapper接口,mapper文件,复杂的查询。
@RequestBody注解可以接收json格式的数据,并将其转换成对应的数据类型,在pom.xml中导入jackson-databind的依赖即可,如果未使用,会报500,类转换器找不到异常。
用户输入数据,并进行校验:1.JQuery前端校验 2ajax用户名重复校验,重要数据(后端校验(JSR303),唯一约束)。
错误集合(仅个人):
指定SpringMVC配置文件的两种方法:
1.在与web.xml同级的WEB-INF文件夹下创建一个+“-servlet”命名的XML文件,例如dispatcherServlet-servlet.xml
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
2.指定SpringMVC配置文件的位置,使用标签,并在pom.xml文件中配置扫描当前项目所有的XML文件。
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- 指定SpringMVC配置文件 -->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<!--load-on-startup是启动顺序,让这个Servlet随Servlet容器一起启动。-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--扫描所有的xml文件-->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
AOP 织入切入点(txPoint)与事务增强(txAdvice)错误:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘departmentService’ defined in file [E:\IDEAworkplace\ssmcrud\out\artifacts\ssmcrud_war_exploded\WEB-INF\classes\com\atguigu\service\DepartmentService.class]: BeanPostProcessor before instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0’: Cannot resolve reference to bean ‘txPoint’ while setting bean property ‘pointcut’; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘txPoint’: Failed to introspect bean class [org.springframework.aop.aspectj.AspectJExpressionPointcut] for lookup method metadata: could not find class that it depends on; nested exception is java.lang.NoClassDefFoundError: org/aspectj/weaver/reflect/ReflectionWorld$ReflectionWorldException
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0’: Cannot resolve reference to bean ‘txPoint’ while setting bean property ‘pointcut’; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘txPoint’: Failed to introspect bean class [org.springframework.aop.aspectj.AspectJExpressionPointcut] for lookup method metadata: could not find class that it depends on; nested exception is java.lang.NoClassDefFoundError: org/aspectj/weaver/reflect/ReflectionWorld$ReflectionWorldException
该错误是由于缺少aspectjweaver依赖导致,引入即可。
<!--织入 缺少会报错-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
分页查询错误:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘departmentService’: Unsatisfied dependency expressed through field ‘departmentMapper’; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘departmentMapper’ defined in file [E:\IDEAworkplace\ssmcrud\out\artifacts\ssmcrud_war_exploded\WEB-INF\classes\com\atguigu\dao\DepartmentMapper.class]: Unsatisfied dependency expressed through bean property ‘sqlSessionFactory’; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘sessionFactory’ defined in class path resource [applicationContext.xml]: Invocation of init method failed; nested exception is org.springframework.core.NestedIOException: Failed to parse config resource: class path resource [mybatis-config.xml]; nested exception is org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias ‘com.github.pagehelper.PageInterceptor’. Cause: java.lang.ClassNotFoundException: Cannot find class: com.github.pagehelper.PageInterceptor
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘departmentMapper’ defined in file [E:\IDEAworkplace\ssmcrud\out\artifacts\ssmcrud_war_exploded\WEB-INF\classes\com\atguigu\dao\DepartmentMapper.class]: Unsatisfied dependency expressed through bean property ‘sqlSessionFactory’; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘sessionFactory’ defined in class path resource [applicationContext.xml]: Invocation of init method failed; nested exception is org.springframework.core.NestedIOException: Failed to parse config resource: class path resource [mybatis-config.xml]; nested exception is org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias ‘com.github.pagehelper.PageInterceptor’. Cause: java.lang.ClassNotFoundException: Cannot find class: com.github.pagehelper.PageInterceptor
分页查询的4.x与5.x的配置不一样 用法有所不同,并不是向下兼容,在使用5.x版本的时候可能会报错。
pagehelper4.x版本的使用com.github.pagehelper.PageHelper
agehelper5.x版本com.github.pagehelper.PageInterceptor
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.0.0</version>
</dependency>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!--分页参数合理化 -->
<property name="helperDialect" value="mysql" />
<property name="reasonable" value="true"/>
</plugin>
</plugins>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.2.1</version>
</dependency>
<plugins>
<plugin interceptor="com.github.pagehelper.PageHelper">
<!--分页参数合理化 -->
<property name="helperDialect" value="mysql" />
<property name="reasonable" value="true"/>
</plugin>
</plugins>
类转换器找不到异常:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalArgumentException: No converter found for return value of type: class com.atguigu.beans.Msg
缺少jackson-databind依赖,该依赖会自动导入jackson-annontations和jackson-core包。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9</version>
</dependency>
未知知识记录:
一、@PathVariable是spring3.0的一个新功能:接收请求路径中占位符的值
@PathVariable(“xxx”)
通过 @PathVariable 可以将URL中占位符参数{xxx}绑定到处理器类的方法形参中@PathVariable(“xxx“)
二、@RequestParam
GET和POST请求传的参数会自动转换赋值到@RequestParam 所注解的变量上
@RequestParam(org.springframework.web.bind.annotation.RequestParam)用于将指定的请求参数赋值给方法中的形参。
例如:
<form action="/WxProgram/json/requestParamTest" method="get">
requestParam Test<br>
用户名:<input type="text" name="username"><br>
用户昵称:<input type="text" name="usernick"><br>
<input type="submit" value="提交">
</form>
@RequestMapping(value="/requestParamTest", method = RequestMethod.GET)
public String requestParamTest(@RequestParam(value="username") String userName, @RequestParam(value="usernick") String userNick){
System.out.println("requestParam Test");
System.out.println("username: " + userName);
System.out.println("usernick: " + userNick);
return "hello";
}
也可不使用@RequestParam,直接接收,此时要求controller方法中的参数名称要跟form中name名称一致。
@RequestMapping(value="/requestParamTest", method = RequestMethod.GET)
public String requestParamTest(String username, String usernick){
System.out.println("requestParam Test");
System.out.println("username: " + username);
System.out.println("usernick: " + usernick);
return "hello";
}
总结:
@RequestParam
用来处理Content-Type: 为 application/x-www-form-urlencoded编码的内容。提交方式为get或post。(Http协议中,如果不指定Content-Type,则默认传递的参数就是application/x-www-form-urlencoded类型)
RequestParam实质是将Request.getParameter() 中的Key-Value参数Map利用Spring的转化机制ConversionService配置,转化成参数接收对象或字段。
get方式中query String的值,和post方式中body data的值都会被Servlet接受到并转化到Request.getParameter()参数集中,所以@RequestParam可以获取的到。
三、@Valid
用于验证注解是否符合要求,直接加在变量employee之前,在变量中添加验证信息的要求,当不符合要求时就会在方法中返回Msg 的错误提示信息。
@RequestMapping(value = "/emp", method = RequestMethod.POST)
@ResponseBody
public Msg saveEmp(@Valid Employee employee, BindingResult result){
if (result.hasErrors()){
//校验失败,应该返回失败,在模态框中显示校验失败的错误提示
HashMap<String, Object> map = new HashMap<>();
List<FieldError> errors = result.getFieldErrors();
for (FieldError fieldError: errors ) {
System.out.println("错误的字段名" + fieldError.getField());
System.out.println("错误信息" + fieldError.getDefaultMessage());
map.put(fieldError.getField(), fieldError.getDefaultMessage());
}
return Msg.fail().add("errorFields", map);
}else{
employeeService.saveEmp(employee);
return Msg.success();
}
}
四、DepartmentExample的分析
DepartmentExample类中还包含了GeneratedCriteria、Criteria、Criterion三个静态内部类
Criterion表示一个最小粒度的条件的描述
GeneratedCriteria是一个抽象类;表示Criterion的集合,元素之间使用and连接
Criteria是GeneratedCriteria的实现类
DepartmentExample表示一个SQL语句的where、distinct、order by部分的描述;其中where部分表示Criteria的集合,元素间使用or连接
优点:
因为是代码生成的,可以省去自己写XML和接口方法
查询条件灵活
缺点:
查删改都需要使用Example,略显繁琐
没有分页功能
五:请求方式:GET&POST
详细讲解连接:https://blog.csdn.net/qq_42988711/article/details/102600788
将一个method定义成RequestMethod.GET时,可以直接通过地址访问,这非常方便我们在开发的时候调用到我们的接口并进行测试;
同样的接口,将其method更改为RequestMethod.POST时,你会发现接口在地址栏访问不了了,只有向服务器发起一个POST请求时才起作用。
GET一般用于获取/查询资源信息,而POST一般用于更新资源信息。
POST方法向服务器提交数据,比如完成表单数据的提交,将数据提交给服务器处理。
PUT方法是让服务器用请求的主体部分来创建一个由所请求的URL命名的新文档;如果那个文档存在的话,就用这个主体来代替它。
**
GET在浏览器回退时是无害的,而POST会再次提交请求。
GET产生的URL地址可以被Bookmark,而POST不可以。
GET请求会被浏览器主动cache,而POST不会,除非手动设置。
GET请求只能进行url编码,而POST支持多种编码方式。
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
GET请求在URL中传送的参数是有长度限制的,而POST么有。
对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
GET参数通过URL传递,POST放在Request body中。
**
GET和POST还有一个重大区别,简单的说:
GET产生一个TCP数据包;POST产生两个TCP数据包。
长的说:
对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
也就是说,GET只需要汽车跑一趟就把货送到了,而POST得跑两趟,第一趟,先去和服务器打个招呼“嗨,我等下要送一批货来,你们打开门迎接我”,然后再回头把货送过去。
因为POST需要两步,时间上消耗的要多一点,看起来GET比POST更有效。因此Yahoo团队有推荐用GET替换POST来优化网站性能。但这是一个坑!跳入需谨慎。为什么?
-
GET与POST都有自己的语义,不能随便混用。
-
据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。
-
并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。