SQL映射详解

1.1SQL映射文件
SQL映射是MyBatis框架最具特色的部分,功能强大且使用简单。MyBatis框架的主要思想是讲SQL语句从程序代码中分离出来,对JDBC访问数据库的代码进行封装,从程序中消除了所有的SQL参数设置及处理结果集的JDBC代码,从而大幅减少数据访问层的编码量。

SQL映射文件中的几个顶级元素介绍如下:
①mapper:SQL映射文件的根元素。只有一个属性namespace,用于区分不同的mapper,必须全局唯一。

②cache:为给定命名空间配置缓存

③cache-ref:引用其他命名空间中的缓存配置。

④resultMap:用来描述查询结果集中的字段和java实体类属性的对应关系

⑤sql:定义可重用的SQL语句快,可以在其他语句映射中引用,提高编写和维护SQL语句的效率

⑥insert:映射inset语句

⑦update:映射update语句

⑧delete:映射delete语句

⑨select:映射select语句

SQL映射文件的开发需要注意以下规则:
①习惯上,SQL映射文件与该Mapper接口同名(实体类名+Mapper),并放置在同一包路径下。

②以要映射的Mapper接口的完全限定名(即包含包名的完整名称)作为namespace属性的值。

③接口中的方法名与映射文件中SQL语句映射的ID一一对应。MyBatis框架通过namespace+ID确定和接口方法绑定的SQL语句。

④在不同的SQL映射文件中,子元素的ID可以相同。

1.2MyBatis框架的条件查询
1.2.1实现单一条件查询
(1)在接口中添加查询方法:
/**
 * 根据用户的真实姓名模糊匹配查询用户,参数realName为需要传递给SQL语句的查询条件
 */
public List<SysUser> getUserByRealName(String realName);

(2)在xml文件中添加SQL语句映射:

<select id="getUsersByRealName" resultType="sysuser" parameterType="string">
    select * from t_sys_user where realName like concat('%',#{param},'%')
</select>

(3)在测试类中添加测试方法: 

......//省略创建SqlSession的代码
List<SysUser> userList = sqlSession.getMapper(接口类名.class).getUserByRealName("李");
......//省略输出查询结果的代码

1.2.2实现多条件查询
有三种实现方法
1.将查询条件封装成java对象作为入参
2.将查询条件封装成Map对象作为入参
3.使用@Param注解时间多参数入参
在映射的SQL语句中使用默认命名表意不直观,且调整顺序容易出错,所以推荐使用@Param注解为参数命名。例如@Param(“realName”)String realName,相当于改参数的可以命名为realName,便于在映射的SQL中使用。

1.2.3MyBatis框架的结果映射
使用mybatis,有两个属性标签<resultType>、<resultMap>可以提供结果映射

虽然resultType 属性在大部分情况下都够用,但是在一些特殊情况下无能为力,比如属性名和列名不一致,为一些连接的复杂语句编写映射代码。

遇到这些情况,我们要使用<resultMap>标签,一份 <resultMap> 能够代替实现同等功能的数千行代码。

1.2.3.1使用resultMap元素自定义结果映射
MyBatis框架使用resultMap元素来自定义结果映射。

resultMap 元素是 MyBatis 中最重要最强大的元素。
resultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。

属性名和列名不一致
这是开发过程中常见的情境,JavaBean 属性使用驼峰命名,数据库列名单词之间加入下划线。

public class User {
  private int id;
  private String username;
  private String hashedPassword;
 
  public int getId() {
    return id;
  }
  public void setId(int id) {
    this.id = id;
  }
  public String getUsername() {
    return username;
  }
  public void setUsername(String username) {
    this.username = username;
  }
  public String getHashedPassword() {
    return hashedPassword;
  }
  public void setHashedPassword(String hashedPassword) {
    this.hashedPassword = hashedPassword;
  }
}

<select id="selectUsers" resultType="User">
  select
    user_id,user_name,hashed_password
  from some_table
  where id = #{id}
</select>

 高级结果映射
MyBatis 创建时的一个思想是:数据库不可能永远是你所想或所需的那个样子。

我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们并不都是那样。

如果能有一种数据库映射模式,完美适配所有的应用程序,那就太好了,可惜没有。 而 ResultMap 就是 MyBatis 对这个问题的答案。

一对一映射

比如,我们如何映射下面这个语句?

<select id="selectBlogDetails" resultMap="detailedBlogResultMap">
  select
       B.id as blog_id,
       B.title as blog_title,
       B.author_id as blog_author_id,
       A.id as author_id,
       A.username as author_username,
       A.password as author_password,
       A.email as author_email,
       A.bio as author_bio,
       A.favourite_section as author_favourite_section
  from Blog B
  left outer join Author A on B.author_id = A.id
  where B.id = #{id}
</select>

这是典型的一对一的关联关系情况,我们通过<association>配置就可以解决这个问题。

<resultMap id="detailedBlogResultMap" type="Blog">
  <constructor>
    <idArg column="blog_id" javaType="int"/>
  </constructor>
  <result property="title" column="blog_title"/>
  <association property="author" javaType="Author">
    <id property="id" column="author_id"/>
    <result property="username" column="author_username"/>
    <result property="password" column="author_password"/>
    <result property="email" column="author_email"/>
    <result property="bio" column="author_bio"/>
    <result property="favouriteSection" column="author_favourite_section"/>
  </association>
</resultMap>

接下来,我们一步一步来看这些元素的意义。 
reslutMap 的属性
id:当前命名空间中的一个唯一标识,用于标识一个结果映射。
type:类的完全限定名, 或者一个类型别名。

id和result
 id 和 result 元素都将一个列的值映射到一个简单数据类型(String, int, double, Date 等)的属性或字段。

这两者之间的唯一不同是,id 元素对应的属性会被标记为对象的标识符,在比较对象实例时使用。 这样可以提高整体的性能,尤其是进行缓存和嵌套结果映射(也就是连接映射)的时候。

两者的一些属性:

property:映射到列结果的字段或属性。
column:数据库中的列名,或者是列的别名。
javaType:一个 Java 类的全限定名,或一个类型别名。通常不会配置,mybatis 能够根据参数信息自动识别,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。
jdbcType:JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。

属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。
resultType期望从这条语句中返回结果的类全限定名或别名。 如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。
resultMap对外部 resultMap 的命名引用。 resultType 和 resultMap 之间只能同时使用一个。
flushCache将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。
useCache将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
fetchSize这是一个给驱动的建议值,尝试让驱动程序每次批量返回的结果行数等于这个设置值。 默认值为未设置(unset)(依赖驱动)。
statementType可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
resultSetTypeFORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖数据库驱动)。
resultOrdered这个设置仅针对嵌套结果 select 语句:如果为 true,将会假设包含了嵌套结果集或是分组,当返回一个主结果行时,就不会产生对前面结果集的引用。 这就使得在获取嵌套结果集的时候不至于内存不够用。默认值:false
resultSets这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔。

