第一个MyBatis案例

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38234015/article/details/89682985

瞎聊

今天跟着参考书目尝试书写第一个MyBatis案例,跟着资料一路往下写,最后的运行结果却很是诧异,没有报任何错误,但是也没有任何结果。经过多次排查和修改,最终完成了整个项目。
通过这次修改过程,发现了之前没有注意到的一个编程恶习:对异常处理不完整,包括对异常进行捕获时,没有使用Exception作最后的捕获,最不能原谅的是捕获异常之后,竟然没有做任何处理,包括对异常信息的显示。正是因为这两个原因,导致问题的定位耗时严重。
除此之外,由于缺乏对XML的基本认识,导致配置文件出现了一些本不应该出现的问题,比如DTD声明错误以及 & 字符没有进行转义等低级错误。
不过通过这次改错的过程,对MyBatis的运行过程的理解更近了一步,还是很开心!


现在将项目重置到初始状态,再现修改过程,以为前车之鉴。

目录结构

avatar


血泪调试过程

为了方便叙述,先给出所有代码内容(代码内容来源于《JavaEE互联网轻量级框架整合开发》第三章实例)。
Role.java

package cn.edu.sxau.zpc.pojo;

public class Role {
	private int id;
	private String roleName;
	private String note;
	
	/**
	* <p>Title: </p>
	* <p>Description: </p>
	*/
	public Role() {
		// TODO Auto-generated constructor stub
	}
  // 省略Getters and Setters
}

RoleMapper.java

package cn.edu.sxau.zpc.mapper;

import java.util.List;
import cn.edu.sxau.zpc.pojo.Role;

public interface RoleMapper {
	public int insertRole(Role role);
	public int deleteRole(int id);
	public int updateRole(Role role);
	public Role getRole(int id);
	public List<Role> findRoles(String roleName);
}

RoleMapper.xml

