Java基础笔记(MyBatis)

1 使用MyBatis

iBatis 是 MyBatis 前身,它最大的特性就是动态SQL语句,它是通过 XML 文件配置SQL语句,可以随意的拼接sql语句,Maven工程中使用 MyBatis,需要在 pom.xml 文件中添加依赖:

<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis</artifactId>
	<version>${mybatis.version}</version>
</dependency>

MyBatis 结合 Spring,还需要引入以下依赖

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>${mybatis-spring.version}</version>
</dependency>

1.1 SqlSession 对象

SqlSession 是 Mybatis 的核心,它作用就是替代 JDBC,它可以向 SQL 语句传参,执行 SQL 语句,获取结果以及事务的控制。
获取一个 SqlSession 对象需要通过 SqlSessionFactory 对象实例的 openSession() 方法,而 SqlSessionFactory 对象实例化时,需要传入的MyBatis 的核心配置文件,如:

Reader reader = Resources.getResourceAsReader("cn/seiei/config/Configuration.xml");
// 创建 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
// 打开 SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();

1.2 MyBatis 配置文件

MyBatis 核心配置文件包含了 连接数据库的基本信息,以及注册 Dao 映射配置文件,如:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<environments default="development">
		<environment id="development">
			<!-- 事务 -->
			<transactionManager type="JDBC">
				<property name="" value="" />
			</transactionManager>
			<!-- 数据库信息 -->
			<dataSource type="UNPOOLED">
				<property name="driver" value="com.mysql.cj.jdbc.Driver" />
				<property name="url"
					value="jdbc:mysql://localhost:3306/test?serverTimezone=UTC" />
				<property name="username" value="root" />
				<property name="password" value="123456" />
			</dataSource>
		</environment>
	</environments>
	<!-- DAO配置文件地址 -->
	<mappers>
		<mapper resource="cn/seiei/config/sqlxml/UserMapper.xml" />
	</mappers>
</configuration>

1.3 Dao 配置文件

SQL语句编写在 Dao 配置文件中,它需要在 MyBatis 配置文件中注册了才能正常使用,如:

<?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">

