MyBatis(应用篇)

MyBatis

一、安装MyBatis

如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:

	<dependency>
    	<groupId>mysql</groupId>
    	<artifactId>mysql-connector-java</artifactId>
    	<version>8.0.17</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.5</version>
    </dependency>

二、使用MyBatis

推荐使用XML构建SqlSessionFactory

配置mybatis-config.xml

<?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">
<!-- 
	  注意 XML 头部的声明,它用来验证 XML 文档的正确性。
	 environment 元素体中包含了事务管理和连接池的配置。
	 mappers 元素则包含了一组映射器(mapper),这些映射器的 XML 
	  映射文件包含了 SQL 代码和映射定义信息。 
-->
<configuration>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC"/>
			<dataSource type="POOLED">
				<property name="driver" value="${driver}"/>
				<property name="url" value="${url}"/>
				<property name="username" value="${username}"/>
				<property name="password" value="${password}" />
			</dataSource>
		</environment>
	
	</environments>
	<mappers>
		<mapper resource="mapper/StudentMapper.xml" />
	</mappers>
</configuration>

mapper.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="com.ttc.StudentMapper">

	<select id="selectStudentByID" resultType="com.ttc.pojo.StudentBO">
		select * from Student where id = #{id}
	</select>
	
	<select id="selectStudentByName" resultType="com.ttc.pojo.StudentBO">
		select * from student where name like "%"#{pname}"%"
	</select>
</mapper>

