Mybaits级联demo详细说明

一.前言

目前在学习mybatis级联,做一个demo的备份,(读者按照我补全的代码,可以无脑运行)。

二.简单介绍

Mybatis级联分为三种:

1.      鉴别器(discriminator)它是根据某系条件决定采用具体实现类级联的方案,比如以下例子中体检表根据性别区分。

2.      一对一(association)比如员工工牌和员工一对一的级联。

3.      一对多(collection)比如班主任和学生的一对多级联。

另外,Mybaits没有多对多的级联。

如下图:为雇员级联模型图,级联关系已经标出。

Mybaits级联demo详细说明_第1张图片

分析:

a.      该模型以雇员表为中心。

b.      雇员表和工牌表是一对一级联关系。

c.      雇员表和员工任务表是一对多级联关系。

d.      员工任务表和任务表是一对一级联关系。

e.      每个雇员都会有一个体检表,随着雇员表字段性别取值不同,会有不同的关联表。

三.根据级联建表

根据上面的级联管理建立数据库表,SQL如下:

---------------------------MyBatis级联测试-----------
 drop table if exists t_female_health_form;
 drop table if exists t_male_health_form;
 drop table if exists t_task;
 drop table if exists t_work_card;
 drop table if exists t_employee_task;
 drop table if exists t_employee;

 create table t_employee
 (
   id         int(12) not null auto_increment,
   real_name  varchar(60) not null,
   sex        int(2) not null comment '1 - 男     0 - 女 ',
   birthday   date not null,
   mobile     varchar(20) not null,
   email      varchar(60) not null,
   POSITION   varchar(20) not null,
   note       varchar(256),
   primary key (id)
 );
 
 create table t_employee_task
 (
   id         int(12) not null auto_increment,
   emp_id     int(12) not null,
   task_id    int(2) not null,
   task_name  varchar(60) not null,
   note       varchar(256),
   primary key (id)
 );

 create table t_female_health_form
 (
   id         int(12) not null auto_increment,
   emp_id     int(12) not null,
   heart      varchar(64) not null,
   liver      varchar(64) not null,
   spleen     varchar(64) not null,
   lung       varchar(64) not null,
   kidney     varchar(64) not null,
   uterus     varchar(64) not null,
   note       varchar(256),
   primary key (id)
 );

 create table t_male_health_form
 (
   id         int(12) not null auto_increment,
   emp_id     int(12) not null,
   heart      varchar(64) not null,
   liver      varchar(64) not null,
   spleen     varchar(64) not null,
   lung       varchar(64) not null,
   kidney     varchar(64) not null,
   prostate   varchar(64) not null,
   note       varchar(256),
   primary key (id)
 );
 
 create table t_task
 (
   id         int(12) not null,
   title      varchar(60) not null,
   context    varchar(256) not null,
   note       varchar(256),
   primary key (id)
 );
 
 create table t_work_card
 (
   id         int(12) not null auto_increment,
   emp_id     int(12) not null,
   real_name  varchar(60) not null,
   department varchar(60) not null,
   mobile     varchar(20) not null,
   POSITION   varchar(20) not null,
   note       varchar(256),
   primary key (id)
 );
 ---增加外键约束---------
  alter table t_employee_task add constraint FK_Reference_4 foreign  key (emp_id) references t_employee (id) on delete restrict on update restrict;
  alter table t_employee_task add constraint FK_Reference_8 foreign  key (task_id) references t_task (id) on delete restrict on update restrict;
  alter table t_female_health_form add constraint FK_Reference_5 foreign  key (emp_id) references t_employee (id) on delete restrict on update restrict;
  alter table t_male_health_form add constraint FK_Reference_6 foreign  key (emp_id) references t_employee (id) on delete restrict on update restrict;
  alter table t_work_card add constraint FK_Reference_7 foreign  key (emp_id) references t_employee (id) on delete restrict on update restrict;
  --------------------------------插入测试数据--------
    insert into t_employee values 
 (1,'Tom',1,'2018-05-05','18888888888','email@com','position',''),
 (2,'Alice',0,'2018-03-03','1222222222','email@com2','position2','');
   insert into t_employee_task values
 (1,1,1,'tom_task',''),
 (2,2,2,'alice_task','');
  insert into t_female_health_form values(1,2,'heart','liver','spleen','lung','kidney','uterus','');
 insert into t_male_health_form values(1,1,'heart','liver','spleen','lung','kidney','prostate','');
 insert into t_task values 
 (1,'tom_task','tom_task_context',''),
 (2,'alice_task','alice_task_context','');
  insert into t_work_card values
 (1,1,'Tom','departmentA','18888888888','position',''),
 (2,2,'Alice','departmentB','1222222222','position2','');

