junit 4 sqlSession.close()空指针异常

本文分享了在使用MyBatis框架时遇到的log4j配置错误导致的空指针异常及debug日志无法打印的问题,并提供了解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天做mybatis的Demo,用junit4测试一直空指针这种是log4j配置有问题,删除所有不需要的配置就可以。这种是log4j配置文件有问题,删除多余的配置就可以!
还有logger.debug()打印无结果也是log4j.properties配置问题。

"D:\java\IntelliJ IDEA 2019.2.4\jbr\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\java\IntelliJ IDEA 2019.2.4\lib\idea_rt.jar=54212:D:\java\IntelliJ IDEA 2019.2.4\bin" -Dfile.encoding=UTF-8 -classpath "D:\java\IntelliJ IDEA 2019.2.4\lib\idea_rt.jar;D:\java\IntelliJ IDEA 2019.2.4\plugins\junit\lib\junit-rt.jar;D:\java\IntelliJ IDEA 2019.2.4\plugins\junit\lib\junit5-rt.jar;D:\java\ljj-di4\target\test-classes;D:\java\ljj-di4\target\classes;D:\MavenRepository\org\mybatis\mybatis\3.5.2\mybatis-3.5.2.jar;D:\MavenRepository\mysql\mysql-connector-java\8.0.11\mysql-connector-java-8.0.11.jar;D:\MavenRepository\com\google\protobuf\protobuf-java\2.6.0\protobuf-java-2.6.0.jar;D:\MavenRepository\junit\junit\4.12\junit-4.12.jar;D:\MavenRepository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 Test1,testGetMemberByUnameAndSex java.lang.ExceptionInInitializerError at Test1.testGetMemberByUnameAndSex(Test1.java:80) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) Caused by: org.apache.ibatis.exceptions.PersistenceException: ### Error building SqlSession. ### The error may exist in SQL Mapper Configuration ### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.lang.NullPointerException at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30) at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:52) at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:36) at com.itheima.untils.MyBatisUtils.<clinit>(MyBatisUtils.java:20) ... 23 more Caused by: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.lang.NullPointerException at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:121) at org.apache.ibatis.builder.xml.XMLConfigBuilder.parse(XMLConfigBuilder.java:98) at org.apache.ibatis.session.SqlSessionFactoryBuilder.build(SqlSessionFactoryBuilder.java:50) ... 25 more Caused by: java.lang.NullPointerException at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:105) ... 27 more Process finished with exit code -1 ??? @Test public void testGetMemberByUnameAndSex(){ SqlSession session= MyBatisUtils.getSession(); MemberDaoMapper memberDaoMapper=session.getMapper(MemberDaoMapper.class); List<Member> memberList=memberDaoMapper.getMemberByUnameAdnSex("李四",0); for(Member member:memberList){ System.out.println(member); } session.close(); }???<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.itheima.mapper.MemberDaoMapper"> <!-- 插入会员(主键自增,不指定member_id) --> <insert id="insert" parameterType="com.itheima.pojo.Member" keyProperty="member_id" useGeneratedKeys="true"> insert into ms_member(uname, password, email, sex, mobile) values (#{uname}, #{password}, #{email}, #{sex}, #{mobile}) </insert> <!-- 批量插入(无需指定member_id) --> <insert id="insert1"> insert into ms_member(uname, password, email, sex, mobile) values ('李白', '123456', 'aabb@qq.com', 0, '2434234'), ('程咬金', '123456', 'yyrry@qq.com', 0, '234324') </insert> <!-- 根据ID查询会员 --> <select id="getMemberById" resultType="com.itheima.pojo.Member"> select member_id, uname, password, email, sex, mobile from ms_member where member_id = #{member_id} </select> <!-- 查询所有会员 --> <select id="findAll" resultType="com.itheima.pojo.Member"> select * from ms_member </select> <!-- 按用户名和性别查询(方法1:参数顺序传递) --> <select id="getMemberByUnameAndSex" resultType="com.itheima.pojo.Member"> select * from ms_member where uname = #{0} and sex = #{1} </select> <!-- 按用户名和性别查询(方法2:@Param注解命名参数) --> <select id="getMemberByUnameAndSex1" resultType="com.itheima.pojo.Member"> select * from ms_member where uname = #{uname} and sex = #{sex} </select> <!-- 按用户名和性别查询(方法3:Map参数) --> <select id="getMemberByUnameAndSex2" resultType="com.itheima.pojo.Member"> select * from ms_member where uname = #{uname} and sex = #{sex} </select> <!-- 按用户名和手机号查询(动态SQL) --> <select id="findMemberByUnameAndMobile" resultType="com.itheima.pojo.Member" parameterType="com.itheima.pojo.Member"> select * from ms_member where 1=1 <if test="uname != null and uname != ''"> and uname like concat('%', #{uname}, '%') </if> <if test="mobile != null and mobile != ''"> and mobile like concat('%', #{mobile}, '%') </if> </select> <!-- 删除会员 --> <delete id="deleteById" parameterType="Integer"> delete from ms_member where member_id = #{member_id} </delete> <!-- 更新会员 --> <update id="updateById" parameterType="com.itheima.pojo.Member"> update ms_member set uname = #{uname}, password = #{password}, email = #{email}, sex = #{sex}, mobile = #{mobile} where member_id = #{member_id} </update> </mapper>????<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.itheima.mapper.MemberDaoMapper"> <!-- insert元素(修正后) --> <insert id="insert" parameterType="com.itheima.pojo.Member" keyProperty="member_id" useGeneratedKeys="true"> insert into ms_member(uname, password, email, sex, mobile) values (#{uname}, #{password}, #{email}, #{sex}, #{mobile}) </insert> <!-- 批量插入示例 --> <insert id="insert1"> insert into ms_member(uname, password, email, sex, mobile) values ('李白', '123456', 'aabb@qq.com', 0, '2434234'), ('程咬金', '123456', 'yyrry@qq.com', 0, '234324') </insert> <!-- 根据ID查询会员 --> <select id="getMemberById" resultType="com.itheima.pojo.Member"> select member_id, uname, password, email, sex, mobile from ms_member where member_id = #{member_id} </select> <!-- 查询所有会员 --> <select id="findAll" resultType="com.itheima.pojo.Member"> select * from ms_member </select> <!-- 按用户名和性别查询(修正方法名和SQL) --> <select id="getMemberByUnameAndSex" resultType="com.itheima.pojo.Member"> select * from ms_member where uname = #{uname} and sex = #{sex} </select> <select id="getMemberByUnameAndSex1" resultType="com.itheima.pojo.Member"> select * from ms_member where uname = #{uname} and sex = #{sex} </select> <select id="getMemberByUnameAndSex2" resultType="com.itheima.pojo.Member"> select * from ms_member where uname = #{uname} and sex = #{sex} </select> <!-- 按用户名和手机号查询(已修正mobile条件) --> <select id="findMemberByUnameAndMobile" resultType="com.itheima.pojo.Member" parameterType="com.itheima.pojo.Member"> select * from ms_member where 1=1 <if test="uname != null and uname != ''"> and uname like concat('%', #{uname}, '%') </if> <if test="mobile != null and mobile != ''"> and mobile like concat('%', #{mobile}, '%') </if> </select> <!-- delete元素 --> <delete id="deleteById" parameterType="Integer"> delete from ms_member where member_id = #{member_id} </delete> <!-- update元素 --> <update id="updateById" parameterType="com.itheima.pojo.Member"> update ms_member set uname = #{uname}, password = #{password}, email = #{email}, sex = #{sex}, mobile = #{mobile} where member_id = #{member_id} </update> </mapper>
最新发布
06-11
<think>好的,我现在需要帮助用户分析他们提供的Java测试类代码,并指出其中的问题,特别是关于`sqlSession.close()`在`@After`方法和测试方法中的重复调用。首先,我应该仔细阅读用户提供的代码,理解每个部分的作用,然后找出潜在的问题。 首先,用户展示了一个使用MyBatis进行单元测试的类`UserMapperTest`。他们使用了JUnit 4的注解,如`@Before`、`@After`和`@Test`。在`@Before`方法`setUp`中,他们初始化了`SqlSessionFactory`,创建了`SqlSession`,并获取了`UserMapper`的实例。这看起来是正确的,因为每个测试方法运行前都需要一个新的会话和映射器。 接下来,`@After`方法`tearDown`中检查了`sqlSession`是否为空,如果不为空则关闭它。这符合资源管理的良好实践,确保每个测试方法执行后都会关闭会话,防止资源泄露。 然后是两个测试方法:`testFindByID`和`testFindTotal`。这两个方法都调用了`sqlSession.close()`。这里可能存在重复关闭的问题,因为`@After`已经处理了关闭操作。重复关闭同一个`SqlSession`可能会导致异常,比如在MyBatis中,关闭已经关闭的会话可能会抛出`IllegalStateException`。 接下来,我需要确认MyBatis中`SqlSession`的关闭机制。通常,每个`SqlSession`应该在使用完毕后被正确关闭,但多次关闭是否会有问题呢?根据MyBatis的文档,一旦调用了`close()`方法,再次调用它应该不会有任何效果,或者可能会抛出异常。因此,在测试方法中显式关闭会话,加上`@After`中的关闭,会导致两次关闭调用,这可能引发问题。 此外,用户可能在测试方法中关闭会话后,`@After`方法再次尝试关闭,此时`sqlSession`可能已经为null或者已经关闭,从而导致潜在的异常。例如,在`testFindByID`中,先调用了`sqlSession.close()`,然后在`tearDown`中再次检查并关闭,但此时`sqlSession`已经被关闭,可能会抛出异常。 另一个需要注意的地方是,如果在测试方法中关闭了会话,那么后续对`userMapper`的调用可能会失败,因为映射器与已经关闭的会话相关联。不过,在用户的测试方法中,关闭会话是在最后执行的,所以可能不会影响当前测试,但如果有多个测试方法依赖于同一个会话,可能会有问题,但在这个例子中,每个测试方法都是独立的,因为`@Before`会在每个测试前重新初始化。 总结问题:测试方法中不必要的显式关闭`sqlSession`,导致与`@After`方法中的关闭重复,可能引发异常。应该移除测试方法中的`sqlSession.close()`,仅保留`@After`中的关闭逻辑,确保每个测试方法结束后只关闭一次。 另外,检查`tearDown`方法中的条件判断。如果`sqlSession`在测试方法中被关闭并设置为null,那么`@After`中的检查可以防止NullPointerException。但根据当前代码,测试方法中的`sqlSession.close()`并没有将`sqlSession`变量设为null,所以在`tearDown`中,即使测试方法已经关闭了会话,`sqlSession`变量仍然不为null,只是处于关闭状态。因此,再次调用`close()`可能会导致问题。 解决方案建议: 1. 移除测试方法中的`sqlSession.close()`,让`@After`方法统一管理资源的关闭。 2. 确保`tearDown`方法正确处理可能的异常,例如捕获并忽略`IllegalStateException`,或者检查会话是否已关闭再决定是否关闭。不过,通常MyBatis的`close()`方法可以多次调用而不抛出异常,但最好避免这种情况。 3. 可能需要在关闭后将会话变量设为null,但这不是必须的,只要确保不重复关闭即可。 最后,检查代码的其他部分,比如资源加载是否正确,是否有异常处理的需要。例如,`setUp`方法中的`getResourceAsStream`应该处理异常,但用户代码已经声明抛出Exception,这可能足够,但在实际应用中可能需要更细致的处理。 综上所述,主要问题是重复关闭`SqlSession`,需要调整测试方法中的关闭调用,保持资源管理的集中和一致性。</think>以下是对你提供的Java测试类代码的分析及改进建议: ### 代码分析 1. **重复关闭SqlSession问题**: - 在`@After`注解的`tearDown`方法中已正确实现了`sqlSession`的关闭操作 - 但在`testFindByID()`和`testFindTotal()`测试方法中又显式调用了`sqlSession.close()` - 这会导致每个测试方法执行时会触发两次关闭操作,可能引发`IllegalStateException` 2. **资源管理规范**: ```java // 测试方法中的冗余关闭代码(应删除) sqlSession.close(); ``` 3. **潜在风险**: - MyBatis的`SqlSession`实例被重复关闭 - 可能干扰MyBatis的一级缓存机制 - 增加调试时的复杂度 ### 改进建议 ```java @Test public void testFindByID() { User user = userMapper.findByID(1); System.out.println(user); // 删除此处的 sqlSession.close() } @Test public void testFindTotal() { Integer total = userMapper.findTotal(); System.out.println(total); // 删除此处的 sqlSession.close() } ``` ### 优化后的完整代码 ```java public class UserMapperTest { private UserMapper userMapper; private SqlSession sqlSession; @Before public void setUp() throws Exception { InputStream ace = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ace); sqlSession = sqlSessionFactory.openSession(); userMapper = sqlSession.getMapper(UserMapper.class); } @After public void tearDown() { if(sqlSession != null) { sqlSession.close(); } } @Test public void testFindByID() { User user = userMapper.findByID(1); System.out.println(user); } @Test public void testFindTotal() { Integer total = userMapper.findTotal(); System.out.println(total); } } ``` ### 最佳实践建议 1. **事务管理**: ```java // 在@Before中开启事务 sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false); // 在@After中统一提交/回滚 sqlSession.rollback(); // 或 commit() ``` 2. **异常处理增强**: ```java @After public void tearDown() { try { if(sqlSession != null) { sqlSession.rollback(); sqlSession.close(); } } catch (Exception e) { System.err.println("资源关闭异常: " + e.getMessage()); } } ``` 3. **使用try-with-resources**(Java 7+): ```java @Before public void setUp() throws Exception { try (InputStream ace = Resources.getResourceAsStream("mybatis-config.xml")) { SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ace); sqlSession = sqlSessionFactory.openSession(); userMapper = sqlSession.getMapper(UserMapper.class); } } ``` 是否需要进一步说明MyBatis的其他配置细节或测试场景?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值