2

记录一下从hibernate 改成 mybatis 遇到的一些问题

小问题一

运行的时候突然发现的报错:

lALPJwKt15YT0yHNAbzNB00_1869_444.png

检查过几遍之后才发现原因: 之前的时候不小心在后面增加了一个斜杠

lQLPJxbo6TArbmbNAdbNA0SwNaEyiX1gdWwDfqrhxcBAAA_836_470.png

由于编译器不会对 sql 语句进行检查, 所以运行后台的时候并没有报错。所以当时没发现。

而过几天之后,当运行到这条sql 语句的时候, 后台才报了错。

所以,我们写sql 语句的时候要小心。

以防我们写的不对, 还可以进入mysql 执行一下这条语句看看结果。

image.png

发现问题

背景: 分页的时候发现这三列数据为空

效果图:
lQLPJxbo6TH1MzPNAVXNAnWw2qQNvPSVSDMDfqrk1oDsAA_629_341.png

排查问题:

内部编号这一列的问题很快就排查了出来:

后台与数据库字段名不一致

数据库:
image.png


后台实体:
image.png

所以在我们执行下面这条语句时,在进行实体映射的时候对应不上, 所以为null

 <select id="findAll" resultType="equipmentManagementSystem.entity.Equipment">
        select * from equipment
  </select>

以往的解决办法

我想起了以前 前后台字段不对应时 的解决办法:

@JsonProperty("internal_number")
private String internalNumber;

然而这个注释并没有用。

因为这个注解是json序列化时,序列化为另一个名称。 前后台就是通过 json 传递数据信息。

而目前情况是后台与数据库的交互, ORM 把数据库映射成对象, 用不到Json。

所以此时我们需要用到myBatis的另一个标签 resultMap

resultMap

resultMap属于mybatis返回操作结果的一个标签,可以用来映射select查询出来结果的集合。

主要作用是将实体类中的字段与数据库表中的字段进行关联映射

resultMap 属性:

属性描述
id当前命名空间中的一个唯一标识,用于标识一个结果映射。
type类的完全限定名
extends可以继承其他resultMap的一些写好的属性

result属性:

属性描述
id一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
result注入到字段或 JavaBean 属性的普通结果
association一个复杂类型的关联;许多结果将包装成这种类型
collection一个复杂类型的集合

例子:

<mapper namespace="equipmentManagementSystem.Mybatis.EquipmentMapper">
    <!--type指向javabean类,id可自定义 -->
        <resultMap id="equipmentMap" type="equipmentManagementSystem.entity.Equipment">
            <id column="id" property="id"></id>
             <!-- property对应实体类的属性名称,column为数据库字段名 -->
            <result column="name" property="name"/>
            <result column="model" property="model"/>
            <result column="place" property="place"/>
            <result column="states" property="states"/>
            <result column="sale_time" property="saleTime"/>
            <result column="internal_number" property="internalNumber"/>
        </resultMap>
    <select id="findAll" resultMap="equipmentMap">
        select * from equipment
    </select>
</mapper>

这个时候, 我们就可以在 column 属性写上 实体名,在 property 属性写上数据库名。
最后就映射成功。在列表中可以看到这列。

image.png

-

剩下两列还为空。 原因是: 剩下两个属性都是对象

image.png

image.png

因为我们在数据库中存的是 主键

所以我们没办法使用, result标签来进行简单映射。 否则, 我们只得到的是type_id 这个字段, 而不是 一个对象

<result column="type_id" property="tyoe"/>

这时候就需要用到上面提到过的 association 标签了。

association 一对一

它其实就相当于: 一对一OneToOne

用法有两种:

1. 嵌套结果 resultMap

第5行是重点, 和result标签一样的,property 代表 java 实体的属性名, column 代表的 数据库的字段名。

resultMap 属性值,给的是该对象对应的resultMap。

简单来说,我们用一个resultMap 映射了该属性对象。让myBatis帮我们自动装配对象

1   <resultMap id="equipmentMap" type="equipmentManagementSystem.entity.Equipment">
2            <!-- property对应实体类的属性名称,column为数据库字段名 -->
3            <id column="id" property="id"></id>
4            <result column="internal_number" property="internalNumber"/>
5            <association property="type" column="type_id" resultMap="equipmentManagementSystem.Mybatis.TypeMapper.typeResultMap"/>
6        </resultMap>
7    <select id="findAll" resultMap="equipmentMap">
8        select * from equipment
9    </select>

image.png

2. 嵌套查询 select

image.png

1 <resultMap id="equipmentMap" type="equipmentManagementSystem.entity.Equipment">
2    <!-- property对应实体类的属性名称,column为数据库字段名 -->
3    <id column="id" property="id"></id>
4    <result column="sale_time" property="saleTime"/>
5    <result column="internal_number" property="internalNumber"/>
6    <association property="type" column="type_id" select="equipmentManagementSystem.Mybatis.TypeMapper.findByid"/>
7 </resultMap>
8 <select id="findAll" resultMap="equipmentMap">
9    select * from equipment
10 </select>

第6行的 select 语句 调用的是 TypeMapper 下的findByid方法。

简单来说, 我们调用了一个sql语句, 把type_id 查询成 type对象

  <select id="findById" resultMap="typeResultMap">
        select *
        from type
        where id=#{id}
    </select>

不过,嵌套查询会带来 n+1 问题。 而嵌套结果不会, 所以推荐 第一种。

什么是 n+1 问题

N+1问题就是这样产生的:查询主表是一次,查询出N 条记录,根据这N 条记录,查询关联的副(从)表,共需要N 次。所以,应该叫1+N 问题更合适一些。

实际上, 在hibernate 中也默认有这种问题。

@ManyToOne标注的fetch的默认值是fetchType.EAGER

这时,hibernate除了发出查询多的一方对应的表的记录的sql语句外,还会发出n(多方记录数)条sql语句,这就是1+n问题。

既然在hibernate中有这种问题。 根据目前的使用情况来看, 小型项目没有必要管这个问题。

成功运行

image.png

collection 一对多

同样的有两种办法

第一种方法:用select,跟association 中使用select类似:

public interface MobilePhoneDao {
    List<MobilePhone> queryMbByUserId(int userId);
}

实现该方法


<select id="queryMbByUserId" parameterType="int" resultMap="mobilePhoneMap">
        SELECT brand,price
        FROM tb_mobile_phone
        WHERE user_id=#{userId}
    </select>
1 <resultMap type="User" id="userMap">
2        <id property="userId" column="user_id"/>
3        <result property="userName" column="user_name"/>
4    <collection property="mobilePhone" column="user_id" 
                select="com.zhu.ssm.dao.MobilePhoneDao.queryMbByUserId">
5        </collection>
6 </resultMap> 

2、第二种方法:嵌套resultMap

<resultMap type="MobilePhone" id="mobilephoneMap">
         <id column="mobile_phone_id" property="mobilePhoneId"/>
         <result column="brand" property="brand" />
         <result column="price" property="price" /></resultMap>
    <resultMap type="User" id="userMap">
        <result property="userName" column="user_name"/>
        <result property="age" column="age"/>
        <collection property="mobilePhone" resultMap="mobilephoneMap" >
        </collection>
</resultMap> 

和association都类似。


weiweiyi
1k 声望123 粉丝