<!-- namespace 命名空间,必须有,意思与包名一样,在 SqlSession 中调用时使用 -->
<mapper namespace="UserMapper">
	<!-- 
		resultMap 标签表示查询后的结果以什么样的 java 类型(对象)表示
		其中 id 是唯一的,用于附着于数据库操作标签中的 resultMap 或 resultType
		type 表示的是返回的对象,意指在外部提前设计好的 bean
		result 标签中的 column 表示数据库对应的列名称,而 property 表示的是指定赋值的 bean 对应的属性
	-->
	<resultMap id="BaseResultMap" type="cn.seiei.bean.User">
		<result column="id" jdbcType="INTEGER" property="userId" />
		<result column="name" jdbcType="VARCHAR" property="name" />
		<result column="age" jdbcType="INTEGER" property="age" />
		<result column="sex" jdbcType="VARCHAR" property="sex" />
	</resultMap>
	<!-- 
		数据库操作标签
		如 select 标签
		其中 id 是唯一的,用于在 SqlSession 调用,resultMap 用于指向返回结果对象 
	 -->
	<!-- 查询数据 -->
	<select id="queryUserMsg" resultMap="BaseResultMap" parameterType="cn.seiei.bean.User">
		select name, age, sex, id from users
		<where>
			<if test="sex != null and !&quot;&quot;.equals(sex.trim())">and sex = #{sex}</if>
			<if test="name != null and !&quot;&quot;.equals(name.trim())">and name like '%' #{name} '%'</if>
		</where>
	</select>
	<!-- 添加数据 -->
	<insert id="addUserMsg" parameterType="cn.seiei.bean.User" useGeneratedKeys="true" keyProperty="userId" keyColumn="id">
		insert into users (name, sex, age) values (#{name}, #{sex}, #{age})
	</insert>
</mapper>

配置好以上步骤,就可以使用 SqlSession 对象调用 selectList 方法传入要执行的 sql 语句Dao 配置文件所在的位置(字符串,由配置文件命名空间及语句的 id 组成,用句号间隔)以及 sql 语句所用到的参数,最后调用 commit 提交。
eg:

public static List<User> queryUserMsg(String sex, String ishas) {
    // 初始化 dbAccess 对象,用于获取 SqlSession 对象,SqlSession 对象使用过后要关闭
    DBAccess dbAccess = new DBAccess();
    SqlSession sqlSession = null;
    // 初始化结果        
    List<User> usersList = new ArrayList<User>();
    // 初始化数据库传入参数
    // 映射文件中的操作标签的 parameterType 只接受一个参数,所以要传入两个信息时,就要使用的对象的封装了
    
    User parameter = new User();
    parameter.setSex(sex);
    parameter.setName(ishas);
    
    try {
        // 获取 SqlSession 对象
        sqlSession = dbAccess.getSqlSession();
        // 执行命名空间为 UserMapper 映射文件中的 queryUerMsg 方法,并将结果赋值
        usersList = sqlSession.selectList("UserMapper.queryUserMsg", parameter);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        //  关闭 SqlSession 对象        
        if (sqlSession != null) {
            sqlSession.close();
        }
    }
    return usersList;
}

2 MyBatis-Generator

可以使用 Maven 的插件自动生成 MyBatis 所需的 beanmapperdao 文件,需要在 pom.xmlplugins 标签中添加:

<!-- mybatis 自动化生产 bean,mapper,dao 插件 -->
<!-- 使用命令 mybatis-generator:generate -->
<plugin>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-maven-plugin</artifactId>
    <version>1.3.7</version>
    <dependencies>
        <dependency>
            <groupId>com.microsoft.sqlserver</groupId>
            <artifactId>sqljdbc4</artifactId>
            <version>4.0</version>
        </dependency>
    </dependencies>
    <configuration>
    	<!-- 重新执行时,原有的代码会被替代重写 -->
        <verbose>true</verbose>
        <overwrite>true</overwrite>
       <!-- 配置文件 -->
        <configurationFile>${basedir}/src/main/resources/generatorcfg.xml</configurationFile>
    </configuration>
</plugin>

其中还需要引入 generatorcfg.xml 配置文件,大致如下,细节自行查询:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <!-- mybatis-generator:generate -->
    <!--  context:生成一组对象的环境, 其中 id:上下文id,用于在生成错误时提示,其余的 targetRuntime 以及 defaultModelType 都可以不声明使用默认值-->
    <context id="context" targetRuntime="MyBatis3">
        <!-- 自动生成equals方法和hashcode方法 -->
        <plugin type="org.mybatis.generator.plugins.EqualsHashCodePlugin"></plugin>
        <!-- 自动序列化 -->
        <plugin type="org.mybatis.generator.plugins.SerializablePlugin"></plugin>
        <!-- 自动生成toString方法 -->
        <plugin type="org.mybatis.generator.plugins.ToStringPlugin"></plugin>

        <!-- 注释 -->
        <commentGenerator>
            <!-- 用于表示生成的注释中是否包含有生成日期,true 表示抑制生成 -->
            <property name="suppressDate" value="true" />
            <!-- 是否去除自动生成注释 -->
            <property name="suppressAllComments" value="true" />
        </commentGenerator>

        <!-- 数据库的链接URL,用户名,密码 -->
        <jdbcConnection driverClass="com.microsoft.sqlserver.jdbc.SQLServerDriver"
            connectionURL="jdbc:sqlserver://192.168.0.203:1433;databaseName=TWErp"
            userId="j2ee" password="123456"></jdbcConnection>

        <!-- 数据库类型映射Java数据类型,默认会先尝试使用Integer,Long,Short等来对应DECIMAL和 NUMERIC数据类型 -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>

        <!-- 生成模型的包名及位置 -->
        <javaModelGenerator targetPackage="com.fang.produceSchedule.bean"
            targetProject="src/main/java">
            <!-- 是否允许子包,即targetPackage.schemaName.tableName -->
            <property name="enableSubPackages" value="false"/>
            <!-- 是否对model添加 构造函数 -->
            <property name="constructorBased" value="true"/>
            <!-- 是否对类CHAR类型的列的数据进行trim操作 -->
            <property name="trimStrings" value="true"/>
            <!-- 建立的Model对象是否 不可改变  即生成的Model对象不会有 setter方法,只有构造方法 -->
            <property name="immutable" value="false"/>
        </javaModelGenerator>

        <!-- 生成映射文件的包名及位置 -->
        <sqlMapGenerator targetPackage="com.fang.produceSchedule.mapping"
            targetProject="src/main/java">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>

        <!-- 生成DAO的包名及位置 -->
        <javaClientGenerator targetPackage="com.fang.produceSchedule.dao"
            type="XMLMAPPER" targetProject="src/main/java">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>
        
        <!-- 需要映射的表的名称,以及一些是否要生成的 sql 方法 -->
        <!-- 
            其中 tableName 是要映射的表名,domainObjectName 是要输出的对应名称
            还可以选择预定义一些 sql 语句:
            enableInsert(默认true):指定是否生成insert语句;
            enableSelectByPrimaryKey(默认true):指定是否生成按照主键查询对象的语句(就是getById或get);
            enableSelectByExample(默认true):MyBatis3Simple为false,指定是否生成动态查询语句;
            enableUpdateByPrimaryKey(默认true):指定是否生成按照主键修改对象的语句(即update);
            enableDeleteByPrimaryKey(默认true):指定是否生成按照主键删除对象的语句(即delete);
            enableDeleteByExample(默认true):MyBatis3Simple为false,指定是否生成动态删除语句;
            enableCountByExample(默认true):MyBatis3Simple为false,指定是否生成动态查询总条数语句(用于分页的总条数查询);
            enableUpdateByExample(默认true):MyBatis3Simple为false,指定是否生成动态修改语句(只修改对象中不为空的属性);
         -->
        <table tableName="t_LineProperties" domainObjectName="LineProperties" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"></table>
    </context>
</generatorConfiguration>

最后点击 maven build.. 输入 mybatis-generator:generate 命令即可。

如果数据库中的字段为 text 或者 blob 这种大文本类型,在使用MybatisGenerator工具自动生成代码的时候会将其进行特殊处理,会默认拆分 实体类,拆分后多产生一个 XXXXWithBlobs 类,里头是对应 testblob 这些大文本类型的属性,想要取消这种默认行为,就需要在 <table> 标签下添加 columnOverride 配置,如下,其中 column 值为对应的的列名:

<table tableName="t_SYUsers" domainObjectName="SYUser"
			enableCountByExample="false" enableUpdateByExample="false"
			enableDeleteByExample="false" enableSelectByExample="false"
			selectByExampleQueryId="false">
	<columnOverride column="vcard" jdbcType="VARCHAR" />
	<columnOverride column="photodata" jdbcType="VARCHAR" />
	<columnOverride column="hometown" jdbcType="VARCHAR" />
	<columnOverride column="adress" jdbcType="VARCHAR" />
	<columnOverride column="memo" jdbcType="VARCHAR" />
</table>

3 MyBatis 拦截器

MyBatis 拦截器可以拦截一切对数据库的动作过程,比方说现使用 MySql 的 limit 关键字实现分页查询功能,并且希望这个功能能够复用。通过 MyBatis 拦截器可以当 MyBatis 执行查询 sql 语句时,将其拦截下来,并通过组装重组此 sql 语句(后缀添加 limit 关键字),从而达到复用的效果。

3.1 Interceptor 接口

使用 MyBatis 拦截器需要声明一个实现 Interceptor 接口的类,同时在声明该类时添加 @Interceptes 注释,该注释的作用是确定拦截的 MyBatis 源码位置,同时实现该接口需要实现三个方法,分别是:

  • plugin 方法:用于判断是否为要拦截的对象
  • intercept 方法:拦截后的动作
  • setProperties 方法:用于获取配置文件注册时中参数
/*
 *	MyBatis 分页拦截器
 *	实现 Mybatis 的 Interceptor 接口
 * 	选中接口后,按 F2 可搜索其实现类
 * 
 */

/* 
 * MyBatis 的 @Interceptes 注释用于声明要拦截的位置(查看MyBatis源码)
 * @Signature 的 type 指向所要拦截的接口的 Class, method 指向所要拦截的方法, args 是该拦截方法的参数
 * MyBatis 的 StatementHandler 主要用于制作 Statement 对象,即装载 sql 语句
 */
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class }) })
public class PageInterceptor implements Interceptor {