四.建立POJO

对于体检表,男女体检表字段重复度高,建立一个父类:HealthForm,通过继承完成POJO,如下:

体检表父类:

package com.ssm.chapter5.pojo;

public abstract class HealthForm {
  
	private Long id;
	private Long empId;
	private String heart;
	private String liver;
	private String spleen;
	private String lung;
	private String kidney;
	private String note;
	
	public Long getId(){
		return id;
	}
	public void setId(Long id){
		this.id = id;
	}
	
	public Long getEmpId(){
		return empId;
	}
	public void setEmpId(Long empId){
		this.empId = empId;
	}
	
	public String getHeart(){
		return heart;
	}
	public void setHeart(String heart){
		this.heart = heart;
	}
	
	public String getLiver(){
		return liver;
	}
	public void setLiver(String liver){
		this.liver = liver;
	}
	
	public String getSpleen(){
		return spleen;
	}
	public void setSpleen(String spleen){
		this.spleen = spleen;
	}
	
	public String getLung(){
		return lung;
	}
	public void setLung(String lung){
		this.lung = lung;
	}
	
	public String getKidney(){
		return kidney;
	}
	public void setKidney(String kidney){
		this.kidney = kidney;
	}
	
	public String getNote(){
		return note;
	}
	public void setNote(String note){
		this.note = note;
	}
}

女性体检表POJO:

package com.ssm.chapter5.pojo;

public class FemaleHealthForm extends HealthForm {

	private String uterus;
	
	public String getUterus(){
		return uterus;
	}
	public void setUterus(String uterus){
		this.uterus = uterus;
	}
}

男性体检表POJO:

package com.ssm.chapter5.pojo;

public class MaleHealthForm extends HealthForm {

	private String prostate;
	
	public String getProstate(){
		return prostate;
	}
	public void setProstate(String prostate){
		this.prostate = prostate;
	}
}

女性和男性区分是在mybatis的鉴别器完成。

工牌表POJO:

package com.ssm.chapter5.pojo;

public class WorkCard {

	private Long id;
	private Long empId;
	private String realName;
	private String department;
	private String mobile;
	private String position;
	private String note;
	
	public Long getId(){
		return id;
	}
	public void setId(Long id){
		this.id = id;
	}
	
	public Long getEmpId(){
		return empId;
	}
	public void setEmpId(Long empId){
		this.empId = empId;
	}
	
	public String getRealName(){
		return realName;
	}
	public void setRealName(String realName){
		this.realName = realName;
	}
	
	public String getDepartment(){
		return department;
	}
	public void setDepartment(String department){
		this.department = department;
	}
	
	public String getMobile(){
		return mobile;
	}
	public void setMobile(String mobile){
		this.mobile = mobile;
	}
	
	public String getPosition(){
		return position;
	}
	public void setPosition(String position){
		this.position = position;
	}
	
	public String getNote(){
		return note;
	}
	public void setNote(String note){
		this.note = note;
	}
}

任务表POJO:

package com.ssm.chapter5.pojo;

public class Task {
	private Long id;
	private String title;
	private String context;
	private String note;
	
	public Long getId(){
		return id;
	}
	public void setId(Long id){
		this.id = id;
	}
	
	public String getTitle(){
		return title;
	}
	public void setTitle(String title){
		this.title = title;
	}

