JSON Converter
Jimmer实体并非POJO,但可以靠org.babyish.jimmer.jackson.ImmutableModule支持Jackson序列化。
如果使用Jimmer的Spring Boot Starter,该ImmutableModule会被自动注册,无需用户关注。
然而有的时候,我们需要对某些属性的Jackson序列化行为进行定制,我们以两种情况来举例。
-
雪花ID
-
字段敏感
1. 雪花ID
雪花id会导致数值很大的int64类型,遗憾的是,JavaScript的数字类型无法表达其他语言中的int64大数字。因此我们不得不让这类id属性被序列化为字符串。
用法
- Java
- Kotlin
@Entity
public interface Book {
@Id
@GeneratedValue(generatorType = SnowflakeIdGenerator.class)
@JsonConverter(LongToStringConverter.class)
long id();
String name();
...省略其他属性...
}
@Entity
interface Book {
@Id
@GeneratedValue(generatorType = SnowflakeIdGenerator::class)
@JsonConverter(LongToStringConverter::class)
val id: Long
val name: String
...省略其他属性...
}
其中SnowflakeIdGenerator是用户提供的分布式雪花id生成器实现,不是我们讨论的重点。
这里,我们讨论被标记的那行代码:
-
@org.babyfish.jimmer.jackson.JsonConverter表示自定义Book.id属性的Jackson序列化/反序列化行为。 -
org.babyfish.jimmer.jackson.LongToStringConverter表示将long类型的值按照String来序列化/反序列化。LongToStringConverter是Jimmer内置的Converter,这里展示一下其源代码,我们就可以快熟了解如何自定义Converter。package org.babyfish.jimmer.jackson;
public class LongToStringConverter implements Converter<Long, String> {
// 如何序列化
@Override
public String output(Long value) {
return Long.toString(value);
}
// 如何反序列化
@Override
public Long input(String jsonValue) {
return Long.parseLong(jsonValue);
}
}
这里,我们绕过ORM,通过模拟数据展示效果。
- Java
- Kotlin
Book book = Immutables.createBook(draft -> {
draft.setId(1546434604146774987L);
draft.setName("SQL in Action");
});
System.out.println(book);
val book = Book {
id = 1546434604146774987L
name = "SQL in Action"
}
println(book)
由于Jimmer实体的toString就是Jackson序列化,所以,我们可以直接打印并观察Jackson序列化的结果:
{
"id": "1546434604146774987",
"name": "SQL in Action"
}
可见,序列化后的id是字符串,而非数字,这样,就避开了JavaScript客户端无法解析大数字的问题。
自动应用于IdView属性
@IdView属性是进阶映射才会讲解的内容,读者也可以先跳过本节,以后再回来看。
- Java
- Kotlin
@Entity
public interface Author {
@ManyToMany(mappedBy = "authors")
List<Book> books();
@IdView("authors")
List<Long> bookIds();
...省略其他属性...
}
@Entity
interface Author {
@ManyToMany(mappedBy = "authors")
val books: List<Book>
@IdView("authors")
val bookIds: List<Long>
...省略其他属性...
}
这里,bookIds属性并非普通的List<Long>集合,而是所有关联的Book对象的id的集合。
由于Book.id属性已经被注解@JsonConverter修饰,所以,无需为这里的bookIds属性指定任何注解 ,该属性也有这种能力。
同样,绕过ORM,通过模拟数据展示效果。
- Java
- Kotlin
Author author = Immutables.createAuthor(draft -> {
draft.setBookIds(
Arrays.asList(
1546434604146774987L,
1546434604146774988L
)
);
});
System.out.println(book);
val book = Book {
bookIds = listOf(
1546434604146774987L,
1546434604146774988L
)
}
println(book)
由于Jimmer实体的toString就是Jackson序列化,所以,我们可以直接打印并观察Jackson序列化的结果:
{
"bookIds": [
"1546434604146774987",
"1546434604146774988"
]
}
可见,序列化后的bookIds属性的所有元素都是字符串,而非数字,这样,就 避开了JavaScript客户端无法解析大数字的问题。
自动应用于DTO
DTO是进阶映射才会讲解的内容,读者也可以先跳过本节,以后再回来看。
在src/main/dto目录下新建任何一个扩展名为dto的文件,编辑其代码如下
export com.yourcompany.yourproject.model.Book
-> package com.yourcompany.yourproject.model.dto
BookView {
id
name
}
编译项目,生成的Java/Kotlin类型如下
- Java
- Kotlin
@GeneratedBy(file = "<yourproject>/src/main/dto/Book.dto")
public class BookView {
private String id;
private String name;
...省略其他属性...
}
@GeneratedBy(file = "<yourproject>/src/main/dto/Book.dto")
class BookView(
val id: String,
val name: String
) {
...省略其他属性...
}
可见,编译后自动生成的DTO类型中id属性的类型为String,而非long。
最后,我们验证一下将实体转化为DTO对象,如下
- Java
- Kotlin
Book book = Immutables.createBook(draft -> {
draft.setId(1546434604146774987L);
draft.setName("SQL in Action");
});
System.out.println(new BookView(book));