使用Springboot @TypeDiscriminator注解实现多态对象的查询,jackson @JsonTypeInfo注解实现controller多态支持

背景:

最近项目中涉及到要实现继承对象的获取,由于习惯用注解实现mybatis对象映射,所以也想用@TypeDiscriminator实现。但是在百度中却搜索不到@TypeDiscriminator的应用实例,幸好能上国外网,Google之。下面以一个最简单的例子来讲@TypeDiscriminator用法。

例子:

有五个对象,Person,Teacher,Student,Answer,Scholarship,Teacher和Student继承于Person,Teacher扩展answers属性,Student扩展scholarship属性。

// ------------------------------------------------------------------------

package org.zyd.java.springboot.test.mybatis.person;

public class Person {

	protected Integer id;
	protected String type;
	protected String name;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}
	
	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

}

// ------------------------------------------------------------------------

package org.zyd.java.springboot.test.mybatis.person;

import java.util.List;

import org.apache.ibatis.annotations.*;
import org.zyd.java.springboot.test.mybatis.person.student.Student;
import org.zyd.java.springboot.test.mybatis.person.teacher.Teacher;

@Mapper
public interface PersonRepository {

	@Insert("insert into person (name) values (#{name})")
	int insert(Person person);

	@Delete("delete from person where id = #{id}")
	int delete(@Param("id") Integer id);

	@Update("update person set name=IFNULL(#{name}, name) where id = #{id}")
	int update(Person person);

	@Select("select * from person")
	@TypeDiscriminator(javaType = String.class, column = "type", cases = {
			@Case(value = "teacher", type = Teacher.class, results = { @Result(property = "id", column = "id"),
					@Result(property = "answers", column = "id", many = @Many(select = "org.zyd.java.springboot.test.mybatis.answer.AnswerRepository.searchByPerson")) }),
			@Case(value = "student", type = Student.class, results = { @Result(property = "id", column = "id"),
					@Result(property = "scholarship", column = "id", one = @One(select = "org.zyd.java.springboot.test.mybatis.scholarship.ScholarshipRepository.searchByPerson"), javaType = Student.class) }) })
	List<Person> search();

}

// ------------------------------------------------------------------------

package org.zyd.java.springboot.test.mybatis.person;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class PersonService {
	@Autowired
	PersonRepository personRepository;

	int insert(Person person) {
		return personRepository.insert(person);
	}

	int delete(Integer id) {
		return personRepository.delete(id);
	}

	int update(Person person) {
		return personRepository.update(person);
	}

	List<Person> search() {
		return personRepository.search();
	}

}

// ------------------------------------------------------------------------

package org.zyd.java.springboot.test.mybatis.person;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(path = "/person")
public class PersonController {

	@Autowired
	PersonService personService;

	@PostMapping("insert")
	public int insert(@RequestBody Person person) {
		return personService.insert(person);
	}

	@PostMapping("delete")
	public int delete(@RequestParam("id") Integer id) {
		return personService.delete(id);
	}

	@PostMapping("update")
	public int update(@RequestBody Person person) {
		return personService.update(person);
	}

	@GetMapping("search")
	public List<Person> search() {
		return personService.search();
	}

}

// ------------------------------------------------------------------------

package org.zyd.java.springboot.test.mybatis.person.teacher;

import java.util.List;

import org.zyd.java.springboot.test.mybatis.answer.Answer;
import org.zyd.java.springboot.test.mybatis.person.Person;

public class Teacher extends Person {
	
	private List<Answer> answers;

	public List<Answer> getAnswers() {
		return answers;
	}

	public void setAnswers(List<Answer> answers) {
		this.answers = answers;
	}

}

// ------------------------------------------------------------------------

package org.zyd.java.springboot.test.mybatis.person.student;

import org.zyd.java.springboot.test.mybatis.person.Person;
import org.zyd.java.springboot.test.mybatis.scholarship.Scholarship;

public class Student extends Person {
	
	private Scholarship scholarship;

	public Scholarship getScholarship() {
		return scholarship;
	}

	public void setScholarship(Scholarship scholarship) {
		this.scholarship = scholarship;
	}

}

// ------------------------------------------------------------------------

package org.zyd.java.springboot.test.mybatis.answer;

public class Answer {

	private Integer id;
	private String subject;
	
	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getSubject() {
		return subject;
	}

	public void setSubject(String subject) {
		this.subject = subject;
	}

}

// ------------------------------------------------------------------------

package org.zyd.java.springboot.test.mybatis.answer;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface AnswerRepository {

	@Select("select * from answer where person_id=#{personId}")
	List<Answer> searchByPerson(@Param("personId") Integer personId);

}

// ------------------------------------------------------------------------

package org.zyd.java.springboot.test.mybatis.scholarship;

public class Scholarship {
	
	private Integer id;
	private Integer level;
	
	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public Integer getLevel() {
		return level;
	}

	public void setLevel(Integer level) {
		this.level = level;
	}

}

// ------------------------------------------------------------------------

package org.zyd.java.springboot.test.mybatis.scholarship;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface ScholarshipRepository {
	
	@Select("select * from scholarship where person_id=#{personId}")
	Scholarship searchByPerson(@Param("personId") Integer personId);

}

在实现过程中,填了两个坑:
 

1. 需指明Student类型

javaType = Student.class

否则会报错:
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: Could not set property 'scholarship' of 'class org.zyd.java.springboot.test.mybatis.person.student.Student' with value 'org.zyd.java.springboot.test.mybatis.scholarship.Scholarship@6b616750' Cause: java.lang.IllegalArgumentException: argument type mismatch] with root cause

这个坑非常坑,纠结了很久,最开始我在数据库里有两条person数据,一个为teacher,一个为student,我发现List<Answer>并没有报错,但是Scholarship却不能映射,以为只能映射基本java类型,但是想想之前关联查询也有可以映射,说明并不是不能映射,而是mybatis在此时并不认识Student类型,所以解决之。

2. 需手动映射id

@Result(property = "id", column = "id"),

扫描二维码关注公众号,回复: 4479278 查看本文章

不然查出来的Teacher和Student的id都会为null。

现在,查询实现了,但是调用接口的时候,比如插入一个Person(有可能是Teacher,或者Student),怎么去统一接口调用呢?即实现post路径都是 localhost:8080/person/insert。惯例,google之。实现中使用了一些java映射的trick:获取到相应的子类service,同时转化实例为子类对象,还是有点意思的,实现源代码就不再贴一遍了,直接下载吧:

https://download.csdn.net/download/chunzhenzyd/10835621   (如果链接不对的话,可以去我的资源列表去找)

在实现过程中,填了一个坑:
 

1. 需指定visible=true

不然得不到Person中的属性值,比如getType会返回null。

猜你喜欢

转载自blog.csdn.net/chunzhenzyd/article/details/84839078