1. 程式人生 > >使用Springboot @TypeDiscriminator註解實現多型物件的查詢,jackson @JsonTypeInfo註解實現controller多型支援

使用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 '

[email protected]16750' 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"),

不然查出來的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。