mybatis---一对一双向关联

在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;
	}
}

然后是xml配置文件

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表的配置文件说,请继续往下看。

mybatis---一对一双向关联_第1张图片


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();
	}
}

至于中间的缓存测试,是后来修改过去的,详细的缓存机制,后面会单独写一篇来测试,这里只稍微提一下,mybatis和hibernate一样,都是默认支持一级缓存的,缓存的好处,自然是减少查询次数,它也可以解决N+1的问题。



你可能感兴趣的:(mybatis)