	public String getContext(){
		return context;
	}
	public void setContext(String context){
		this.context = context;
	}
	
	public String getNote(){
		return note;
	}
	public void setNote(String note){
		this.note = note;
	}
}

雇员任务表通过任务标号(task_id)和任务进行一对一关联,这里只考虑其自身和任务编号的关联,而雇员对它的关联有雇员维护,得到雇员任务POJO:

package com.ssm.chapter5.pojo;

public class EmployeeTask {

	private Long id;
	private Long empId;
	private Task task = null;
	private String taskName;
	private String note;
	
	public Long getId(){
		return id;
	}
	public void setId(Long id){
		this.id = id;
	}
	
	public Long getEmpId(){
		return empId;
	}
	public void setEmpId(Long empId){
		this.empId = empId;
	}
	
	public Task getTask(){
		return task;
	}
	public void setTask(Task task){
		this.task = task;
	}
	
	public String getTaskName(){
		return taskName;
	}
	public void setTaskName(String taskName){
		this.taskName = taskName;
	}
	
	public String getNote(){
		return note;
	}
	public void setNote(String note){
		this.note = note;
	}
}

其中属性task是一个Task类对象,由它进行关联任务信息

           设置雇员表是本次demo的关键,雇员根据性别分为男雇员和女雇员,他们对应的体检记录也是不同,因此,建立一个雇员类(Employee)作为父类,包含两个子类男雇员(MaleEmployee)和女雇员(FemaleEmployee)。在Mybatis中这就是一个鉴别器,通过雇员类的字段性别(sex)来决定使用哪个具体的子类,因此,我们需要自定义mybatis的类型转换器(TypeHandler),首先建立SexEnum这个枚举类如下:

package com.ssm.chapter5.pojo;

public enum SexEnum {
	
	female(0,"女"),
	male(1,"男");
	
	private int id;
	private String name;
	
	SexEnum(int id, String name){
		this.id = id;
		this.name = 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 static SexEnum getSexById(int id){
		for(SexEnum sex : SexEnum.values())
		{
			if(sex.getId() == id){
				return sex;
			}
		}
		return null;
	}
}

自定义类型转换器SexEnumTypeHandler如下:

package com.ssm.chapter5.typehandler;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.JdbcType;

import com.ssm.chapter5.pojo.SexEnum;

@MappedTypes(SexEnum.class)
@MappedJdbcTypes(JdbcType.INTEGER)
public class SexEnumTypeHandler implements TypeHandler {
	
   public void setParameter(PreparedStatement ps, int i, SexEnum parameter, JdbcType jdbcType) throws SQLException{
	   ps.setInt(i, parameter.getId());
   }
   
   public SexEnum getResult(ResultSet rs, String columnName) throws SQLException {
	   int id = rs.getInt(columnName);
	   return SexEnum.getSexById(id);
   }
   
   public SexEnum getResult(ResultSet rs, int columnIndex) throws SQLException{
	   int id = rs.getInt(columnIndex);
	   return SexEnum.getSexById(id);
   }
   
   public SexEnum getResult(CallableStatement cs, int columnIndex) throws SQLException{
	   int id = cs.getInt(columnIndex);
	   return SexEnum.getSexById(id);
   }
}

然后是Employee的POJO:

package com.ssm.chapter5.pojo;

import java.util.Date;
import java.util.List;

public class Employee {

	private Long id;
	private String realName;
	private SexEnum sex = null;
	private Date birthday;
	private String mobile;
	private String email;
	private String position;
	private String note;
	private WorkCard workCard;  //工牌按照一对一级联
	private List employeeTaskList = null;  //雇员任务,按照一对多级联
	
	public Long getId(){
		return id;
	}
	public void setId(Long id){
		this.id = id;
	}
	
	public String getRealName(){
		return realName;
	}
	public void setRealName(String realName){
		this.realName = realName;
	}
	
	public SexEnum getSex(){
		return sex;
	}
	public void setSex(SexEnum sex){
		this.sex = sex;
	}
	
