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

13 篇文章 0 订阅
2 篇文章 0 订阅

背景:

最近项目中涉及到要实现继承对象的获取,由于习惯用注解实现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类型,所以解决之。另注意,answers不能加这个javaType = Teacher.class。

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。

2. 需指定defaultImpl=Person.class

这样对于type为除"teacher", "student"之外的其他值时,默认的处理类型为Person,比如我想新建一个type="leader",那么这个leader就是一个简单的Person类

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当然,我可以为您提供一个Java使用Jackson实现解析JSON的示例。这里是一个基本的示例代码: ``` public class Animal { public String name; public String type; } @JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type" ) @JsonSubTypes({ @JsonSubTypes.Type(value = Dog.class, name = "dog"), @JsonSubTypes.Type(value = Cat.class, name = "cat"), }) public abstract class AnimalMixin { @JsonCreator public AnimalMixin(@JsonProperty("name") String name) { } } public class Dog extends Animal { public String breed; public Dog() {} public Dog(String name, String breed) { this.name = name; this.breed = breed; this.type = "dog"; } } public class Cat extends Animal { public int age; public Cat() {} public Cat(String name, int age) { this.name = name; this.age = age; this.type = "cat"; } } public static void main(String[] args) throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); String json = "{ \"name\": \"Fido\", \"type\": \"dog\", \"breed\": \"beagle\" }"; Animal animal = mapper.readValue(json, Animal.class); System.out.println(animal.name); System.out.println(((Dog)animal).breed); } ``` 在这个示例中,我们有一个Animal类,它有一个name属性和一个type属性。type属性用于标识动物的类型,它将在JSON解析期间用于决定将JSON转换为哪个子类。我们通过@JsonTypeInfo注释和@JsonSubTypes注释来指示Jackson使用解析JSON。@JsonTypeInfo注释告诉Jackson使用"type"属性来确定JSON应该被反序列化为哪个类。@JsonSubTypes注释告诉Jackson哪些子类需要被考虑。 我们还有一个AnimalMixin抽象类,它用于处理将JSON转换为适当的子类。在这个例子中,我们有两个实现:Dog和Cat。两个类都有它们自己的属性,但都继承自Animal类。 在main方法中,我们首先创建一个ObjectMapper对象,然后使用readValue方法将JSON字符串转换为Animal对象。然后我们打印了Animal对象的name属性和子类特有的属性。 这是一个Java使用Jackson实现解析JSON的基本示例。我希望它能帮助您。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值