<?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="cn.edu.sxau.zpc.mapper.RoleMapper">
	<insert id="insertRole" parameterType="role">
		insert into t_role(role_name, note) values(#{roleName}, #{note})
	</insert>
	
	<delete id="deleteRole" parameterType="int">
		delete from t_role where id = #{id}
	</delete>
	
	<update id="updateRole" parameterType="role">
		update t_role set role_name = #{roleName}, note = #{note} where id = #{id}
	</update>
	
	<select id="getRole" parameterType="int">
		select id, role_name as roleName, note from t_role
		 where id = #{id}
	</select>
	
	<select id="findRoles" parameterType="string" resultType="role">
		select id, role_name as roleName, note from t_role
		 where role_name like concat('%', #{roleName}, '%')
	</select>
</mapper>

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<!-- 别名 -->
	<typeAliases>
		<typeAlias type="cn.edu.sxau.zpc.pojo.Role" alias="role"/>
	</typeAliases>
	
	<!-- 数据库环境 -->
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC"></transactionManager>
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
				<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=FALSE&serverTimezone=UTC&characterEncoding=utf8"/>
				<property name="username" value="admin"/>
				<property name="password" value="12346"/>
			</dataSource>
		</environment>
	</environments>
	
	<!-- 映射文件 -->
	<mappers>
		<mapper resource="cn/edu/sxau/zpc/mapper/RoleMapper.xml"/>
	</mappers>
</configuration>

SqlSessionFactoryUtils.java

package cn.edu.sxau.zpc.utils;

import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class SqlSessionFactoryUtils {
	private final static Class<SqlSessionFactoryUtils> LOCK = SqlSessionFactoryUtils.class;
	private static SqlSessionFactory sqlSessionFactory = null;
	
	private SqlSessionFactoryUtils() {
		
	}
	
	public static SqlSessionFactory getSqlSessionFactory() {
		synchronized (LOCK) {
			if (sqlSessionFactory != null) {
				return sqlSessionFactory;
			}
			String resource = "mybatis-config.xml";
			InputStream inputStream;
			try {
				inputStream = Resources.getResourceAsStream(resource);
				sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
			} catch (IOException e) {
				e.printStackTrace();
				return null;
			}
			return sqlSessionFactory;
		}
	}
	
	public static SqlSession openSqlSession() {
		if (null == sqlSessionFactory) {
			getSqlSessionFactory();
		}
		return sqlSessionFactory.openSession();
	}
}

Main.java

package cn.edu.sxau.zpc.main;

import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import cn.edu.sxau.zpc.mapper.RoleMapper;
import cn.edu.sxau.zpc.pojo.Role;
import cn.edu.sxau.zpc.utils.SqlSessionFactoryUtils;

public class Main {
	public static void main(String[] args) {
		Logger log = Logger.getLogger(Main.class);
		SqlSession sqlSession = null;
		try {
			sqlSession = SqlSessionFactoryUtils.openSqlSession();
			RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
			Role role = roleMapper.getRole(1);
			log.info(role.getRoleName());
		} catch (Exception e) {
			
		} finally {
			if (sqlSession != null) {
				sqlSession.close();
			}
		}
	}
}


运行项目,控台没有输入任何信息。
手动debug,通过在代码中到处穿插System.out.println("in here");,最终将问题锁定在SqlSessionFactoryUtils中创建SqlSessionFactory的那个地方,即sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
既然找到了问题的出处,就开始排查,找了很长时间都没有找到根源,最后突发奇想,将catch中的IOException捕获放大到Exception捕获,控制台终于报出来错误信息。

try {
	inputStream = Resources.getResourceAsStream(resource);
	sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (Exception e) {
	e.printStackTrace();
	return null;
}

摘取有效报错信息:

Caused by: org.xml.sax.SAXParseException; lineNumber: 3; columnNumber: 16; 文档根元素 "configuration" 必须匹配 DOCTYPE 根 "mapper"。

根据报错信息发现问题是mybatis-config.xml文件的DTD声明有问题

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">

正确格式应该是:

<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">

修改完DTD定义之后再次运行,发现还是有错误,不过好在错误原因报的很明确

Cause: org.apache.ibatis.builder.BuilderException: Error creating document instance.  Cause: org.xml.sax.SAXParseException; lineNumber: 15; columnNumber: 96; 对实体 "serverTimezone" 的引用必须以 ';' 分隔符结尾。

要奔溃的感觉,因为我的数据库是MySQL8.0,连接的时候url附带信息较多,但是在XML中竟然没有对&符号进行转义。。。

<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=FALSE&serverTimezone=UTC&characterEncoding=utf8"/>

正确格式应该是:

<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=FALSE&amp;serverTimezone=UTC&amp;characterEncoding=utf8"/>

经过上面的修改,控制台终于打印出日志信息,但是总感觉哪里有些不对劲,于是试着打印一下role对象的信息,System.out.println(role.getNote());,果然,程序都没有执行到这里。我竟然在捕获了异常的情况下没有打印异常信息。。。
修改后Main.java的main方法如下:

public static void main(String[] args) {
	Logger log = Logger.getLogger(Main.class);
	SqlSession sqlSession = null;
	try {
		sqlSession = SqlSessionFactoryUtils.openSqlSession();
		RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
		Role role = roleMapper.getRole(1);
		System.out.println(role.getNote());
		log.info(role.getRoleName());
	} catch (Exception e) {
		e.printStackTrace();
	} finally {
		if (sqlSession != null) {
			sqlSession.close();
		}
	}
}

再次运行程序,得到下面的报错信息:

Error querying database.  
Cause: org.apache.ibatis.executor.ExecutorException: 
  A query was run and no Result Maps were found for the Mapped Statement 'cn.edu.sxau.zpc.mapper.RoleMapper.getRole'.  It's likely that neither a Result Type nor a Result Map was specified.

好吧,RoleMapper.xml的getRole的select标记中忘了写resultType属性了,修改一下:

<select id="getRole" parameterType="int" resultType="role">
	select id, role_name as roleName, note from t_role
   where id = #{id}
</select>

终于,一切正常了,日志反馈信息也与数据库信息吻合。

DEBUG 2019-04-29 20:39:04,319 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <==      Total: 1
 INFO 2019-04-29 20:39:04,321 cn.edu.sxau.zpc.main.Main: enenene

猜你喜欢

转载自blog.csdn.net/qq_38234015/article/details/89682985