最近用light-4j的项目在正式环境偶尔报Executor was close问题 导致sql有时执行失败
错误分析
这个错误看起来像是两个进程 A进程在对数据库操作的时候 B进程将sqlSession关闭了 但是想不通在哪里关闭的
代码分析
封装了一个单例的sessionFactory
public class SqlSessionFactoryHelper {
//首先创建静态成员变量sqlSessionFactory,静态变量被所有的对象所共享。
public static SqlSessionFactory sqlSessionFactory;
private SqlSessionFactoryHelper() {}
//使用静态代码块保证线程安全问题
static{
if(sqlSessionFactory==null) {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
}
//获得一个session
public static SqlSession getSession(boolean autoCommit){
SqlSession session= sqlSessionFactory.openSession(autoCommit);
return session;
}
}
感觉这里并没有什么问题 继续看service层
public class UserService {
private static final Logger LOGGER = LoggerFactory.getLogger(UserService.class);
private SqlSession sqlSession;
private UserDao userDao;
void getUserDao (boolean autoCommit){
sqlSession= SqlSessionFactoryHelper.getSession(autoCommit);
userDao = sqlSession.getMapper(UserDao.class);
}
/**
* 根据id查找用户脱敏
* @param userId
* @return
* @throws ServiceException
*/
public User findByUserIdSafe(Integer userId) throws ServiceException {
getUserDao(Boolean.TRUE);
try {
return userDao.findByUserIdSafe(userId);
}catch (Exception e){
LOGGER.error(e.getMessage());
throw new ServiceException("查询用户信息失败:"+e.getMessage());
}finally {
sqlSession.close();
}
}
}
这里看起来也没有问题 根据需求决定获取一个是否需要自动提交的sqlSession 然后调用dao层获取数据 在finally里边关闭sqlSession 并不会有线程冲突啊 继续看看控制层
/**
* 获得用户信息
*/
public class GetUserHandler implements HttpHandler {
private UserService userService = new UserService();
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
exchange.getRequestReceiver().receiveFullString((exchangeCopy, message) -> {
exchangeCopy.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain; charset=utf-8");
JsonResultHelper jsonResult = new JsonResultHelper();
Map<String, Deque<String>> queryParam = exchange.getQueryParameters();
String userId = queryParam.get("userId") == null ? "" : queryParam.get("userId").getFirst();
if(!StringHelper.isNumeric(userId)){
jsonResult = new JsonResultHelper(JsonResultHelper.FAIL,"输入参数错误!");
}else {
try{
User user = userService.findByUserIdSafe(Integer.parseInt(userId));
if(user==null){
jsonResult = new JsonResultHelper(JsonResultHelper.FAIL,"用户不存在!");
}else{
jsonResult = new JsonResultHelper(user);
}
}catch (ServiceException e){
jsonResult = new JsonResultHelper(JsonResultHelper.FAIL,e.getMessage());
}catch (Exception e){
e.printStackTrace();
jsonResult = new JsonResultHelper(JsonResultHelper.FAIL,"处理错误,请联系管理员!");
}
}
exchangeCopy.setStatusCode(StatusCodes.OK);
exchangeCopy.getResponseSender().send(jsonResult.toJsonStr(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES));
}, Charset.forName("UTF-8"));
}
}
这里加了system.out.print(userService);
以后发现多次请求调用了同一个service对象 查看了light-4j文档后 发现只在项目启动时对handler进行初始化 由于之前习惯了Spring的代码风格 把service对象定义成了类成员变量 并且在service层中将变量SqlSession也定义成了类成员变量 导致数据库操作频繁时会互相影响 出现这次问题
解决方法
将service调用方式改为
User user = new UserService().findByUserIdSafe(Integer.parseInt(userId));
这种调用方式 问题解决