Mybatis 逆关系导致对象重复
Mybatis inverse relation causes duplicated objects
我尝试用 MyBatis 3.2.8 映射集合的逆关系。看起来 MyBatis 会复制对象,即使它们具有相同的 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
}
映射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() 和 objA 是 ObjA 的同一个实例(同一个引用)。
我如何配置我的结果图才能使其正常工作。
你描述的POJO的ObjA
和ObjB
在circular dependency
.
考虑以下示例:让我们有一个 Artist
class 和一个 Recording
class。它们不需要实现 Comparable 接口,但我们这样做是因为我们要对这些对象进行排序。或者,可以编写 SQL 查询来指定返回行的所需排序顺序。
这是艺术家 class。请注意,艺术家持有其录音的集合。
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;
}
}
这是录音class。请注意,录音包含对其艺术家的引用。
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>
映射语句在 SqlMapConfig.xml 文件引用的 XML 文件中定义。
这是指定与我们的艺术家相关的声明的文件 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>
这是指定与我们的录音相关的语句的文件 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>
我尝试用 MyBatis 3.2.8 映射集合的逆关系。看起来 MyBatis 会复制对象,即使它们具有相同的 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
}
映射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() 和 objA 是 ObjA 的同一个实例(同一个引用)。
我如何配置我的结果图才能使其正常工作。
你描述的POJO的ObjA
和ObjB
在circular dependency
.
考虑以下示例:让我们有一个 Artist
class 和一个 Recording
class。它们不需要实现 Comparable 接口,但我们这样做是因为我们要对这些对象进行排序。或者,可以编写 SQL 查询来指定返回行的所需排序顺序。
这是艺术家 class。请注意,艺术家持有其录音的集合。
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;
}
}
这是录音class。请注意,录音包含对其艺术家的引用。
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>
映射语句在 SqlMapConfig.xml 文件引用的 XML 文件中定义。
这是指定与我们的艺术家相关的声明的文件 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>
这是指定与我们的录音相关的语句的文件 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>