最近用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));
這種調用方式 問題解決