	public Date getBirthday(){
		return birthday;
	}
	public void setBirthday(Date birthday){
		this.birthday = birthday;
	}
	
	public String getMobile(){
		return mobile;
	}
	public void setMobile(String mobile){
		this.mobile = mobile;
	}
	
	public String getEmail(){
		return email;
	}
	public void setEmail(String email){
		this.email = email;
	}
	
	public String getPosition(){
		return position;
	}
	public void setPosition(String position){
		this.position = position;
	}
	
	public String getNote(){
		return note;
	}
	public void setNote(String note){
		this.note = note;
	}
	
	public WorkCard getWorkCard(){
		return workCard;
	}
	public void setWorkCard(WorkCard workCard){
		this.workCard = workCard;
	}
	
	public List getEmployeeTaskList(){
		return employeeTaskList;
	}
	public void setEmployeeTaskList(List employeeTaskList){
		this.employeeTaskList = employeeTaskList;
	}
	
}

男性雇员POJO:

package com.ssm.chapter5.pojo;

public class MaleEmployee extends Employee {

	private MaleHealthForm maleHealthForm = null;
	
	public MaleHealthForm  getMaleHealthForm(){
		return maleHealthForm;
	}
	public void setMaleHealthForm(MaleHealthForm maleHealthForm){
		this.maleHealthForm = maleHealthForm;
	}
}

女性雇员POJO:

package com.ssm.chapter5.pojo;

public class FemaleEmployee extends Employee {

	private FemaleHealthForm femaleHealthForm = null;
	
	public FemaleHealthForm  getFemaleHealthForm(){
		return femaleHealthForm;
	}
	public void setMaleHealthForm(FemaleHealthForm femaleHealthForm){
		this.femaleHealthForm = femaleHealthForm;
	}
}
其中, MaleEmployee和FemaleEmployee都是继承Employee的,因此对应着不同的体检表,同时,Employee是通过employeeTaskList属性和多个雇员任务进行一对多关联的,工牌表是通过workCard进行一对一关联的,至此,POJO结束。

五.配置映射文件

配置映射文件是级联核心内容,我们从最简单的出发,

TaskMapper接口和XML如下:

mapper:

package com.ssm.chapter5.mapper;

import com.ssm.chapter5.pojo.Task;

public interface TaskMapper {

	public Task getTask(Long id);
}

XML:




  

WorkCard接口和XMl如下:

mapper:

package com.ssm.chapter5.mapper;

import com.ssm.chapter5.pojo.WorkCard;

public interface WorkCardMapper {

	public WorkCard getWorkCardByEmpId(Long empId);
}

XML:




  

雇员任务表是通过任务编号(task_id)和任务表示关联,是一对一关联的,使用association,接口和XML如下:

mapper:

package com.ssm.chapter5.mapper;

import com.ssm.chapter5.pojo.EmployeeTask;

public interface EmployeeTaskMapper {
	public EmployeeTask getEmployeeTaskByEmpId (Long empId);

}

XML:




 
    
    
    
    
    
 



其中,association元素表示代表着一对一级联,property表示映射到POJO中属性,select配置是命名空间+SQL id的形式,指向对应Mapper的SQL,Mybatis就会通过对应的SQL将数据查询出来。Cloumn代表SQL的列,用作参数传递给select属性指定的SQL,如果是多个参数,请用逗号隔开

接下来是体检表,分为男性雇员和女性雇员,映射器比较较简单,

男性雇员体检表的接口和XML如下:

mapper:

package com.ssm.chapter5.mapper;

import com.ssm.chapter5.pojo.MaleHealthForm;

public interface MaleHealthFormMapper {

	public MaleHealthForm getMaleHealthForm(Long EmpId);
}

XML:





女性雇员体检表的接口和XML如下:

mapper:

package com.ssm.chapter5.mapper;

import com.ssm.chapter5.pojo.FemaleHealthForm;

public interface FemaleHealthFormMapper {

