在mybatis中,并没有像hibernate中那么多的关系标签,one-to-one,one-to-many,many-to-one,many-to-many,在mybatis中,只存在一和多的关系标签,对应数据库来说,就是有别的表外键(并不一定设置外键,只是有一个字段对应别的表的主键,这里是为了方便叙述,下同)的表对应多,另一边对应一,多对多,则两边都用多的关系标签,这么说可能会有些模糊,看完下面的例子应该就会清楚许多了
一对一的关系在数据库中并不多见,因为原则上说,一对一关系的两个表,完全可以合成一张表,
下面以学生表和学生证表为例,演示一对一的双向关联,学生证表中设有学生表的id
学生表有四个字段,sid,name,age,birthday,学生证表有两个字段,id 和stu_id.
在这里提一点,用mybatis设计表的时候,最好能让每个表中字段名称不一样,可以在每个字段的前面加上表的缩写,这样在用连接查询的时候,可以不用把表的每个属性写出来设置别名
学生表对应的实体:
package com.entity;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class Student {
private Integer id;
private String name;
private Integer age;
private Date birthday;
private List paper = new ArrayList();
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public List getPaper() {
return paper;
}
public void setPaper(List paper) {
this.paper = paper;
}
}
一如本文开头所说,有外键的表为多的一方,虽然实际情况是一对一的关系,但是从数据库的角度考虑,一个学生可以有多个学生证,因此在学生表中有多个学生证的实体对象,也就是学生证对象的集合,在这里用ArrayList集合,这也是为了和配置文件相照应,
顺带提一下,对于日期属性,一般情况下可以用String,数据库可以用VARCHAR2,这里设置成Date型,是为了验证配置文件中属性类型的设置,见下面配置文件
下面是学生证表对应的实体类:
package com.entity;
public class Paper {
private Integer id;
private Student student;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
}
Student.xml:
注意:resultMap中,property对应的实体类中的变量名,column对应的数据库表中查询结果的字段名,请注意是查询结果的字段名,而不是数据库表设计的字段名,如果你在查询语句中对某个或某几个字段设置了别名的话,一定要将column设置为别名,因为resultMap是用来做返回值类型的,它需要与查询出来后的表数据对应,另外一点,javaType和jdbcType,前者没有什么好说的,实体类中怎么写,这里就怎么写,jdbcType是对应数据库设计的时候字段的类型,注意一定要大写,number型写成INTEGER,VARCHAR2型写成VARCHAR,date型就写DATE,一般常用的就这三个,mybatis有默认的类型处理器,会对javaType和jdbcType对应起来进行处理,当然,你也可以自己设定类型处理器,不过一般用不着,下面附上一张默认的类型处理器截图,无版图上没有下载ps,因此就把全图放上来了,请见谅
另外,关于关联的写法,在开头说过了,有两种,一和多两种标签,Student.xml中的collection是“多”的标签,下面Paper.xml中的association是“一”的标签,注意几个属性,collection标签中,property还是实体类中对应的成员变量名称,而column在这里不再是数据库中的字段名了,而是参数名,这个参数实在两个表之间传递的,如果被关联的表用到了这个参数,那么sql语句中的参数字符串名字,必须和这里的column一致。。
javaType要写成ArrayList,另外要写一个ofType,表示ArrayList中放的对象。最后,也是最关键的,就是两个表关联的手段,也就是方式,一般来说有两种做法,一种是镶嵌的select方法,一种是镶嵌的resultMap方法,这在本文中都有涉及,在这里先解释一下。
顾名思义,镶嵌的select方法,用到了select属性,它的值是对应的关联表的配置xml中的一条sql语句,当resultMap用到的时候,会去执行这条sql语句,并且会把column这个参数的值传递过去,然后这条sql执行的结果,会封装到ofType中,然后放在list集合中,作为student类的一个成员属性,当然,在一对一中,list集合的size永远不会超过1。
补充一点,select对应的查询sql语句的返回值类型,可以直接写成对应的实体类,也可以写resultMap,但是建议这种情况下写两个resultMap,就如上面配置文件中所写,这样在执行这条查询的时候,不用再去关注关联表的数据情况,因为本来就是从关联表查询过来的,这里只需要查询出非对象的一般成员的值即可,更甚至,你可以只在一个resultMap中写出某几个你需要使用的字段,可以省掉不必要的内存消耗,这也是resultMap的一个便利之处。
至于镶嵌的resultMap方法,结合Paper表的配置文件说,请继续往下看。
Paper.xml:
update paper set stu_id='2' where id=#{id}
在一的一方,association标签中属性相对少得多,只需要写出来实体类对应的成员变量名和数据库中“外键“的字段名称(这个外键字段名对应的值,会作为参数传递给关联表),获取关联表信息的方法即可。
镶嵌的resultMap方法,如上面配置文件中的”paperResultMap2“所示,这个Map,对应关联表中的一个resultMap,同select方法,这个resultMap最好只有用到的属性,不好包含对象属性,避免不必要的多余查询,譬如上面如何将resultMap=”com.entity.Student.studentResultMap“,那么在查询student的时候,会再次执行查询paper的sql语句,浪费资源。
用这种方式关联表关系的话,注意sql语句要写成迫切左外链接查询,如上面的”selectPaperAndStudent“,这样一次性将两张表的数据查询出来,然后再按照字段与实体类属性的对应关系一个个封装,一如前面所提,mybatis是对查询出来的表进行封装对象的,如果查询出来的字段有重复的话,字段名相同的的第二个字段会被自动生成别名,但是在resultMap中字段与属性的对应并不会改变,因此两个对象都会使用第一个字段的值,这样数据就错乱了,因此才说创建数据库表的时候,最好设置不同的字段名,如果确实已经创建了名称相同的字段,需要在查询的时候,将重复的字段一一罗列出来,然后设置别名,当然,在resultMap中也要进行相应的修改。
在这里顺带提一下,hibernate中,处理这种情况,可以这样写sql语句: select {s.*},{p.*} from.... 用花括号括起来就可以了。
这两种关联方式,推荐使用第二种,原因很简单,用左外链接的查询方式,可以避免N+1的问题,至于这个问题,有兴趣的朋友可以自行搜索。
下面是测试类(这是LZ摸索学习的时候的案例,没有用接口的方式)
package com.test;
import java.io.IOException;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import com.entity.Paper;
import com.entity.Student;
import com.util.SqlSessionFactoryUtil;
public class TestSelectOne2One {
public static void main(String[] args) throws IOException {
TestSelectOne2One test = new TestSelectOne2One();
test.testSelectOne();
}
public void testSelectOne() {
SqlSession session = SqlSessionFactoryUtil.getSqlSession();
Paper paper = session.selectOne("com.entity.Paper.selectPaperById", 6);
System.out.println(paper.getStudent().getName() + "====="
+ paper.getStudent());
session.close();
}
public void testSelectAll() {
SqlSession session = SqlSessionFactoryUtil.getSqlSession();
List list = session.selectList("selectAllPagers");
for (int i = 0; i < list.size(); i++) {
Paper paper = list.get(i);
System.out.println(paper.getStudent().getName() + "====="
+ paper.getStudent().getAge());
}
session.close();
}
public void testCache() throws IOException {
// mybatis支持一级缓存,不支持二级缓存
SqlSessionFactory factory = new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsReader("configuration.xml"));
SqlSession session = factory.openSession();
Paper paper = session.selectOne("com.entity.Paper.selectPaperById", 6);
Paper paper2 = session.selectOne("com.entity.Paper.selectPaperById", 6);
System.out.println(paper.getStudent().getName() + "-------");
System.out.println(paper2.getStudent().getName() + "=====");
session.clearCache();
Paper paper3 = session.selectOne("com.entity.Paper.selectPaperById", 6);
System.out.println(paper3.getStudent().getName() + "*-*-*-*-*-");
session.close();
session = factory.openSession();
Paper paper4 = session.selectOne("com.entity.Paper.selectPaperById", 6);
System.out.println(paper4.getStudent().getName() + "=/=/=//=/=/=/=/");
session.close();
}
public void test(){
SqlSession session = SqlSessionFactoryUtil.getSqlSession();
List list = session.selectList("selectPaperAndStudent");
for (int i = 0; i < list.size(); i++) {
Paper paper = list.get(i);
System.out.println(paper.getStudent().getName() + "====="
+ paper.getStudent().getAge());
}
session.close();
}
public void test3(){
SqlSession session = SqlSessionFactoryUtil.getSqlSession();
Student student = session.selectOne("com.entity.Student.selectStudent",2);
System.out.println(student.getName()+"========"+student.getPaper());
session.close();
}
}