鼻子亲了脸 - spring
type: channel
link: https://www.bzqll.com/tag/spring/
items: Spring开启事务后导致Redis执行失败
items: Spring Struts2 Mybatis整合Hessian出现重复加载上下文
items: Spring学习笔记（四）事务管理
items: Spring学习笔记（二）Controller的使用
items: Spring学习笔记（三）数据库的操作
items: Spring学习笔记（一）配置文件的使用

Spring开启事务后导致Redis执行失败
type: item
date: 2018-12-23T21:04:07+08:00
description: 1. 案例有甲乙两个公司，甲选择一批自己的订单（一个订单有多个货物），生成的6位数字验证码，乙通过验证码接收订单，接收后需要将订单及订单中的货物的操作权限归属乙，甲不再拥有操作权限，要保证订单和货物的权限必须同时更改成功，我选择了开启了事务。在程序中我首先校验验证码，然后去redis查询对应内容，如果验证码正确则进行更改订单及货物的权限，然后移除Redis中的验证码。@Transactional public String receiveOrder(String code){ //第一步 校验验证码 ...... //第二步 获取验证码对应的内容 ...... //第三步 执行对应的更改权限逻辑 ...... //第四步 移除验证码 ...... //返回结果信息 return .....; } 在进行测试的时候发现有问题，第一种情况，有时候可以执行成功，有时候失败，于是直接打印日志，发现失败的时候获取的内容是空的，实际是存在的；第二种，执行成功后还能继续执行成功，验证码没有被移除掉。经过查询相关信息发现了问题的原因竟然是因为事务导致的，是因为当开启事务的时候，Redis将进行队列模式执行，Redis会按照队列顺序进行执行，会进行等待。2. 具体原因：Redis为单进程单线程模式，采用队列模式将并发访问变成串行访问，Redis对事物支持不会很复杂，当一个客服端连接Redis服务时，发出了MULTI命令时，这个连接会进入事物，在执行MULTI命令之后，执行所有的命令都不会执行，会先放到一个队列中，会提示正在Query，当最后执行EXEC命令之后，Redis会按照之前的进入队列的顺序，执行命令。3. 解决方案解决办法是利用了Spring事务代理的特性将Redis执行的部分抽离出来，导致Redis部分事务失效，这样也能保证权限的问题。@Transactional public String receiveOrder(String code){ //第一步 校验验证码 ...... //第二步 获取验证码对应的内容 String result = getCodeContent(code); //第三步 执行对应的更改权限逻辑 ...... //第四步 移除验证码 if(!removeCode(code)){ //如果移除失败 触发事务回滚 } //返回结果信息 return .....; } //获取验证码内容 private String getCodeContent(String code){ //获取验证码对应的内容 ...... //返回结果信息 return .....; } //移除验证码 private boolean removeCode(String code){ //移除验证码 ...... //返回结果信息 return .....; }

Spring Struts2 Mybatis整合Hessian出现重复加载上下文
type: item
date: 2018-04-24T20:32:06+08:00
description: 在做公司项目时，需要使用Hessian进行远程调用，网上的方案都是SpringMVC如何整合Hessian，这时候遇到了一个Struts的情况下如何整合Hessian的问题。 在配置Struts时已经进行了如下配置： <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>serviceHessian</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-hessian-server.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>serviceHessian</servlet-name> <url-pattern>/remoting/*</url-pattern> </servlet-mapping> 按照网上找到的一堆各种各样的配置方法,会出现HessianProtocolException: '' is an unknown code 网上搜索异常解决的方案都是启用hessian客户端的重载<property name="overloadEnabled" value="true" /> 或者是HessianProxyFactory factory = new HessianProxyFactory(); factory.setOverloadEnabled(true); 其实最终的原因是因为，在web.xml中struts2对所有的请求都进行了拦截，在进行数据转发操作的时候会转发错误导致出现HessianProtocolException: '' is an unknown code 目前想到的解决是对struts2的过滤器进行更改 <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-application-content.xml</param-value> </context-param> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/api/*</url-pattern> </filter-mapping> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>serviceHessian</servlet-name> <ser…

Spring学习笔记（四）事务管理
type: item
date: 2018-03-04T20:31:30+08:00
description: 事务管理在程序进行逻辑操作的时候，往往会对数据库进行几次操作，但是有时候会造成某一次操作失败，导致产生了不合适的数据。 使用事务处理，当进行多次对数据库操作的时候其中一次执行失败的时候，则全部不进行操作。 此处案例结合之前的几篇文章进行处理 创建service类(已添加事务管理注解) package com.example.demo2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.List; @Service public class GirlServer { @Autowired //自动注入 private GirlRepo girlRepo;//此处的类已经在前面的学习笔记中写过 @Transactional //事务注解 public void InsertTwoGirl(){ Girl a = new Girl();//此处的类已经在前面的学习笔记中写过 a.setAge(16); a.setUserName("12345"); a.setPassword("12345"); girlRepo.save(a); Girl b = new Girl(); b.setAge(25); b.setUserName("321656"); b.setPassword("321656"); girlRepo.save(b); } } 添加事务管理注解 在创建的项目中 xxxApplication.java 类中开启事务管理 @EnableTransactionManagement ，在上面的service类中已经添加了 @Transaction 注解 编写测试代码 @GetMapping("/addTwoGirl") public void addTwoGirl(){ girlServer.InsertTwoGirl(); } 访问地址：http://127.0.0.1:8088/addTwoGirl 执行 数据库设置 emmmm......此处的Girl....插入......是因为在imooc看的视频教程，莫名的就开了一把车.....请忽略..... 数据库必须支持 事务管理，类型为 InnoDB 。 数据库中girl表的userName字段的限制长度为5， 若 不使用事务，则会因一条数据长度不够只插入一条数据 若 使用事务，则也会因数据长度不够而插入失败，此时数据库没有插入一条数据。

Spring学习笔记（二）Controller的使用
type: item
date: 2018-03-01T20:31:01+08:00
description: Controller的使用 @RestController 用来处理http请求，Spring4之后新加的注解，原来返回json需要 @Controller 配合 @ResponseBody 实现，现在只需要一个 @RestController 即可 @RequestMapping 配置URL映射，限定请求方法、请求参数、请求头 A. 在一个类的函数上面加上@RequestMapping(value = "/",method = RequestMethod.GET) 就是只能通过GET方式访问 http://127.0.0.1:8088/ @RequestMapping(value = "/",method = RequestMethod.GET) public String index(){ return "OK"; } B. 在一个类的函数上面加上 @RequestMapping(value = "/" 就可以通过GET或者POST方式访问 http://127.0.0.1:8088/index @RequestMapping(value = "/index") public String index(){ return "OK"; } C. 在一个类的上面加上 @RequestMapping(value = "/index",method = RequestMethod.GET) ，要访问该类下面的函数，则URL链接中必须加上 /index ，访问链接 http://127.0.0.1:8088/index/index @RestController @RequestMapping(value = "/index",method = RequestMethod.GET) public class IndexController { @RequestMapping(value = "/index",method = RequestMethod.GET) public String index(){ return "OK"; } } @PathVariable 带占位符的 URL 是 Spring3.0 新增的功能，该功能在SpringMVC 向 REST 目标挺进发展过程中具有里程碑的意义 通过 @PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中：URL 中的 {xxx} 占位符可以通过 @PathVariable("xxx") 绑定到操作方法的入参中。 @RequestMapping(value = "/{id}",method = RequestMethod.GET) public String index(@PathVariable(value = "id",required = false) Integer myId){ return "id:"+myId; } a. 放置方法1 @RequestMapping(value = "/{id}",method = RequestMethod.GET) 访问URL链接为http://127.0.0.1:8088/index/123 时输出 id:123 b. 放置方法2 @RequestMapping(value = "{id}/",method = RequestMethod.GET) 访问URL链接为 http://127.0.0.1:8088/123/index 时输出 id:123 @PathVariable(value = "id",required = false) value：获取URL中参数的值（要与 @RequestMapping(value = "/{id}") 中的id一致） required： 是否必选项 @RequestParam 绑定请求参数 @RequestParam(value = "id",required = false,defaultValue = "1")value：获取URL中参数的值 required： 是否必选项 defaultValue： 默认的值 @RequestMapping(value = "/",method = RequestMethod.GET) public String index(@RequestParam(value = "id",required = false,defaultValue = "1") Integer myId){ return "id:"+myId; } 访问URL为 http://127.0.0.1:8088/index/?id=12312 时输出内容为 id:12312 访问URL为 http://127.0.0.1:8088/index/ 输出…

Spring学习笔记（三）数据库的操作
type: item
date: 2018-03-01T20:30:08+08:00
description: 数据库的操作 首先增加依赖，在pom.xml中增加以下依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> 配置数据库 修改application.yml datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/dbgirl username: root password: 123456 jpa: hibernate: ddl-auto: update show-sql: true 编写实体类 package com.example.demo2; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class Girl { @Id @GeneratedValue private int id; private String userName; private String password; private Integer age; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } } JpaRepository 引入 package com.example.demo2; import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; public interface GirlRepo extends JpaRepository<Girl,Integer> { public List<Girl> findByAge(Integer age); } 编写CURD package com.example.demo2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController public class IndexController { @Autowired private GirlRepo girlRepo; /** * 查询所有数据 * @return */ @GetMapping(value = "/findAllGirl") public List<Girl> findAllGirl(){ return girlRepo.findAll(); } /** * 添加一个girl * @param userName * @param password * @param age * @return */ @PostMapping(value = "/addGirl") public Girl addGirl(@RequestParam("userName") String userName,@RequestParam("password") String password,@RequestParam("age") Integer age){ Gi…

Spring学习笔记（一）配置文件的使用
type: item
date: 2018-02-28T16:31:17+08:00
description: emmmmmm......如何搭建springboot项目，已经超级简单了，这里就不记录下来了application配置文件使用项目默认使用的是 application.properties ，但是建议使用application.yml文件，更加方便简洁 在使用配置文件时，分两种情况：开发，产品上线，可以通过配置不同的配置文件来实现 建立开发环境时使用的配置文件： application-dev.yml 建立生产环境时使用的配置文件： application-prod.yml 如果使用开发环境时，修改 application.yml 为 spring: profiles: active: dev 如果使用生产环境时，修改 application.yml 为 spring: profiles: active: prod 还可以使用jar的方式启动，且使用不同的配置 使用mvn install 或者 mvn package 打包jar之后在target文件夹中会生产xxx.jar,打开CMD，输入命令行 如果使用开发环境时，使用 java -jar xxxx.jar --spring.profiles.active=dev 如果使用生产环境时，使用 java -jar xxxx.jar --spring.profiles.active=prod 文件配置示例# =================================================================== # COMMON SPRING BOOT PROPERTIES # # This sample file is provided as a guideline. Do NOT copy it in its # entirety to your own application. ^^^ # =================================================================== # ---------------------------------------- # CORE PROPERTIES # ---------------------------------------- debug=false # Enable debug logs. trace=false # Enable trace logs. # LOGGING logging.config= # Location of the logging configuration file. For instance, `classpath:logback.xml` for Logback. logging.exception-conversion-word=%wEx # Conversion word used when logging exceptions. logging.file= # Log file name (for instance, `myapp.log`). Names can be an exact location or relative to the current directory. logging.file.max-history=0 # Maximum of archive log files to keep. Only supported with the default logback setup. logging.file.max-size=10MB # Maximum log file size. Only supported with the default logback setup. logging.level.*= # Log levels severity mapping. For instance, `logging.level.org.springframework=DEBUG`. logging.path= # Location of the log file. For instance, `/var/log`. logging.pattern.console= # Appender pattern for output to the console. Supported only with the default Logback setup. logging.pattern.dateformat=yyyy-MM-dd HH:mm:ss.SSS # Appender pattern for log date format. Supported only with the defa…