对于sql中like关键字的三种写法:

  1. “%”#{xxx}"%" 推荐:原因,可以预编译,可以防止SQL注入
  2. ‘%${xxx}%’
  3. concat(’%’,#{xxx},’%’)

Test类

@Test
	public void selectTest() throws IOException{
		// 2.SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得
		SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
		/* 3. SqlSessionFactoryBuilder 则可以从 XML 配置文件
		 	    或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。
		 	   从 XML 文件中构建 SqlSessionFactory 的实例非常简单,建议使用类路径下的
		 	   资源文件进行配置。 但也可以使用任意的输入流(InputStream)实例,比如用
		 	   文件路径字符串或 file:// URL 构造的输入流。MyBatis 包含一个名叫 
		 	  Resources 的工具类,它包含一些实用方法,使得从类路径或其它位置加载资源
		 	   文件更加容易。
		 */
		InputStream configResource = Resources.getResourceAsStream("mybatis-config.xml");
		// 1.每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的
		SqlSessionFactory factory = builder.build(configResource);
		/* 4.既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例
		 	 SqlSession 提供了在数据库执行 SQL 命令所需的所有方法
			 你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句
		*/
		// 参数设置为true, 为自动提交, 默认为false, 需要手动提交
		// sqlSession.commit();
		SqlSession sqlSession = factory.openSession();
		StudentBO student = sqlSession.selectOne("com.ttc.StudentMapper.selectStudentByID", 1);
		System.out.println(student);
		sqlSession.close();
	}

推荐 更简洁的方式——使用和指定语句的参数和返回值相匹配的接口:

代码不仅更清晰,更加类型安全,还不用担心可能出错的字符串字面值以及强制类型转换。

	StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
	Student student = mapper.selectById(1);

三、作用域和生命周期

注意: 对象生命周期和依赖注入框架
依赖注入框架(spring等)可以创建线程安全的, 基于事务的SqlSession和映射器, 并将它们注入到bean中, 因此可以直接忽略他们的生命周期
SqlSessionFactoryBuilder

一旦创建了 SqlSessionFactory,就不再需要它了。 因此SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

SqlSessionFactory

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
类似于线程池原理

SqlSession

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中

映射器实例

映射器是一些绑定映射语句的接口。映射器接口的实例是从 SqlSession 中获得的。虽然从技术层面上来讲,任何映射器实例的最大作用域与请求它们的 SqlSession 相同。但方法作用域才是映射器实例的最合适的作用域。 也就是说,映射器实例应该在调用它们的方法中被获取,使用完毕之后即可丢弃。 映射器实例并不需要被显式地关闭。尽管在整个请求作用域保留映射器实例不会有什么问题,但是你很快会发现,在这个作用域上管理太多像 SqlSession 的资源会让你忙不过来。 因此,最好将映射器放在方法作用域内。就像下面的例子一样:

try (SqlSession session = sqlSessionFactory.openSession()) {
 BlogMapper mapper = session.getMapper(BlogMapper.class);
 // 你的应用逻辑代码
}

MyBatis 详细配置

参考地址:https://mybatis.org/mybatis-3/zh/configuration.html#properties

一、mybatis-config.xml

settings配置

请详细阅读—>settings

typeAliases配置别名

定义在mybatis-config.xml中,后续都将使别名

	<typeAliases>
		<typeAlias type="com.ttc.pojo.StudentBO" alias="StudentBO"/>
		<typeAlias type="com.ttc.pojo.ProductsBO" alias="ProductsBO"/>
		<typeAlias type="com.ttc.pojo.ClassBO" alias="ClassBO"/>
	</typeAliases>

注意:踩到的小坑, 按照头文件标签顺序, 将typeAliases放在properties之后,不然configuration提示错误!!!

二、mapper.xml使用(select语句在映射和sql部分有使用示例)

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="">
</mapper>

sql

这个元素可以用来定义可重用的 SQL 代码片段,以便在其它语句中使用。

<!-- 静态 -->
<sql id="studentColumn">id, name, sex, age, password, cid</sql>
<select id="selectStudent" resultType="StudentBO">
SELECT <include refid="studentColumn" />
FROM STUDENT
WHERE ID = #{id}
</select>
<!-- 动态 -->
<sql id="studentColumn">#{alias}.id,#{alias}.name,#{alias}.sex,#{alias}.age,#{alias}.password,#{alias}.cid</sql>

<select id="selectStudent" resultType="StudentBO">
SELECT 
<include refid="studentColumn">
	<property name="alias" value="T1" />
</include>
FROM STUDENT T1
WHERE T1.ID = #{id}
</select>

结果映射和select

resultType

  1. 自动映射pojo的类型
<!-- 因为前面定义了别名, 这里resultType使用别名 -->
<select id="xxx" resultType="StudentBO">
SELECT * FROM STUDENT WHERE ID = #{id}
</select>
  1. resultType的弊端, 只能匹配数据库字段相同的命名, 日常开发中很多字段采用STUDENT_ID这种命名方式, 就会导致映射不到studentId

resultMap
resultMap元素我认为是mybatis最强大的元素了, 可以解决大部分映射, 但是性能越好意味着代码量越多, resultMap就需要进行单独的配置
resultMap的思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
JavaBean关系:

// 班级类, 包含了学生集合
public class ClassBO {
	private int cid;
	private String cname;
	private List<StudentBO> studentBOs;
	// getter&setter
}
// 学生类, 包含了学生作品类
public class StudentBO {
	private int id;
	private String name;
	private String sex;
	private int age;
	private String password;
	private ProductsBO productsBO;
	// getter&setter
}
// 作品类
public class ProductsBO {
	private int pid;
	private String pname;
	private int price;
	private Date pDate;
	private String sid;
	// getter&setter
}

从bean里面可以看出, 一个班级"有多个"学生, 一个学生"有一个"产品(作业);
分别对应了"一对多"和"一对一"

resultMap的两种对应关系

  1. resultMap引用resultMap
	<!-- class -->
	<resultMap type="ClassBO" id="classBO">
		<id property="cid" column="cid" />
		<result property="cname" column="cname"/>
		<collection property="studentBOs" column="id" resultMap="studentBO" />
	</resultMap>
	<!-- student -->
	<resultMap type="StudentBO" id="studentBO">
		<id property="id" column="id" />
		<result property="name" column="name" />
		<result property="age" column="age"/>
		<result property="sex" column="sex"/>
		<result property="password" column="password"/>
		<association property="productsBO" column="pid" javaType="ProductsBO" resultMap="productsBOS" />
	</resultMap>
	<!-- products -->
	<resultMap type="ProductsBO" id="productsBOS" >
		<id property="pid" column="pid" />
		<result property="pname" column="pname" />
		<result property="price" column="price" />
		<result property="pdate" column="pdate"/>
		<result property="sid" column="sid" />
	</resultMap>
  1. 一个resultMap
	<resultMap type="ClassBO" id="classBO">
		<id property="cid" column="cid" />
		<result property="cname" column="cname"/>
		<collection property="studentBOs" ofType="StudentBO">
			<id property="id" column="id" />
			<result property="name" column="name" />
			<result property="age" column="age"/>
			<result property="sex" column="sex"/>
			<result property="password" column="password"/>
			<association property="productsBO" column="pid" javaType="ProductsBO" >
				<id property="pid" column="pid" />
				<result property="pname" column="pname" />
				<result property="price" column="price" />
				<result property="pdate" column="pdate"/>
				<result property="sid" column="sid" />
			</association>
		</collection>
	</resultMap>
	<!-- 查询语句 -->
	<select id="selectClass" resultMap="classBO">
		select * from
		class c left join 
		(select * from student s left join products p on s.id = p.pid) d
		on c.cid = d.cid
		where c.cid = #{id}
	</select>

比较: 我个人喜欢第一种方式,方便后续其他查询使用到对应的resultMap

resultMap可以继承 如下代码:

	<resultMap type="ClassBO" id="classBO1">
		<id property="cid" column="cid" />
		<result property="cname" column="cname"/>
	</resultMap>
	<resultMap type="ClassBO" id="classBO2" extends="classBO1">
		<collection property="studentBOs" column="id" resultMap="studentBO" />
	</resultMap>

属性说明:
property一定要与类中属性的名字匹配


标签说明:
id: 相当于表中字段的主键, 标记出作为 ID 的结果可以帮助提高整体性能
result: 注入到字段或 JavaBean 属性的普通结果
association: 一个复杂类型的关联, 比如StudentBO中包含了ProductsBO
collection: 一个复杂类型的集合, 比如ClassBO中包含了List< StudentBO>
其他标签说明:
constructor: 用于在实例化类时,注入结果到构造方法中
pojo

public class ClassBO{
	private int cid;
	private String cname;
	public ClassBO(int cid, String cname){
		// ````
	}
}

resultMap
说明:

  1. idArg与arg类似id与result, 都是方便主键标记, 提高性能
  2. javaType采用的是mybatis自定义的别名:string对应java.lang.String, int对应的是包装类Integer
  3. 指定name: 如果不指定, 将按构造器参数的顺序执行, 指定后顺序可以打乱, 自动匹配构造器属性名相同的参数
<constructor>
   <idArg column="cid" javaType="int" name="cid" />
   <arg column="cname" javaType="string" name="cname" />
</constructor>

discriminator: 又叫鉴别器,一个鉴别器的定义需要指定 column 和 javaType 属性, 类似与java代码中的switch语句, 根据返回结果, 判断用哪个resultMap

<resultMap id="vehicleResult" type="Vehicle">
  <id property="id" column="id" />
  <result property="vin" column="vin"/>
  <discriminator javaType="int" column="vehicle_type">
    <case value="1" resultMap="carResult"/>
    <case value="2" resultMap="truckResult"/>
  </discriminator>
</resultMap>

动态SQL

if: 最常使用场景在于动态拼接where语句

<select id="selectById" resultType="StudentBO">
	select * from student
	where
	<if test="id != null">
		id = #{id}
	</if>
	<if test="sex != null">
		AND SEX = #{sex}
	</if>
</select>

trim, where, set: 如果上面id语句不成立, SQL将变成:

SELECT * FROM STUDENT WHERE AND SEX = '男'
-- 或
SELECT * FROM STUDENT WHERE

为了解决这种问题, mybatis提供了where元素: where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

<select id="selectByCondition" resultType="StudentBO">
	select * from student
	<where>
		<if test="sex!=null">
			sex = #{sex}
		</if>
		<if test="name != null">
			AND name like "%"#{name}"%"
		</if>
	</where>
</select>

自定trim, 与where有类似的功能, 主要为了实现自定义

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  	<if test="sex!=null">
		sex = #{sex}
	</if>
	<if test="name != null">
		AND name like "%"#{name}"%"
	</if>
</trim>

动态更新: set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

<update id="updateStudent">
  update Student
    <set>
      <if test="name!= null">name=#{name},</if>
      <if test="password != null">password=#{password},</if>
      <if test="sex!= null">sex=#{sex},</if>
      <if test="age != null">age=#{age}</if>
    </set>
  where id=#{id}
</update>

自定义trim, 与set类似

<trim prefix="SET" suffixOverrides=",">
  <if test="name!= null">name=#{name},</if>
  <if test="password != null">password=#{password},</if>
  <if test="sex!= null">sex=#{sex},</if>
  <if test="age != null">age=#{age}</if>
</trim>

chose, when, otherwise: 类似于switch语句

<select id="selectByCondition" resultType="StudentBO">
	select * from student
	where
	<chose>
		<when test="sex!=null">
			sex = #{sex}
		</when>
		<when test="name != null">
			name like "%"#{name}"%"
		</when>
		<otherwise>
			cid = 1
		</otherwise>
	</chose>
</select>

insert(update 和 delete可参考)

这里只举例插入多条数据
两点说明:

  1. 设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置为目标属性, 可以返回自动生成的主键(仅适用于insert和update)。
  2. 当insert和update的时候, 某字段可以设置为null,则必须设置jdbcType
	<insert id="insertStudentList" useGeneratedKeys="true" keyProperty="id" parameterType="com.ttc.pojo.StudentBO">
		insert into student (name, sex, age, password) values
		<foreach item="item" collection="list" separator=",">
			(#{item.name,jdbcType=VARCHAR}, #{item.sex}, #{item.age}, #{item.password})
		</foreach>
	</insert>

自动映射

通常数据库列使用大写字母组成的单词命名,单词间用下划线分隔;而 Java 属性一般遵循驼峰命名法约定。为了在这两种命名方式之间启用自动映射,需要将 mapUnderscoreToCamelCase 设置为 true。
在这种情况下,对于每一个结果映射,在 ResultSet 出现的列,如果没有设置手动映射,将被自动映射。在自动映射处理完毕后,再处理手动映射。
有三种自动映射等级:

  • NONE - 禁用自动映射。仅对手动映射的属性进行映射。
  • PARTIAL - 对除在内部定义了嵌套结果映射(也就是连接的属性)以外的属性进行映射
  • FULL - 自动映射所有属性。

默认值是 PARTIAL,这是有原因的。当对连接查询的结果使用 FULL 时,连接查询会在同一行中获取多个不同实体的数据,因此可能导致非预期的映射。
无论设置的自动映射等级是哪种,你都可以通过在结果映射上设置 autoMapping 属性来为指定的结果映射设置启用/禁用自动映射。

<resultMap id="userResultMap" type="User" autoMapping="false">
  <result property="password" column="hashed_password"/>
</resultMap>

缓存

mybatis官方文档讲的很详细—>点击这里, 这里不做理解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值