	public FemaleHealthForm getFemaleHealthForm(Long EmpId);
}

XML:




   

最后,根据上述的映射关系,得到下面的雇员的映射关系如下

mapper:

package com.ssm.chapter5.mapper;

import com.ssm.chapter5.pojo.Employee;

public interface EmployeeMapper {

	public Employee getEmployee(Long id);
}

XML:



 

   
   
   
   
   
   
   
   
   
   
   
       
       
   



    



    

   

分析如下:

1.      association元素,表示一对一级联,上述已说过;

2.      collection元素,表示一对多级联,其select指向SQL,将column字段作为参数传递给select,然后返回雇员POJO的属性,employeeTaskList;

3.      discriminator元素,是鉴别器,column表示哪个字段需要进行区分,这里是sex,子元素case则用于区分,类似java的switch…case…语句,resultMap属性表示采用哪个ResultMap去映射,比如sex=1则使用maleHealthFormMapper进行映射。

对于雇员体检表而言,id为employee的resultMap,被maleHealthFormMapper和femaleHealthFormMapper通过extends元素继承,从类的关系看,它们也是这样的。

六.Mytatis配置

日志配置如下:

log4j.rootLogger=DEBUG , stdout
log4j.logger.org.mybatis=DEBUG
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p %d %c: %m%n

mybatis-config.xml配置如下:




 
  
  
  
    
     
   
       
          
          
          
          
       
    
  
  
  
  
 
 
  

另外,我本地使用的是mysql所以需要mysql驱动jar包:

Mybaits级联demo详细说明_第2张图片

七.测试级联

使用单例模式构建SqlSessionFactory,如下:

package com.ssm.chapter5.utils;

import java.io.IOException;
import java.io.InputStream;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class SqlSessionFactoryUtils {

	private final static Class LOCK = SqlSessionFactoryUtils.class;
	
	private static SqlSessionFactory sqlSessionFactory = null;
	
	private SqlSessionFactoryUtils(){}
	
	public static SqlSessionFactory getSqlSessionFactory(){
		synchronized (LOCK){
			if(sqlSessionFactory != null){
				return sqlSessionFactory;
			}
			String resource = "mybatis-config.xml";
			InputStream inputStream;
			try{
				inputStream = Resources.getResourceAsStream(resource);
				sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
			}catch(IOException e){
				e.printStackTrace();
				return null;
			}
			return sqlSessionFactory;
		}
	}
	
	public static SqlSession openSqlSession(){
		if(sqlSessionFactory == null){
			getSqlSessionFactory();
		}
		return sqlSessionFactory.openSession();
	}
}

主函数如下:

package com.ssm.chapter5.main;

import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;

import com.ssm.chapter5.mapper.EmployeeMapper;
import com.ssm.chapter5.pojo.Employee;
import com.ssm.chapter5.utils.SqlSessionFactoryUtils;

public class Chapter5Main {