	/*
	 * 判断是否为要拦截的对象,参数是被检测的对象 如果不是要拦截的对象,则返回,如果是,则返回达成协议,具有代理权力的代理类 这里就是拦截要触发 prepare
	 * 方法的对象,增删改查都需要这个方法,所以这里只是初步过滤
	 */
	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this); // this 就是本拦截器,查看源码
	}

	/*
	 * 参数 invocation 为被拦截下来的对象 首先,需要进一步筛选,比如分页功能只是查,就要把增删改这些给过滤掉 如何过滤? invocation
	 * 是被拦截的对象,使用反射就可以获取它的一切信息 此时只要命名需要分页功能的查询方法名称加入特定的识别字母(如 ByPage 字母作后缀),进行筛选即可
	 */
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		// 获取 Statement
		StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
		// MyBatis 的包装方法用于反射获取信息
		MetaObject metaObject = MetaObject.forObject(statementHandler, new DefaultObjectFactory(),
				new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
		// 查看源码,其实先拦截了 RoutingStatementHandler,再通过它的 delegate 访问到 BaseStatementHandler
		// 而 BaseStatementHandler 通过 MappedStatement 对象访问到具体方法的具体属性
		MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement"); // 字符串是要获取信息的OGNL表达式,注意小写
		// 获取 Dao 接口的名称(sql 的 xml 配置文件中的 id 属性)
		String id = mappedStatement.getId();
		// 筛选匹配后
		if (id.matches(".+ByPage$")) {

			// 获取 BoundSql 类,它包含 Sql 的信息,因为 BoundSql 类 statementHandler 有直接获取的获取的方法,所以不用反射
			BoundSql boundSql = statementHandler.getBoundSql();

			// 获取 sql 参数,sql 语句就算不用参数,也要使用 Map 的形式,目的是为其他可能需要参数 sql 着想
			@SuppressWarnings("unchecked")
			Map<String, Object> paramsMap = (Map<String, Object>) boundSql.getParameterObject();
			Page page = (Page) paramsMap.get("page"); // 获取页数对象
			// 获取原始 sql 语句
			String sql = boundSql.getSql();

			// 查询拦截数据在数据库中的总条数并装置到 Page 对象中,用于传回拦截方
			// 获取总条数,注意查询sql语句返回的总条数时要声明别名
			// 拿出来的这条 sql 语句可能需要参数,mybatis 先会把 sql 语句中参数替代成 ? 号,需要使用 ParameterHandler 的 set
			// 填充参数
			String countSql = "select count(*) from (" + sql + ") a";
			// 获取当前拦截对象的 Connection 数据库链接
			Connection connection = (Connection) invocation.getArgs()[0];
			PreparedStatement preparedStatement = connection.prepareStatement(countSql);
			// 填充参数
			ParameterHandler parameterHandler = (ParameterHandler) metaObject.getValue("delegate.parameterHandler"); // 注意小写
			parameterHandler.setParameters(preparedStatement);
			// 执行 sql 语句,获取总条数
			ResultSet resultSet = preparedStatement.executeQuery();
			if (resultSet.next()) {
				page.setDataCount(resultSet.getInt(1));
			}

			// 改造后的 sql 语句
			String PageSql = sql + " limit " + page.getFirstIndex() + "," + page.getPageSize();
			// 把修改的 sql 语句替代原始的,BoundSql 没有修改 sql 的方法,只能用反射了
			// OGNL 表达式可以参考上面
			metaObject.setValue("delegate.boundSql.sql", PageSql);
		}

		// 返回主权,其实就是重新调用被拦截的方法
		return invocation.proceed();
	}

	// 用于读取注册时传入的参数
	@Override
	public void setProperties(Properties properties) {
		// TODO Auto-generated method stub

	}

}

最后,还需要在 MyBatis 的配置文件中注册该拦截器:

<!-- 注册拦截器 -->
<plugins>
	<plugin interceptor="top.Seiei.forMyBatis.interceptor.PageInterceptor"></plugin>
</plugins>
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值