Mybatis XML vs Annotation

2019-03-16 06:02发布

问题:

I have read the book and documentation about Mybatis, both XML and Annotation does what I want, but from myBatis official website, they claim XML is a better way of doing Mappers, because Java annotation has limitations.

I personally prefer Annotations e.g

public interface PersonDAO {

    String INSERT_PERSON = "insert into person (title,firstName,surName,jobTitle,dob,email,mobile,landPhone,fax,twitter,facebook,linkedin) VALUES  (#{title},#{firstName},#{surName},#{jobTitle},#{dob},#{email},#{mobile},#{landPhone},#{fax},#{twitter},#{facebook},#{linkedin})";
    String UPDATE_PERSON = "update person set title=#{title},firstName=#{firstName},surName=#{surName},jobTitle=#{jobTitle},dob=#{dob},email=#{email},mobile=#{mobile},landPhone=#{landPhone},fax=#{fax},twitter=#{twitter},facebook=#{facebook},linkedin=#{linkedin} where id=#{id}";
    String GET_PERSON_BY_ID = "SELECT * FROM vw_person WHERE id = #{personId}";
    String DELETE_PERSON = "DELETE FROM person WHERE id = #{personId}";

    @Select(GET_PERSON_BY_ID)
    public PersonVO doSelectPerson(long personId) throws Exception;

    @Update(UPDATE_PERSON)@Options(flushCache = true, useCache = true)
    public int doUpdatePerson(PersonVO vo) throws Exception;


    @Insert(INSERT_PERSON)@Options(useGeneratedKeys = true, keyProperty = "id", flushCache = true)
    public int doCreatePerson(PersonVO person) throws Exception;

    @Delete(DELETE_PERSON)@Options(flushCache = true)
    public int doDeletePerson(long personId) throws Exception;

}

I wonder what the limitation is? Nothing seems to appear obvious to me.

回答1:

On top of Nested Join Mapping that Pitchers said, resultMap in XML format supports inheritance, which can not be achieved in annotation, you have do rewrite each time. Also the @Results annotation is a counterpart of the Mapper XML element <resultMap>. However, as of MyBatis 3.2.2 we can't give an ID for the @Results annotation. So unlike the <resultMap> XML element, we can't reuse the @Results declaration across different mapped statements. What this means is that you need to duplicate the @Results configuration even though it is the same. For example, see the following findStudentBy() and findAllStudents() methods:

@Select("SELECT * FROM STUDENTS WHERE STUD_ID=#{studId}")
@Results({
  @Result(id=true, column="stud_id", property="studId"),
  @Result(column="name", property="name"),
  @Result(column="email", property="email"),
  @Result(column="addr_id", property="address.addrId")
})
Student findStudentById(int studId);

@Select("SELECT * FROM STUDENTS")
@Results({
  @Result(id=true, column="stud_id", property="studId"),
  @Result(column="name", property="name"),
  @Result(column="email", property="email"),
  @Result(column="addr_id", property="address.addrId")
})
List<Student> findAllStudents();

Here the @Results configuration is same for both the statements, but we need to duplicate it. There is also a work around for this problem. We can create a Mapper XML file and configure the <resultMap> element and reference that resultMap using the @ResultMap annotation.

Define <resultMap> with ID StudentResult in StudentMapper.xml.

<mapper namespace="com.mybatis3.mappers.StudentMapper">
  <resultMap type="Student" id="StudentResult">
    <id property="studId" column="stud_id"/>
    <result property="name" column="name"/>
    <result property="email" column="email"/>
    <result property="phone" column="phone"/>
  </resultMap>
</mapper>

SQL Mappers Using Annotations

In StudentMapper.java, reference the resultMap attribute StudentResult using @ResultMap.

public interface StudentMapper

@Select("SELECT * FROM STUDENTS WHERE STUD_ID=#{studId}")
@ResultMap("com.mybatis3.mappers.StudentMapper.StudentResult")
Student findStudentById(int studId);

@Select("SELECT * FROM STUDENTS")
@ResultMap("com.mybatis3.mappers.StudentMapper.StudentResult")
List<Student> findAllStudents();

quote from Java-Persistence-with-MyBatis3



回答2:

Yes,the documentation for Mybatis cautions that annotations can be much simpler and easier to read for smaller, simpler projects. However, annotations are limited compared to the XML configuration. If your project includes complex objects or a complex database structure, consider using the XML configuration instead of the Java annotations.

XML mapping is still required for the most advanced mappings like Nested Join Mapping in MyBatis.



回答3:

There are plenty use cases when using .xml can be much more consice and cleaner approach. Say, you can create some Common.xml, define a bunch of queries, like

 <sql id="inStmt">
    IN
    <foreach item="id" collection="ids" separator="," open="(" close=")">
        #{id}
    </foreach>
</sql>

and reuse this code in your project.

Moreover, the template query can be defined, e.g.:

<sql id="selectDistinct">
    SELECT DISTINCT(${column}), #{param}
    FROM ${entityTable}
</sql>

And then you can reference it via

 <include refid="Common.selectDistinct">
        <property name="column" value="id"/>
        <property name="entityTable" value="some_table"/>
 </include>

This approach is less error-prone and much less copy/pasted. (you can have a look here).

Conditions, iterating, etc should also be mentioned, too.