	public static void main(String[] args) {
		Logger logger = Logger.getLogger(Chapter5Main.class);
		SqlSession sqlSession = null;
        try{
        	sqlSession = SqlSessionFactoryUtils.openSqlSession();
        	EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
        	Employee employee = employeeMapper.getEmployee(1L);
        	logger.info(employee.getBirthday());
        }catch(Exception e){
        	logger.info(e.getMessage());
        }finally{
        	if(sqlSession != null){}
        	sqlSession.close();
        }
	}

}

结果如下:

DEBUG 2018-05-24 18:48:47,266 org.apache.ibatis.logging.LogFactory: Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
DEBUG 2018-05-24 18:48:47,336 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
DEBUG 2018-05-24 18:48:47,336 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
DEBUG 2018-05-24 18:48:47,336 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
DEBUG 2018-05-24 18:48:47,336 org.apache.ibatis.datasource.pooled.PooledDataSource: PooledDataSource forcefully closed/removed all connections.
DEBUG 2018-05-24 18:48:47,340 org.apache.ibatis.io.VFS: Class not found: org.jboss.vfs.VFS
DEBUG 2018-05-24 18:48:47,341 org.apache.ibatis.io.JBoss6VFS: JBoss 6 VFS API is not available in this environment.
DEBUG 2018-05-24 18:48:47,341 org.apache.ibatis.io.VFS: Class not found: org.jboss.vfs.VirtualFile
DEBUG 2018-05-24 18:48:47,342 org.apache.ibatis.io.VFS: VFS implementation org.apache.ibatis.io.JBoss6VFS is not valid in this environment.
DEBUG 2018-05-24 18:48:47,342 org.apache.ibatis.io.VFS: Using VFS adapter org.apache.ibatis.io.DefaultVFS
DEBUG 2018-05-24 18:48:47,342 org.apache.ibatis.io.DefaultVFS: Find JAR URL: file:/D:/workspace-LIST/workspace-SSM-Test/mybatistest2/target/classes/com/ssm/chapter5/mapper
DEBUG 2018-05-24 18:48:47,342 org.apache.ibatis.io.DefaultVFS: Not a JAR: file:/D:/workspace-LIST/workspace-SSM-Test/mybatistest2/target/classes/com/ssm/chapter5/mapper
DEBUG 2018-05-24 18:48:47,413 org.apache.ibatis.io.DefaultVFS: Reader entry: EmployeeMapper.class
DEBUG 2018-05-24 18:48:47,414 org.apache.ibatis.io.DefaultVFS: Reader entry: EmployeeMapper.xml
DEBUG 2018-05-24 18:48:47,422 org.apache.ibatis.io.DefaultVFS: Reader entry: EmployeeTaskMapper.class
DEBUG 2018-05-24 18:48:47,423 org.apache.ibatis.io.DefaultVFS: Reader entry: EmployeeTaskMapper.xml
DEBUG 2018-05-24 18:48:47,423 org.apache.ibatis.io.DefaultVFS: Reader entry: FemaleHealthFormMapper.class
DEBUG 2018-05-24 18:48:47,423 org.apache.ibatis.io.DefaultVFS: Reader entry: FemaleHealthFormMapper.xml
DEBUG 2018-05-24 18:48:47,423 org.apache.ibatis.io.DefaultVFS: Reader entry: MaleHealthFormMapper.class
DEBUG 2018-05-24 18:48:47,423 org.apache.ibatis.io.DefaultVFS: Reader entry: MaleHealthFormMapper.xml
DEBUG 2018-05-24 18:48:47,423 org.apache.ibatis.io.DefaultVFS: Reader entry: TaskMapper.class
DEBUG 2018-05-24 18:48:47,424 org.apache.ibatis.io.DefaultVFS: Reader entry: TaskMapper.xml
DEBUG 2018-05-24 18:48:47,446 org.apache.ibatis.io.DefaultVFS: Reader entry: WorkCardMapper.class
DEBUG 2018-05-24 18:48:47,446 org.apache.ibatis.io.DefaultVFS: Reader entry: WorkCardMapper.xml
DEBUG 2018-05-24 18:48:47,447 org.apache.ibatis.io.DefaultVFS: Listing file:/D:/workspace-LIST/workspace-SSM-Test/mybatistest2/target/classes/com/ssm/chapter5/mapper
DEBUG 2018-05-24 18:48:47,447 org.apache.ibatis.io.DefaultVFS: Find JAR URL: file:/D:/workspace-LIST/workspace-SSM-Test/mybatistest2/target/classes/com/ssm/chapter5/mapper/EmployeeMapper.class
DEBUG 2018-05-24 18:48:47,447 org.apache.ibatis.io.DefaultVFS: Not a JAR: file:/D:/workspace-LIST/workspace-SSM-Test/mybatistest2/target/classes/com/ssm/chapter5/mapper/EmployeeMapper.class
DEBUG 2018-05-24 18:48:47,447 org.apache.ibatis.io.DefaultVFS: Reader entry: ����

从打印日志看出,我们所有的级联都成功执行了。

八.参考文献:

《javaEE互联网轻量级框架整合开发》

书中例子不全,需要补充才可以运行。如非允许,禁止转载!


你可能感兴趣的:(Mybatis)