insert, update 和 delete

数据变更语句 insert,update 和 delete 的实现非常接近:

复制代码

<insert
  id="insertAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  keyProperty=""
  keyColumn=""
  useGeneratedKeys=""
  timeout="20">
​
<update
  id="updateAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  timeout="20">
​
<delete
  id="deleteAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  timeout="20">

复制代码

Insert, Update, Delete 元素的属性详解:

属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType将会传入这条语句的参数的类全限定名或别名。
flushCache将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:(对 insert、update 和 delete 语句)true。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
statementType可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
useGeneratedKeys(仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。
keyProperty(仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset)。如果生成列不止一个,可以用逗号分隔多个属性名称。
keyColumn(仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。

主键返回<selectKey>

插入一条新数据时,有两种方法确定最新的主键值:

  • UUID(非自增主键返回)

在执行插入语句前获取最新的主键值

复制代码

    <!-- mysql的uuid生成主键 -->
<mapper namespace="mybatis_test.ManMapper">
    <insert id="insertUser" parameterType="mybatis_test.Man">
        <selectKey keyProperty="id" order="BEFORE" resultType="string">
            <!--去除'-'和空格-->
            select replace(uuid(), '-', '') 
        </selectKey>
        insert into man(id,name) value(#{id},#{name})
    </insert>
</mapper>

复制代码

  • LAST_INSERT_ID()(自增主键返回)

通过LAST_INSERT_ID()获取刚插入记录的自增主键值,在insert语句执行后,执行select LAST_INSERT_ID()就可以获取自增主键

SQL执行语句返回的是影响的行数,为1或者0,主键id已经存到了man对象的“id”字段中,所以应该从对象中获取

    <insert id="insertUser" parameterType="mybatis_test.Man">
        insert into man(name) value(#{name})
        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">
            select LAST_INSERT_ID()
        </selectKey>
    </insert>

代码

            //这里返回的是影像的行数,为1或者0
            manMapper.insertUser(man);
            sqlSession.commit();
            //主键id已经存到了man对象的“id”字段中,所以应该从对象中获取
            System.out.println(man.getId());

selectKey 标签属性

属性描述
keyPropertyselectKey 语句结果应该被设置的目标属性。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
keyColumn匹配属性的返回结果集中的列名称。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
resultType结果的类型。MyBatis 允许任何简单类型用作主键的类型,包括字符串。如果希望作用于多个生成的列,则可以使用一个包含期望属性的 Object 或一个 Map。
order可选值BEFORE和AFTER,设置为BEFORE会先执行selectKey语句,再执行insert语句;设置为AFTER会先执行insert语句再执行selectKey语句

SQL片段

用来定义可重用的 SQL 代码片段,以便在其它语句中使用。 参数可以静态地(在加载的时候)确定下来,并且可以在不同的 include 元素中定义不同的参数值。

定义

<sql id="userColumns"> ... </sql>

在增删查改的语句中使用 include标签 使用SQL片段

参数(占位符)

传入简单参数,如 int ,String等,不用写参数类型(parameterType),如果传入的是对象,就需要写

在Mybatis中,有两种占位符

  • #{} 解析传递进来的参数数据

  • ${} 对传递进来的参数原样拼接在SQL中

结果映射<resultMap>

简单映射

  • 显示配置resultMap

<resultMap type="mybatis_test.Student" id="citiesMap">...</resultMap>
<select id="findById" parameterType="int" resultMap="citiesMap">...</select>

select标签的resultMap属性=显示配置的resultMap标签的id属性

  • 不配置 resultMap

    • resultType属性为类的全限定名(需要保证表字段和类属性相同,如果不同,可以在select语句中设置别名select user_city as usercity from... 或者显示配置resultMap做字段和属性的对应关系)

    <select id="findById" parameterType="int" resultType="mybatis_test.Student">...</select>
    • resultType属性为类的别名

<!-- mybatis.xml 中 -->
<typeAlias type="mybatis_test.Studentr" alias="Studentr"/>
​
<!-- SQL 映射 XML 中 -->
<select id="selectUsers" resultType="Studentr">...</select>

高级结果映射

需要显示配置 resultMap

resultMap的属性

属性描述
id当前命名空间中的一个唯一标识,用于标识一个结果映射。
type类的完全限定名, 或者一个类型别名。
autoMapping如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)。

resultMap的子标签

  • constructor - 用于在实例化类时,注入结果到构造方法中

    • idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能

    • arg - 将被注入到构造方法的一个普通结果

  • id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能

  • result – 注入到字段或 JavaBean 属性的普通结果

  • association – 一个复杂类型的关联;许多结果将包装成这种类型

    • 嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用

  • collection – 一个复杂类型的集合

    • 嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用

  • discriminator – 使用结果值来决定使用哪个 resultMap

    • case – 基于某些值的结果映射

      • 嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射

id和result的属性

属性描述
property映射到列结果的字段或属性。如果 JavaBean 有这个名字的属性(property),会先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。 无论是哪一种情形,你都可以使用常见的点式分隔形式进行复杂属性导航。 比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。
column数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。
javaType一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。
jdbcTypeJDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。
typeHandler我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。

constructor 的属性

属性描述
column数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。
javaType一个 Java 类的完全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。
jdbcTypeJDBC 类型,所支持的 JDBC 类型参见这个表格之前的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可能存在空值的列指定这个类型。
typeHandler我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的完全限定名,或者是类型别名。
select用于加载复杂类型属性的映射语句的 ID,它会从 column 属性中指定的列检索数据,作为参数传递给此 select 语句。具体请参考关联元素。
resultMap结果映射的 ID,可以将嵌套的结果集映射到一个合适的对象树中。 它可以作为使用额外 select 语句的替代方案。它可以将多表连接操作的结果映射成一个单一的 ResultSet。这样的 ResultSet 将会将包含重复或部分数据重复的结果集。为了将结果集正确地映射到嵌套的对象树中,MyBatis 允许你 “串联”结果映射,以便解决嵌套结果集的问题。想了解更多内容,请参考下面的关联元素。
name构造方法形参的名字。从 3.4.3 版本开始,通过指定具体的参数名,你可以以任意顺序写入 arg 元素。参看上面的解释。
  • 7
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值