例如一个人有一部手机,每个品牌手机的价格不同,下面通过两个表来表示这个时间,PERSON表和PHONE表。
PERSON表:
PHONE表:
两个表中通过phone_brand字段对应,首先在PERSON表中通过id查询出某人拥有的手机品牌phone_brand,然后在PHONE表中通过手机品牌phone_brand查出价格。实现两个表的一对一关联查询。
下面演示如何用mybatis实现此两个表一对一的关联查询。
首先建立两个表对应的实体类bean。
Person类:
package com.lzj.mybaits.bean;
public class Person {
private int id;
private String name;
private Phone phone;
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 Phone getPhone() {
return phone;
}
public void setPhone(Phone phone) {
this.phone = phone;
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", phone=" + phone + "]";
}
}
Phone类:
package com.lzj.mybaits.bean;
public class Phone {
private String brand;
private float price;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
@Override
public String toString() {
return "Phone [brand=" + brand + ", price=" + price + "]";
}
}
然后建立Dao接口,对应于Mybatis中mapper文件的查询语句。
public interface PersonDao {
public Person getPerson(int id);
}
下面分三种方式实现一对一级联查询
方式一:
把联表中的字段用不同的别名命名区分,然后把别名通过resultMap 映射成类中对应的属性。
<mapper namespace="com.lzj.mybatis.dao.PersonDao">
<!-- 一对一的级联查询,级联属性的方式 -->
<!--column中为字段的别名;propert对应Person类中属性-->
<resultMap type="com.lzj.mybaits.bean.Person" id="personResult">
<id column="id" property="id" />
<result column="name" property="name" />
<!--phone为Person类中的一个对象属性,通过“对象.”的形式映射到Phone类的对象属性中-->
<result column="pbrand" property="phone.brand" />
<result column="price" property="phone.price" />
</resultMap>
<!--注意字段的别名最好要定义成不同的,以便在resultMap映射清晰-->
<select id="getPerson" parameterType="int" resultMap="personResult2">
SELECT
ps.id, ps.name, ps.phone_brand brand,
ph.phone_brand pbrand,
ph.phone_price price FROM PERSON ps, PHONE ph
WHERE
ps.phone_brand=ph.phone_brand AND ps.id=#{id}
</select>
</mapper>
测试方法为:
public static void testGetPseron(){
String resource = "conf.xml";
InputStream in = MybaitsTest.class.getClassLoader().getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();
PersonDao personDao = session.getMapper(PersonDao.class);
Person person = personDao.getPerson(1);
System.out.println(person);
session.close();
}
运行测试方法,输出结果
Person [id=1, name=lzj, phone=Phone [brand=iphone, price=5000.0]]
方式二:
一对一级联查询,用association的方式
<mapper namespace="com.lzj.mybatis.dao.PersonDao">
<resultMap type="com.lzj.mybaits.bean.Person" id="personResult2">
<!--column中为字段的别名;propert对应Person类中属性-->
<id column="id" property="id" />
<result column="name" property="name" />
<!--Person类中最后一个对象属性为phone,通过property指定;通过javaType指定phone的类型;-->
<association property="phone" javaType="com.lzj.mybaits.bean.Phone">
<!--column中为字段的别名;propert对应Phone类中属性-->
<result column="pbrand" property="brand" />
<result column="price" property="price" />
</association>
</resultMap>
<select id="getPerson" parameterType="int" resultMap="personResult2">
SELECT
ps.id, ps.name, ps.phone_brand brand,
ph.phone_brand pbrand,
ph.phone_price price FROM PERSON ps, PHONE ph
WHERE
ps.phone_brand=ph.phone_brand AND ps.id=#{id}
</select>
</mapper>
测试方法同上,运行测试方法,结果同上。
方式三:
通过association实现分布查询
首先按照id查询PERSON的信息;
然后按照PERSON信息中的phone_brand去查询PHONE信息;
最后把查到的PHONE信息设置到PERSON信息中。
1、首先按照id查询PERSON的信息
Dao接口
package com.lzj.mybatis.dao;
import com.lzj.mybaits.bean.Person;
public interface PersonDao {
public Person getByStepPerson(int id);
}
sql的映射mapper文件PsersonDaoMapper.xml:
<mapper namespace="com.lzj.mybatis.dao.PersonDao">
<!--3.把查询到的PHONE信息设置到PERSON信息中-->
<resultMap type="com.lzj.mybaits.bean.Person" id="resultByStep">
<id column="id" property="id"/>
<result column="name" property="name"/>
<!--phone的信息通过传入字段phone_brand来查询com.lzj.mybatis.dao.PhoneDao.getPhone方法获得-->
<association property="phone" select="com.lzj.mybatis.dao.PhoneDao.getPhone"
column="phone_brand"></association>
</resultMap>
<!--1.首先根据id查询PERSON的信息-->
<select id="getByStepPerson" resultMap="resultByStep">
select * from PERSON where id=#{id}
</select>
</mapper>
2、然后按照PERSON信息中的phone_brand去查询PHONE信息
PHONE的Dao接口:
package com.lzj.mybatis.dao;
import com.lzj.mybaits.bean.Phone;
public interface PhoneDao {
public Phone getPhone(String brand);
}
sql的映射mapper文件PhoneMapper.xml:
<mapper namespace="com.lzj.mybatis.dao.PhoneDao">
<select id="getPhone" resultType="com.lzj.mybaits.bean.Phone">
select phone_brand brand, phone_price price from PHONE where phone_brand=#{brand}
</select>
</mapper>
测试方法同上,输出结果同上。
方法四:
对于方法三分布查询中,先查询PERSON的sql语句,然后再查询PHONE的sql语句。在联合查询中,有时我们只需要联合查询的部分数据,就没必要进行全部的sql语句查询。例如在方法三的联合查询中,只需要PERSON表中的name信息,此时可以不用查询PHONE表。可以在mybatis的配置文件中设置为懒加载,当用到时候执行相应的SQL语句。
配置文件中设置如下:
<settings>
<!--lazyLoadingEnabled设置为false,表示需要的时候才执行sql-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--aggressiveLazyLoading设置为true表示一次性执行联合查询中的所有sql,所以此地设置为false,不执行一次性查询--->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
对于方法三种的案例,如果测试方法改为
public static void testGetPersonByStep(){
String resource = "conf.xml";
InputStream in = MybaitsTest.class.getClassLoader().getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
SqlSession session = factory.openSession();
PersonDao personDao = session.getMapper(PersonDao.class);
Person person = personDao.getByStepPerson(2);
/*只打印了name信息,此时只执行查询PERSON的SQL的语句就够了,查询PHONE的SQL语句没有执行*/
System.out.println(person.getName());
session.close();
}
如果把上面的打印语句改为:
System.out.println(person.getPhone());
表示需要person中的phone信息,此时再继续执行查询PHONE的sql语句。