MyBatis报Executor was closed这个错误

最近才学mybatis
创建了两张表,一张activity表,一张user表
我想接受前端的loginAct和其他数据,通过loginAct在user表中查找userId,再将id和其他的数据保存在activity表中,可是在查找user时一直报错
这是相关类

//这是dao和service的实现类
public interface UserDao {
    User login(@Param("loginAct") String loginAct,
               @Param("loginPwd") String loginPwd);
    List<User> getUserList();
    User findByLoginAct(String loginAct);
}

public class UserServiceImpl implements UserService {
    private UserDao userDao = SqlSessionUtil.getSession().getMapper(UserDao.class);
    @Override
    public User login(String loginAct, String loginPwd, String ip) throws LoginException {
        User user = userDao.login(loginAct, loginPwd);
        if (user == null){
            throw new LoginException("用户名或密码错误");
        }
        if (user.getExpireTime().compareTo(DataUtil.getSystimestamp()) < 0){
            throw new LoginException("时间已失效");
        }
        if (!"1".equals(user.getLockState())){
            throw new LoginException("已锁定");
        }
        if (!ip.equals(user.getAllowIps())){
            throw new LoginException("非合法ip");
        }
        return user;
    }

    @Override
    public List<User> getUserList() {
        return userDao.getUserList();
    }

    @Override
    public User findByLoginAct(String loginAct) {
        return userDao.findByLoginAct(loginAct);
    }
}

public interface ActivityDao {
    int save(Activity activity);

    List<Activity> getActiveTable(Activity activity);
}

public class ActivityServiceImpl implements ActivityService {
    private final ActivityDao activityDao = (ActivityDao) SqlSessionUtil.getSession().getMapper(ActivityDao.class);
    @Override
    public boolean save(Activity activity){
        boolean flag = true;
        int count = activityDao.save(activity);
        if (count != 1) {flag = false;}
        return flag;
    }

    @Override
    public List<Activity> getActiveTable(Activity activity, int pageNum, int pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        return activityDao.getActiveTable(activity);
    }
}
//这里下面是工具类
public class SqlSessionUtil {
    private SqlSessionUtil() {}
    private static SqlSessionFactory sqlSessionFactory = null;
    private static ThreadLocal<SqlSession> t = new ThreadLocal<>();
    static {
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static SqlSession getSession(){
        SqlSession session = t.get();
        if (session == null) {
            session = sqlSessionFactory.openSession();
            t.set(session);
        }
        return session;
    }
    public static void close(SqlSession session){
        session.close();
        t.remove();
    }
}

public class Handler implements InvocationHandler {
    Object target = null;
    public Handler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        SqlSession session = null;
        Object obj = null;
        try {
            session = SqlSessionUtil.getSession();
            obj = method.invoke(target, args);
            session.commit();
        } catch (Exception e) {
            if (session != null) {
                session.rollback();
            }
            e.printStackTrace();
            throw e.getCause();
        } finally {
            SqlSessionUtil.close(session);
        }
        return obj;
    }

    public Object getProxy(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }
}
public class ServiceFactory {
    public static Object getService(Object service){
        return new Handler(service).getProxy();
    }
}

这是我的mapper.xml

<mapper namespace="com.terfation.crm.settings.dao.UserDao">
    <select id="login" resultType="User">
        select *
        from tbl_user
        where loginAct=#{loginAct} and loginPwd = #{loginPwd}
    </select>
    <select id="getUserList" resultType="User">
        select *
        from tbl_user
    </select>
    <select id="findByLoginAct" resultType="User">
        select *
        from tbl_user
        where loginAct = #{loginAct}
    </select>
</mapper>
<mapper namespace="com.terfation.crm.workbench.dao.ActivityDao">
    <insert id="save">
        insert
        into tbl_activity (
            id, owner, name, startDate, endDate, cost, description, createTime, createBy)
        values (
            #{id},
            #{owner},
            #{name},
            #{startDate},
            #{endDate},
            #{cost},
            #{description},
            #{createTime},
            #{createBy})
    </insert>
</mapper>

这是测试代码

    @org.junit.Test
    public void test1(){
        UserService userService = (UserService) ServiceFactory.getService(new UserServiceImpl());
        ActivityService activityService = (ActivityService) ServiceFactory.getService(new ActivityServiceImpl());
        
        User user = userService.findByLoginAct("root");
        
        Activity activity = new Activity();
        activity.setId("123");
        activity.setName("123");
        activityService.save(activity);//这里报错
    }
    @org.junit.Test
    public void test(){
        UserService userService = (UserService) ServiceFactory.getService(new UserServiceImpl());
        ActivityService activityService = (ActivityService) ServiceFactory.getService(new ActivityServiceImpl());
        Activity activity = new Activity();
        activity.setId("123");
        activity.setName("123");
        activityService.save(activity);
        User user = userService.findByLoginAct("root");//调换位置后这里报错
    }
     //单独运行两个都没有问题
     //单独调试findByLoginAct又出现错误
    @org.junit.Test
    public void tryFind(){
        UserService userService = (UserService) ServiceFactory.getService(new UserServiceImpl());
        User user = userService.findByLoginAct("root");
        System.out.println(user.getId());
    }

    @org.junit.Test
    public void trySave(){
        ActivityService activityService = (ActivityService) ServiceFactory.getService(new ActivityServiceImpl());
        Activity activity = new Activity();
        activity.setId("123");
        activity.setName("123");
        activityService.save(activity);
    }

这是两个调换前的错误信息

img

img

这是两个调换后的错误信息

img

img

错误原因:
在第一次创建SQL session查询时进入了代理类的的invoke方法中

        try {
            session = SqlSessionUtil.getSession();
            obj = method.invoke(target, args);
            session.commit();
        } catch (Exception e) {
            if (session != null) {
                session.rollback();
            }
            e.printStackTrace();
            throw e.getCause();
        } finally {
            SqlSessionUtil.close(session);
        }

此时提交后进入finally中调用工具类将sqlsession关闭了
在下一次执行操作时还是调用的原来的sqlsession创建的dao对象中的方法

    private final ActivityDao activityDao = (ActivityDao) SqlSessionUtil.getSession().getMapper(ActivityDao.class);

虽然代理类对象调用工具类方法重新获取了到了一个新的sqlsession,但是原来创建dao对象的sqlsession已经关闭了

关于原本没问题的代码,调试时报错,好像是因为idea debug展现变量 会调用变量的toString方法才产生问题,原因不清除,日后再找错

这个问题我解决了,主要是因为private UserDao userDao = SqlSessionUtil.getSession().getMapper(UserDao.class);这行代码,
当你用mybatis自动生成dao实现类机制时候,mybatis底层是会调用原来的SqlSession对象的,但是之前的SqlSession对象被你一次使用后关闭了SqlSessionUtil.close(session);
所以之后会报Executor was closed,执行者被关闭错误,有两种解决办法:
1.你可以将UserDao userDao = SqlSessionUtil.getSession().getMapper(UserDao.class);这行代码放到方法中,
每次调用生成新的UserDao对象,mybatis底层也会调用新的SqlSession对象,这是我采用的办法
2.可以选择一次会话不关闭SqlSession对象,用户关闭浏览器才SqlSession.close(),(我更偏向于这个方法,但是还没想到怎么实现)

insert标签,需要指定传参。

<insert id="save" parameterType=“”>