关注“Java后端技术全栈”
回复“面试”获取全套面试材料
MyBatis
提供了XML配置和注解配置两种形式。明天就来搞搞这两种形式是如何实现的。
MyBatis
的真正弱小在于它的语句映射,这是它的魔力所在。因为它的异样弱小,映射器的 XML 文件就显得绝对简略。如果拿它跟具备雷同性能的JDBC
代码进行比照,你会立刻发现省掉了将近 95% 的代码。MyBatis
致力于缩小应用老本,让用户能更专一于SQL
代码。
来自官网。
Mybatis
映射九个顶级元素:
- mapper:映射文件的根节点,只有一个属性namespace(命名空间),作用如下:
- 用于辨别不同的mapper,全局惟一。
- 绑定
DAO
接口,即面向接口编程,当绑定一个接口,就不必写此接口的实现类,会通过接口的齐全限定名找到对应的mapper配置来执行SQL
语句,所以,namespace
的命名必须要写接口的齐全限定名。 - cache:配置给定命名空间的缓存。
- cache-ref:从其余命名空间援用缓存配置。
resultMap
:用来形容数据库后果集和对象的对应关系。sql
:能够重用的SQL
块,也能够被其余语句援用。通常时寄存一些专用性的SQL
。- insert:映射插入语句。
- update:更新映射语句。
- delete:删除映射语句。
- select:映射查问语句。
xml形式
九个顶级映射元素对应标签:
`<mapper namespace="com.tian.mybatis.mapper.UserMapper">` `<resultMap id="" type=""></resultMap>` `<sql id=""></sql>` `<cache blocking="" ></cache>` `<cache-ref namespace=""></cache-ref>` `<select id="selectUserById"></select>` `<insert id="insert" ></insert>` `<update id=""></update>` `<delete id=""></delete>``</mapper>`
select详解
能够看得出,前面可选项还是蛮多的。上面是官网对每项的解释。
select应用案例
`<?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.tian.mybatis.mapper.UserMapper">` `<select id="selectUserById" resultType="com.tian.mybatis.entity.User" parameterType="int" >` `select * from m_user where id = #{id}` `</select>``</mapper>`
- id必须在这个Mapper中是惟一的,能够被用来援用这条语句 ,这个id必须与只对应的是
XxxMapper.java
中的办法,必须是一一对应。 - 返回类型:User类型,
resultType
:查问语句返回后果类型的齐全限定名或别名。别名应用形式和parameterType
是一样的。 - 参数:整形,示意查问语句传入参数的类型和齐全限定名或别名。反对根底数据类型和简单数据类型。
{参数名}:通知MyBatis
生成的PreparedStatement
参数,绝对于JDBC中
,改参数被标识为‘?’。
别名与参数映射类型如下:
返回类型中别名的应用,留神:
如果是咱们的entity类,那么resultType
是无奈应用别名的,只能应用resultMap
才能够应用别名。
`<?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.tian.mybatis.mapper.UserMapper">` `<resultMap id="User" type="com.tian.mybatis.entity.User"/>` `<select id="selectUserById" resultMap="User" parameterType="int" >` `select * from m_user where id = #{id}` `</select>``</mapper>`
然而如果应用的下面映射表里,也能够间接应用别名。
数据库里有两条数据:
UserMapper.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.tian.mybatis.mapper.UserMapper">` `<select id="countUser" resultType="int">` `select count(1) from m_user` `</select>``</mapper>`
UserMapper.java
`import com.tian.mybatis.entity.User;``public interface UserMapper {` `int countUser();``}`
测试类:
`public class MybatisApplication {` `public static final String URL = "jdbc:mysql://localhost.com:3306/mblog?useUnicode=true";` `public static final String USER = "root";` `public static final String PASSWORD = "123456";` `public static void main(String[] args) {` `String resource = "mybatis-config.xml";` `InputStream inputStream = null;` `SqlSession sqlSession = null;` `try {` `inputStream = Resources.getResourceAsStream(resource);` `//工厂模式` `SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);` `//获取sql操作会话` `sqlSession = sqlSessionFactory.openSession();` `//结构对象(这里比拟非凡,这里结构对象的形式前面会专门分享)` `UserMapper userMapper = sqlSession.getMapper(UserMapper.class);` `//查问统计` `System.out.println(userMapper.countUser());` `} catch (Exception e) {` `e.printStackTrace();` `} finally {` `try {` `inputStream.close();` `} catch (IOException e) {` `e.printStackTrace();` `}` `sqlSession.close();` `}` `}``}`
输入:2
当数据库表中的字段名和咱们entity中的字段名不统一,怎么解决?
在理论开发中,这种常见是在劫难逃。咱们能够应用上面的这种形式解决。
实体类User
`public class User {` `private Integer id;` `private String userName;` `private Integer age;` `//set get toString办法这里就不贴了``}`
UserMapper.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.tian.mybatis.mapper.UserMapper">` `<resultMap id="User" type="com.tian.mybatis.entity.User">` `<id column="id" property="id"/>` `<result column="name" property="userName"/>` `</resultMap>` `<select id="selectUserById" resultMap="User" parameterType="int" >` `select * from m_user where id = #{id}` `</select>``</mapper>`
- type:对应的是咱们的实体类,全路径名。
- id:能够了解为别名。
- id:惟一标识,此id值用于select元素
resultMap
属性的援用。 - column:对应咱们数据库表中的字段名称。
- property:对应咱们的实体类的属性,比方:User中的属性userName,要和数据库表m\_user中的name对应。
- result:标识一些简略属性,其中column属性代表数据库的字段名,property代表查问进去的字段名映射到实体类的某个属性。
持续应用咱们后面的测试类进行测试:
`UserMapper userMapper = sqlSession.getMapper(UserMapper.class);``System.out.println(userMapper.selectUserById(1));`
输入:User{id=1, userName='tian', age=22}
留神:实体类的get set
和toString()
办法这里给省略, 心愿大家在应用的应用,应用快捷键很简略的就搞定了。
下面提到过resultType
和resultMap
,那么他们两到底有什么区别呢?
resultType和resultMap 有什么区别?
resultType
:间接示意返回类型, 包含根本数据类型和简单数据类型。resultMap
:内部resultMap
定义的援用,通过对应的内部resultMap
的id,示意后果映射到哪个resultMap
上,个别用于字段名和属性名不统一的状况,或者须要做简单的联结查问以便自在管制映射后果。
两者的关联
当进行查问时,查问进去的每个字段都会放在一个Map里,当查问元素返回属性是resultType
的时候,会将键值对取出赋所指定的属性。其实MyBatis
的每个查问映射的返回类型都是resultMap
,只是当咱们应用resultType
的时候,会主动把对应的值赋给所指定的对象属性,当应用resultMap
时候,因为map不是很好的示意畛域,咱们就进一步的转化为对应的实体对象。resultMap
次要作用于简单的联结查问上。
resultMap
的主动映射级别:默认级别为PARTIAL,也能够在settings更改值。
留神:resultType
和resultMap
实质是一样的,都是Map数据结构,然而二者不能同时存在。
增删改案例
insert
从这里能够晓得,对于减少insert是没有返回值类型能够让咱们指定的。默认返回int类型。
`<insert id="insert" parameterType="com.tian.mybatis.entity.User">` ``INSERT INTO m_user(`name`,age) VALUES ( #{userName},#{age})```</insert>`
对应Mapper中的办法
`int insert(User user);`
另外的update和delete相似,这里就没有必要逐个演示了。
注解形式
九个顶级映射元素对应注解:
其余局部注解是配合九个注解进行应用的。
select注解
把本地的UserMapper.xml删掉,而后改一下mybatis-config.xml
,把其中的UserMapper.xml给正文掉。增加
`<mapper class="com.tian.mybatis.mapper.UserMapper"/>`
UserMapper.java增加注解
`public interface UserMapper {` `@Select("select * from m_user where id = #{id}")` `User selectUserById(Integer id);``}`
再次测试
`User user = sqlSession.selectOne("com.tian.mybatis.mapper.UserMapper.selectUserById", 1);``System.out.println(user);`
输入:
User{id=1, userName='null', age=22}
从输入内容看到,userName为null,这也是因为和数据库表汇中的字段name不统一导致的,那么如何解决呢?
这么搞,再增加一个注解:
`public interface UserMapper {` `@Select("select * from m_user where id = #{id}")` `@Results( @Result(column = "name",property = "userName"))` `User selectUserById(Integer id);``}`
输入:
User{id=1, userName='tian', age=22}
这样也就是在应用注解的时候,解决实体属性名和数据库表字段名不一样的问题的方法。
insert、update、delete同样也能够应用注解来搞定了。
@Insert、@Update、@Delete配上相应的SQL语句。
注解和xml是否能够共存?
`<update id="updateAuthorIfNecessary">` `update m_user` `<trim prefix="SET" suffixOverrides=",">` `<if test="userName != null and userName != ''">` `` `name` = #{userName},`` `</if>` `<if test="gender != null and gender != 0">` `gender = #{gender},` `</if>` `<if test="age != null and age != 0">` `age = #{age},` `</if>` `</trim>` `where id=#{id}` `</update>`
同时在UserMapper.java中的办法上增加注解
``@Update("update m_user set `name` = #{userName},gender = #{gender},age = #{age} where id=#{id}")```int updateAuthorIfNecessary(User user);`
再次中子星的时候回报异样的:
`nested exception is java.lang.IllegalArgumentException:``Mapped Statements collection already contains value for com.tian.mybatis.mapper.UserMapper.updateAuthorIfNecessary.` `please check file [D:\workspace\my_code\mybatis\target\classes\mapper\UserMapper.xml] and com/tian/mybatis/mapper/UserMapper.java (best guess)`
大抵意思就是说,曾经存在了,即就是不能同时应用xml和注解。二者选其一。
xml能够喝注解联合应用,然而得保障同一个办法不能同时存在xml和注解。
倡议
简略的sql解决能够应用注解,简单的sql应用xml。然而理论工作还得看你待的我的项目中有没有对这个进行规范化。
在我的项目中无非就三种:
1.全副必须应用xml形式。
2.全副必须应用注解形式。
3.能够同时应用xml和注解。
高级映射
association
映射到JavaBean
的某个简单的”数据类型”属性,仅解决一对一的关联关系。
`<resultMap type="com.tian.mybatis.entity.User" id="userMapRole">` `<id column="id" property="id" />` `<result column="name" property="userName" />` `<result column="age" property="age" />` `<association property="role" javaType="UserRole">` `<id column="id" property="id" />` `<result column="roleName" property="roleName" />` `</association>``</resultMap>`
association的属性节点:
- property:映射数据库列的实体对象属性名。
javaType
:残缺的java
类名和限定名。propert所映射的属性的类型。
子元素
- id:个别为映射主键,能够进步性能。
- result:
- column:映射的数据库的字段名。
- property:映射的数据列对应的实体对象属性。
collection
映射到JavaBean的某个简单的”数据类型”属性,这个属性是一个汇合列表,解决一对多的关联关系。
`<resultMap type="com.tian.mybatis.entity.User" id="userMapAddress">` `<id column="id" property="id"/>` `<result column="name" property="userName"/>` `<collection property="lists" ofType="UserAddress">` `<id column = "id" property = "id">` `<result column="addressDesc" property="addressDesc"/>` `</collection>` `</resultMap>`
ofType
:残缺的Java类名和限定名。propert所映射的属性的类型。
其余和association基本一致。
association和collection都具备提早加载性能。
提早加载:先从单表查问,须要时再查关联表,大大的进步了数据库性能,因为相对来说单表查问比多表查问要快。
xml和注解的关系
下面咱们曾经讲了两种形式的实现,上面来比照一下,两种形式的关系:
xml形式
必须有个一个XxxMapper.xml与之对应,办法名对应xml中的id,办法入参和办法出参都必须对应起来,很容易出问题。咱们在开发的时候有的是能够应用代码生成器生成,然而有的是必须本人手写,有的公司也是要求必须手写,所以这里须要留神。
注解形式
不须要XxxMapper.xml
文件,只须要在对应XxxMapper.java
中的办法上加上注解就搞定了,然而这里是有坑的。毕竟把sql放到了咱们的Java代码里了。
优缺点
xml形式: 减少了xml文件,批改麻烦,条件不确定(ifelse判断),容易出错,非凡转义字符比方大于小于 。
注解形式:简单sql不好用,收集sql不不便,治理不不便,批改需从新编译
总结
本文讲述了Mybatis
的两种映射形式,以及一些留神点,一些关系和区别。
实体属性名和数据库表字段名不一样的状况下,xml和注解别离是如何解决的。resultType和resultMap的区别。
举荐浏览
6000多字 | 秒杀零碎设计留神点【实践】
面试官:说说你对Java异样的了解