Обратная связь Mybatis вызывает дублирование объектов
Я пытаюсь сопоставить обратное отношение коллекции с MyBatis 3.2.8 . Похоже, что MyBatis дублирует объекты, даже если у них один и тот же идентификатор.
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
}
XML-файл отображения
<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
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
}
}
Я хотел бы objB.getObjA() и обя - , чтобы быть в одном экземпляре (та же ссылка) в обя.
Как я могу настроить свою карту результатов, чтобы заставить ее работать?
1 ответ:
POJO'S
ObjA
иObjB
, которые вы описали, находятся вcircular dependency
.рассмотрим следующий пример : пусть у нас есть класс
Artist
и классRecording
. Им не нужно реализовывать сопоставимый интерфейс, но мы делаем это, потому что хотим отсортировать эти объекты. Кроме того, SQL-запросы могут быть написаны для указания желаемого порядка сортировки возвращаемых строк.Вот класс художников. Обратите внимание, что художник хранит коллекцию своих записей.
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; } }
Вот класс записи. Обратите внимание, что запись содержит ссылку на своего исполнителя.
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; } }
Конфигурация:
<?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>
Сопоставленные операторы определяются в XML-файлах, на которые ссылаются из SqlMapConfig.XML-файл.
Вот файл, который определяет операторы, связанные с нашей таблицей исполнителей.
<?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>
Вот файл, который определяет операторы, связанные с нашей таблицей записей.
<?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>