目录
一、Shiro框架的平稳对接
二、mapper无法正确映射,Dao层找不到数据
三、pageHelper失效,分页分了个寂寞
四、偶尔找不到符号/编译器无法解析
五、如何对类型为Integer的字段进行模糊搜索
六、SQL语句改了,但是报错中的SQL语句一直不变
完事开头难。一个非纯信息展示平台,也就是有着增删改查功能的平台,首先遇到的问题就是会话维持和权限管理。毕竟我不能创建别人的订单,别人也不能代替我进行收货。
我们的项目决定采用shiro进行会话维持和权限管理,那我们首先来fork一个使用了shiro工具的网站后端的源码,来看它在哪里用到了shiro以及怎么用的:
以下代码部分是有关shiro的配置、权限声明以及shiro-redis缓冲区的配置。阅读代码后加上了所有的注释,并对其进行测试运行。
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import undestiny.stell.shiro.MyRealm;
import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
@Slf4j
@Component
public class ShiroConfig {
/*DefaultWebSecurityManager类主要定义了设置subjectDao,获取会话模式,设置会话模式,设置会
*话管理器,是否是http会话模式等操作,它继承了DefaultSecurityManager类,实现了
*WebSecurityManager接口
*/
@Bean
public DefaultWebSecurityManager securityManager(MyRealm realm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
log.info("securityManager -----------> 初始化了");
return securityManager;
}
//一个shiro过滤器初始化的工厂方法
@Bean(name = "shiroFilterFactoryBean")
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// //自定义过滤器
// Map filters = shiroFilterFactoryBean.getFilters();
// // 注意这里不要用Bean的方式,否则会报错
// filters.put("myRole", new MyRoleFilter());
// shiroFilterFactoryBean.setFilters(filters);
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("/unauth");
shiroFilterFactoryBean.setUnauthorizedUrl("/unauth");
//下面这些hashMap是定义了哪些url需要哪些权限,进行了权限管理的头文件似的声明
Map hashMap = new LinkedHashMap<>();
// hashMap.put("/**", "authc");
//alipay
hashMap.put("/pay", "roles[user]");
hashMap.put("/return", "anon");
hashMap.put("/notify", "anon");
//test
hashMap.put("/unauth", "anon");
hashMap.put("/userTest", "roles[user]");
hashMap.put("/adminTest", "roles[admin]");
hashMap.put("/anonTest", "anon");
hashMap.put("/exception", "anon");
//swagger2
hashMap.put("/swagger-ui.html", "anon");
hashMap.put("/swagger/**", "anon");
hashMap.put("/swagger-resources/**", "anon");
hashMap.put("/v2/**", "anon");
hashMap.put("/webjars/**", "anon");
hashMap.put("/configuration/**", "anon");
//account
hashMap.put("/password", "anon");
hashMap.put("/verifyCode/password", "anon");
// hashMap.put("/auth", "roles[user]");
hashMap.put("/signIn", "anon");
hashMap.put("/signOut", "anon");
hashMap.put("/signUp", "anon");
hashMap.put("/signUp/**", "anon");
//admin
hashMap.put("/admin", "roles[admin]");
hashMap.put("/admin/userLogin/today", "roles[admin]");
hashMap.put("/admin/member", "roles[admin]");
hashMap.put("/admin/buyHistoryStatics", "roles[admin]");
hashMap.put("/admin/buyHistoryStatics/**", "roles[admin]");
hashMap.put("/admin/ buyHistory", "roles[admin]");
//company
// hashMap.put("/company", "myRole[company]");
// hashMap.put("/company/**", "myRole[company]");
//fileReader
hashMap.put("/file/company/logo/**", "anon");
hashMap.put("/file/company/content/**", "anon");
//homepage
// hashMap.put("/collection", "roles[user]");
// hashMap.put("/collection/**", "roles[user]");
hashMap.put("/homepage/**", "anon");
hashMap.put("/search/**", "anon");
hashMap.put("/visit/**", "anon");
// //member
// hashMap.put("/member", "roles[user]");
// hashMap.put("/member/**", "roles[user]");
hashMap.put("/**", "anon");
shiroFilterFactoryBean.setFilterChainDefinitionMap(hashMap);
return shiroFilterFactoryBean;
}
/**
* 开启aop注解支持
* 即在controller中使用 @RequiresPermissions("user/userList")
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager){
AuthorizationAttributeSourceAdvisor attributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
//设置安全管理器
attributeSourceAdvisor.setSecurityManager(securityManager);
return attributeSourceAdvisor;
}
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
// /**
// * 处理未授权的异常,返回自定义的错误页面(403)
// * @return
// */
// @Bean
// public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
// SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
// Properties properties = new Properties();
// /*未授权处理页*/
// properties.setProperty("UnauthorizedException", "classpath:403.html");
// resolver.setExceptionMappings(properties);
// return resolver;
// }
// shiro-redis
//====== session共享 ========
/**
* 配置shiro redisManager
* 使用的是shiro-redis开源插件
*
* @return
*/
@Bean
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setHost("127.0.0.1:6379");
redisManager.setDatabase(0);
return redisManager;
}
/**
* DAO大家都用过,数据访问对象,用于会话的CRUD,比如我们想把Session保存到数据库,那么可以实现自己的SessionDAO,
* 通过如JDBC写到数据库;比如想把Session放到Memcached中,可以实现自己的Memcached SessionDAO;
* 另外SessionDAO中可以使用Cache进行缓存,以提高性能;
*/
@Bean
RedisSessionDAO redisSessionDAO() {
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager());
return redisSessionDAO;
}
@Bean
DefaultWebSessionManager sessionManager(RedisSessionDAO redisSessionDAO) {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(redisSessionDAO);
return sessionManager;
}
/**
* 缓存控制器,来管理如用户、角色、权限等的缓存的;
* 因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能
*/
@Bean
RedisCacheManager redisCacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
//redis中针对不同用户缓存(此处的id需要对应user实体中的id字段,用于唯一标识)
redisCacheManager.setPrincipalIdFieldName("id");
//用户权限信息缓存时间
redisCacheManager.setExpire(200000);
return redisCacheManager;
}
}
在此基础上进行修改,使其适配我们的项目:
package com.phoenix.logistics.config;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import com.phoenix.logistics.shiro.MyRealm;
import java.util.LinkedHashMap;
import java.util.Map;
@Slf4j
@Component
public class ShiroConfig {
@Bean
public DefaultWebSecurityManager securityManager(MyRealm realm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
log.info("securityManager -----------> 初始化了");
return securityManager;
}
@Bean(name = "shiroFilterFactoryBean")
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("/unauth");
shiroFilterFactoryBean.setUnauthorizedUrl("/unauth");
Map hashMap = new LinkedHashMap<>();
//swagger2
hashMap.put("/swagger-ui.html", "anon");
hashMap.put("/swagger/**", "anon");
hashMap.put("/swagger-resources/**", "anon");
hashMap.put("/v2/**", "anon");
hashMap.put("/webjars/**", "anon");
hashMap.put("/configuration/**", "anon");
//account
hashMap.put("/account/checkPassword", "anon");
hashMap.put("/account/changPassword", "anon");
hashMap.put("/account/changMessage", "anon");
hashMap.put("/account/checkUsername", "anon");
hashMap.put("/account/signIn", "anon");
hashMap.put("/account/signOut", "anon");
hashMap.put("/account/signUp", "anon");
hashMap.put("/account/user/all", "roles[user]");
//adminOrder
hashMap.put("/admin_order/deal", "roles[admin]");
hashMap.put("/admin_order/detail", "roles[admin]");
hashMap.put("/admin_order/message", "roles[admin]");
hashMap.put("/admin_order/search", "roles[admin]");
hashMap.put("/admin_order/list", "roles[admin]");
//car
hashMap.put("/car/list", "roles[admin]");
hashMap.put("/car/add", "roles[admin]");
hashMap.put("/car/delete", "roles[admin]");
hashMap.put("/car/free", "roles[admin]");
//driver
hashMap.put("/driver/list", "roles[admin]");
hashMap.put("/driver/add", "roles[admin]");
hashMap.put("/driver/delete", "roles[admin]");
hashMap.put("/driver/free", "roles[admin]");
//userOrder
hashMap.put("/user_order/submit", "roles[user]");
hashMap.put("/user_order/detail", "roles[user]");
hashMap.put("/user_order/message", "roles[user]");
hashMap.put("/user_order/search", "roles[user]");
hashMap.put("/user_order/list", "roles[user]");
hashMap.put("/user_order/receive", "roles[user]");
shiroFilterFactoryBean.setFilterChainDefinitionMap(hashMap);
return shiroFilterFactoryBean;
}
/**
* 开启aop注解支持
* 即在controller中使用 @RequiresPermissions("user/userList")
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager){
AuthorizationAttributeSourceAdvisor attributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
//设置安全管理器
attributeSourceAdvisor.setSecurityManager(securityManager);
return attributeSourceAdvisor;
}
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
// shiro-redis
//====== session共享 ========
/**
* 配置shiro redisManager
* 使用的是shiro-redis开源插件
*
* @return
*/
@Bean
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setHost("127.0.0.1:6379");
redisManager.setDatabase(0);
return redisManager;
}
/**
* DAO大家都用过,数据访问对象,用于会话的CRUD,比如我们想把Session保存到数据库,那么可以实现自己的SessionDAO,
* 通过如JDBC写到数据库;比如想把Session放到Memcached中,可以实现自己的Memcached SessionDAO;
* 另外SessionDAO中可以使用Cache进行缓存,以提高性能;
*/
@Bean
RedisSessionDAO redisSessionDAO() {
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager());
return redisSessionDAO;
}
@Bean
DefaultWebSessionManager sessionManager(RedisSessionDAO redisSessionDAO) {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(redisSessionDAO);
return sessionManager;
}
/**
* 缓存控制器,来管理如用户、角色、权限等的缓存的;
* 因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能
*/
@Bean
RedisCacheManager redisCacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
//redis中针对不同用户缓存(此处的id需要对应user实体中的id字段,用于唯一标识)
redisCacheManager.setPrincipalIdFieldName("id");
//用户权限信息缓存时间
redisCacheManager.setExpire(200000);
return redisCacheManager;
}
}
配置初步完成,剩下的就是在适当的地方进行使用了。
首先我们看如何登录:
@RestController
@Api("登录Controller")
@Validated
@RequestMapping("/account")
public class AccountController {
@PostMapping("/signIn")
@ApiOperation("登录")
@ApiImplicitParams({
@ApiImplicitParam(name = "username", value = "用户名", required = true, paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "password", value = "密码(长度6-20)", required = true, paramType = "query", dataType = "String")
})
public Result doLogin(@RequestParam("username")@NotNull String username, @RequestParam("password")@NotNull @Size(min = 6,max = 20)String password) {
if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
return Result.fail("用户名或密码不能为空!");
}
AuthenticationToken token = new UsernamePasswordToken(username, PasswordUtil.convert(password));
try {
//尝试登陆,将会调用realm的认证方法
SecurityUtils.getSubject().login(token);
}catch (AuthenticationException e) {
if (e instanceof UnknownAccountException) {
return Result.fail("用户不存在");
} else if (e instanceof LockedAccountException) {
return Result.fail("用户被禁用");
} else if (e instanceof IncorrectCredentialsException) {
return Result.fail("密码错误");
} else {
return Result.fail("用户认证失败");
}
}
UserDTO principal = (UserDTO) SecurityUtils.getSubject().getPrincipal();
if(principal.getType()==1) return Result.success("登录成功",principal);
return Result.success("登录成功", new GetUserResponse(accountService.getUser(username),principal.getType()));
}
可以看到,登录在controller里是通过shiro提供的SecurityUtils.getSubject.login(token)方法来实现的,这里会调用realm的认证方法:
package com.phoenix.logistics.shiro;
import com.phoenix.logistics.dto.UserDTO;
import com.phoenix.logistics.entity.Admin;
import com.phoenix.logistics.entity.User;
import com.phoenix.logistics.mapper.AdminMapper;
import com.phoenix.logistics.mapper.UserMapper;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MyRealm extends AuthorizingRealm {
@Autowired
UserMapper userMapper;
@Autowired
AdminMapper adminMapper;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
Object principal = principals.getPrimaryPrincipal();
UserDTO userDTO = (UserDTO) principal;
String username = userDTO.getUsername();
int type = userDTO.getType();
//注入角色与权限
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//管理员
if(type == 1)
info.addRole("admin");
//普通用户
if(type == 0)
info.addRole("user");
return info;
}
/**
* 认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//数据库匹配,认证
String username = token.getUsername();
String password = new String(token.getPassword());
//管理员
Admin admin = adminMapper.getAdminByUsername(username);
if(admin != null && (admin.getPassword()+"").equals(password)){
UserDTO userDTO = new UserDTO(admin);
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(userDTO, token.getCredentials(), getName());
return info;
}
//普通用户
User user = userMapper.getUserByUsername(username);
if(user != null && (user.getPassword()+"").equals(password)){
UserDTO userDTO = new UserDTO(user);
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(userDTO, token.getCredentials(), getName());
return info;
}
//认证失败
throw new AuthenticationException();
}
}
由于我们这个项目是物流管理项目,管理员负责增删改查车辆和司机,用户主要可以提交和查看自己的订单,而且用户和管理员的功能并没有很多重合的地方,所以我们以不同的身份登录时会在info中增加不同的role,而且管理员这边不会增加用户的role,所以管理员是没有用户的功能权限的。
还有一点是,我们使用的shiro工具,它自己所带的UsernamePasswordToken是使用名为username的字符串类型的字段进行用户验证的(而且个人觉得用Long类型的id作为用户名也不是很妥,用户体验也不好),所以我们在User类中将username作为副键,登录时输入用户名和密码。
并且我们在异常这里控制好返回的信息,一旦出现账号相关的错误,就返回相应的异常信息。
这样我们就可以在其他接口中维持此用户的登录状态了!只有正确登录才能执行相应的方法:有RequireRole为user的方法要以用户身份登录用户账户,有RequireRole为admin的方法要以管理员身份登录管理员账户。不登录就调用方法,就会出现以下报错:
至此,shiro工具就配置好了,我们会话的维持功能就做好啦!
很多东西,用别人的架子是察觉不到的,只有自己做对接才能发现很多脑残的细节。
根据自己的经验,我一开始做的数据库设计,是这样的:
可以看到,我喜欢把数据库里的字段单词之间用下划线隔开。(我们姑且称为下划线命名法)
但是我在entity包中采用的字段命名是这样的:
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ApiModel("ControllerOrder 管理员订单")
public class AdminOrder implements Serializable {
@Id
@ApiModelProperty("id")
private Long id;
@ApiModelProperty("用户订单id")
private Long userOrderId;
@ApiModelProperty("货物id")
private Long goodsId;
@ApiModelProperty("车辆id")
private Long carId;
@ApiModelProperty("司机id")
private Long driverId;
@ApiModelProperty("管理员用户名")
private String adminUsername;
@ApiModelProperty("状态")
private Integer status;
@ApiModelProperty("状态更新时间")
private String statusUpdateTime;
@ApiModelProperty("预计运输时间(以小时为单位)")
private Integer transportTime;
@ApiModelProperty("发货时间")
private String sendTime;
@ApiModelProperty("送达时间")
private String arriveTime;
@ApiModelProperty("是否已读")
private Integer isRead;
public AdminOrder(Long userOrderId, Long goodsId, Integer status, String statusUpdateTime,Integer transportTime,Integer isRead) {
this.userOrderId = userOrderId;
this.goodsId = goodsId;
this.status = status;
this.statusUpdateTime = statusUpdateTime;
this.transportTime = transportTime;
this.isRead = isRead;
}
}
显然用的是标准的驼峰命名法。这样我在通过id返回详情时,即使我的数据库里有东西,返回出来也是这样的:
发现问题是因为只有status有信息,而刚刚好status是下划线命名法和驼峰命名法一样的那个。
解决方法当然是把数据库里的字段都改成标准的驼峰命名法,之后再测试这个方法:
芜湖成功!
因为我在发现这个问题之前一直以为是一个mapper的bean注入到不同的service里返回不同了,我想想不太可能,问了学长,学长让我拉出来单写接口,只用mapper,合在一个service里,结果相同:果然不是mapper的问题,才往mapper本身和数据库方面想。后来改完再问学长,学长说其实要自己做数据库和mapper的映射才行,否则MyMapper自己是找不到名称不同的数据库字段的。
学到了学到了
这个问题分两个阶段解决的,分别解决的也是两个小的问题:
1.分页之后要的不是所要求的那页,而是返回了所有的信息
2.分页之后,pages和total显示的都是0
第一个问题在于:我们在设计时想要分不同的种类展示,像淘宝的订单展示一样:
而这些订单列表的展示都是做在一个service里的(为了良好的代码复用性和封装性),根据前端传来的参数进行筛选,这样的话,要求哪些订单,返回的分页的各个参数也就都要不同。
最最开始的时候,我们将mapper封装的很好,只有一个返回所有订单的接口给service用,然后在service里再进行信息的分类和补全等等,但是这样的话必然没法做到通过pageHelper来分页了,只能返回一个个列表,而不是一个一个页,所以我们又把mapper进行一定程度的拆分,拆成如下:
@Select("SELECT id,userOrderId,status,statusUpdateTime,goodsId FROM adminOrder")
List getBriefAdminOrderList();
@Select("SELECT id,userOrderId,status,statusUpdateTime,goodsId FROM adminOrder WHERE status=#{status}")
List getBriefAdminOrderListByStatus(@Param("status")Integer status);
变成全部和分状态获取,这样可以在service里获取分好页的Page,再对该页的每一项进行信息的补全就行了。(相当于把分类放到mapper里做了)
就当我觉得自己方案完美,必没问题时,我发现我的orderBy字段好像没用了,即使我传的是desc,给我的也是按asc排的序,而且我传的是“statusUpdateTime desc",它给我的是"id asc":
我开始觉得是排序不对,于是我在该网站上搜了好多有关startPage和orderBy的问题······
直到我发现,即使我的pageSize==1,它也给了我数据库里所有的东西······
我根本没分页!!!
我可以回退到学pageHelper怎么用了(唉······
原来是要让MyBatis查询语句紧随其后,那我就知道怎么改了:
然后第一个小问题就解决了!
第二个小问题,发现于前端找我要pages时,我发现我返回的pages,和total一直是0。
找了好久,发现是数据处理的问题,我不应该创建一个新的列表,返回的时候包装这个列表,那一开始startPage还有什么意义?
我们应该直接在页内改动,或者获取它在mybatis的select语句得到的pages和total,再赋给我们要返回的新页:
@Override
public Page getBriefAdminOrderListByStatus(int pageNum, int pageSize,int status){
if(status==0)PageHelper.startPage(pageNum, pageSize,"statusUpdateTime asc");
else PageHelper.startPage(pageNum, pageSize,"statusUpdateTime desc");
if(status==2||status==3) updateTransportingAdminOrderStatus();
Page briefAdminOrderPage = new Page<>();
if(status!=4) briefAdminOrderPage = new Page(new PageInfo(adminOrderMapper.getBriefAdminOrderListByStatus(status)));
else briefAdminOrderPage = new Page(new PageInfo(adminOrderMapper.getBriefAdminOrderList()));
List briefAdminOrderArrayList = briefAdminOrderPage.getItems();
for(TmpAdminOrder adminOrder:briefAdminOrderArrayList){
UserOrder userOrder = userOrderMapper.getUserOrderById(adminOrder.getUserOrderId());
Goods goods = goodsMapper.getGoodsById(adminOrder.getGoodsId());
adminOrder.setGoodsName(goods.getName());
adminOrder.setGoodsType(goods.getType().getDescription());
adminOrder.setSenderUsername(userOrder.getSenderUsername());
adminOrder.setReceiverUsername(userOrder.getReceiverUsername());
}
briefAdminOrderPage.setItems(briefAdminOrderArrayList);
return briefAdminOrderPage;
}
如上解决!
这个解决方案有时候确实是万能的:
如果明明你的代码写的不能再对了,报了个找不到符号,但是你的import写的很明白啊?!
这时候你只需要:
打开maven窗口:
先clean,再compile:
毕竟有的时候只是因为的你的包没下载完······
如果还不行,可以考虑直奔./m2文件下删所下的文件,再重新compile
这个真的有用!
我们的订单列表设置的搜索框用的是针对id的模糊搜索,但是id字段因为要符合数据库中的自动递增,所以我们设置成为了Long类型。
但是这样的话,如果我们的SQL语句这样写:
@Select("SELECT id,userOrderId,status,statusUpdateTime,goodsId FROM adminOrder WHERE id LIKE %#{id}")
List searchAdminOrder(@Param("id")Integer id);
它就会报错:
进而我们发现数字类型没法简单的通配符······
又是寻觅良久,找到解决方案:
@Select("SELECT id,userOrderId,status,statusUpdateTime,goodsId FROM adminOrder WHERE id LIKE CONCAT('%',#{id,jdbcType=VARCHAR},'%')")
List searchAdminOrder(@Param("id")Integer id);
只要这么写,它就能跑出来了!
这个是我第一个写到脊背发凉,想念着佛经调的bug
我的DELETE方法一直天衣无缝:
但是postman的报错是这样的:
但是我的语法没啥错呀?
于是我就开始各种改:
加个分号:
改个语句顺序(虽然不知道对不对):
甚至把整句话截到一半:
我的报错里面的SQL语句变都没有变!!!
汗毛耸立······
直到我注意到了报错中的这一句:
我才发现:原来我的select语句没有打星号,导致java自己把SELECT和DELETE方法做成了内联方法?!
所以以后不要抓着一个语句不放,也看看其他可能的错误!+好好看报错信息!!
这样将SELECT方法的语句改正确之后:
至此解决!
以上就是我在写后端时碰到的主要几个大问题和解决方案,以下是项目的相关资源:
接口文档:Swagger UIhttp://1.15.30.214:8081/swagger-ui.html#/
项目仓库(12.29开源):
logistics: J2EE大作业 网页版物流平台 (gitee.com)https://gitee.com/phoenix1975/logistics个人gitee仓库:
仓库 - 凤雏 (phoenix1975) - Gitee.comhttps://gitee.com/phoenix1975/projects