Mybatis feedback causes duplicate objects

I am trying to map the inverse of a collection relationship with MyBatis 3.2.8 . MyBatis seems to be duplicating objects even though they have the same ID.

public class ObjA {
    private String id;
    private ArrayList<ObjB> objBs;

    // Getters, setters ...

    // Equals based on the id field
}

public class ObjB {
    private String id;
    private ObjA objA;

    // Getters, setters ...

    // Equals based on the id field
}

      

Displayed xml file

<resultMap id="xx" type="ObjA">
    <id column="idA" property="id" />
    <collection property="objBs" javaType="ArrayList" ofType="ObjB">
        <id column="idB" property="id"/>
        <association property="objA" type="ObjA">
            <id column="idA" property="id" />
        </association>
    </collection>
</resultMap>

      

JUnit test

ArrayList<ObjA> result = service.getAllObjA();

for(ObjA objA : result) {
    for(ObjB objB : objA.getObjBs()) {
        assertEquals(objB.getObjA(), objA); // Pass
        assertTrue(objB.getObjA() == objA); // Does not pass
    }
}

      

I would like objB.getObjA () and objA to be the same instance (same reference) of ObjA.

How can I customize my scorecard to make it work.

+3


source to share


1 answer


The POJO description ObjA

and ObjB

that you described are in circular dependency

.

Consider the following example: let's have a class Artist

and a class Recording

. They don't need to implement the Comparable interface, but we do it because we want to sort these objects. Alternatively, SQL queries can be written to specify the desired sort order for the returned rows.

Here is the Artist class. Please note that the Artist has a collection of their Records.

import java.util.List;

public class Artist implements Comparable {

  private int id;
  private List<Recording> recordings;
  private String name;

  public Artist() {}
  public Artist(String name) { this.name = name; }

  public int compareTo(Object obj) {
    Artist r = (Artist) obj;
    return name.compareTo(r.name);
  }

  public int getId() { return id; }
  public void setId(int id) { this.id = id; }

  public String getName() { return name; }
  public void setName(String name) {
    this.name = name;
  }

  public List<Recording> getRecordings() { return recordings; }
  public void setRecordings(List<Recording> recordings) {
    this.recordings = recordings;
  }
}

      

Here's the recording class. Please note that the post contains a link to its artist.

public class Recording implements Comparable {

  private int id;
  private int year;
  private Artist artist;
  private String name;

  public Recording() {}
  public Recording(Artist artist, String name, int year) {
    this.artist = artist;
    this.name = name;
    this.year = year;
  }

  public int compareTo(Object obj) {
    Recording r = (Recording) obj;
    return name.compareTo(r.name);
  }

  public Artist getArtist() { return artist; }
  public void setArtist(Artist artist) { this.artist = artist; }

  public int getId() { return id; }
  public void setId(int id) { this.id = id; }

  public String getName() { return name; }
  public void setName(String name) { this.name = name; }

  public int getYear() { return year; }
  public void setYear(int year) { this.year = year; }
}

      

Configuration:



<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMapConfig
  PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
  "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">

<sqlMapConfig>

  <settings useStatementNamespaces="true"/>

  <transactionManager type="JDBC">
    <dataSource type="SIMPLE">
      <property name="JDBC.Driver" value="com.mysql.jdbc.Driver"/>
      <property name="JDBC.ConnectionURL"
        value="jdbc:mysql://localhost:3306/music"/>
      <property name="JDBC.Username" value="root"/>
      <property name="JDBC.Password" value=""/>
    </dataSource>
  </transactionManager>

  <sqlMap resource="Artist.xml"/>
  <sqlMap resource="Recording.xml"/>

</sqlMapConfig>

      

The corresponding assertions are defined in the XML files referenced by the SqlMapConfig.xml files.

Here's a file that specifies the statements associated with our artist table.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMap
  PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
  "http://ibatis.apache.org/dtd/sql-map-2.dtd">

<sqlMap namespace="Artist">

  <delete id="deleteAll">
    delete from artists
  </delete>

  <insert id="insert" parameterClass="com.ociweb.music.Artist">
    insert into artists (name) values (#name#)
    <selectKey resultClass="int" keyProperty="id">
      select last_insert_id() as id
    </selectKey>
  </insert>

  <resultMap id="result" class="com.ociweb.music.Artist">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <!-- This results in N+1 selects. To avoid this, see page 40
         in the iBATIS Developer Guide. -->
    <result property="recordings" column="id"
      select="Recording.getByArtist"/>
  </resultMap>

  <select id="getById"
    parameterClass="java.lang.Integer" resultMap="result">
    select * from artists where id=#id#
  </select>

  <select id="getAll" resultClass="com.ociweb.music.Artist">
    select * from artists
  </select>

</sqlMap>

      

Here is the file that specifies the statements related to our record table.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMap
  PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
  "http://ibatis.apache.org/dtd/sql-map-2.dtd">

<sqlMap namespace="Recording">

  <delete id="deleteAll">
    delete from recordings
  </delete>

  <insert id="insert" parameterClass="com.ociweb.music.Recording">
    insert into recordings (name, year, artist_id)
    values (#name#, #year#, #artist.id#)
    <selectKey resultClass="int" keyProperty="id">
      select last_insert_id() as id
    </selectKey>
  </insert>

  <resultMap id="resultWithoutArtist" class="com.ociweb.music.Recording">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="year" column="year"/>
  </resultMap>

  <resultMap id="result"
    class="com.ociweb.music.Recording" extends="resultWithoutArtist">
    <result property="artist" column="artist_id" select="Artist.getById"/>
  </resultMap>

  <select id="getAll" resultClass="com.ociweb.music.Recording">
    select * from recordings
  </select>

  <!-- resultWithoutArtist is used here to avoid a circular
       dependency when Artist.result (in Artist.xml) is used. -->
  <select id="getByArtist"
    parameterClass="java.lang.Integer" resultMap="resultWithoutArtist">
    select * from recordings where artist_id=#artistId#
  </select>

  <select id="getByYear"
    parameterClass="java.lang.Integer" resultMap="result">
    select * from recordings where year=#year#
  </select>

</sqlMap>

      

Find a complete example

+1


source







All Articles