为啥 Arraylist<String> 作为长且随机的字符串存储在 H2 数据库中?

Posted

技术标签:

【中文标题】为啥 Arraylist<String> 作为长且随机的字符串存储在 H2 数据库中?【英文标题】:Why Arraylist<String> get stored in H2 database as a long and random string?为什么 Arraylist<String> 作为长且随机的字符串存储在 H2 数据库中? 【发布时间】:2021-12-14 06:22:55 【问题描述】:

我在 Spring-Boot (Kotlin) 项目中创建了一个实体(模型或数据类),其中包含一个类型为 Arraylist 的字段,但是当我从 Postman 发送 JSON 格式的数组数据时,该数组存储在数据库中作为一个长的随机字符串。

当我尝试从数据库中检索数据时,我得到了格式完美的实际数组。

我的问题是为什么 ArrayList 会像这样存储在 H2 数据库中???

评估.kt

@Entity
data class Evaluation (
 @Id val id : String,
 val timeStamp : Long,
 val symptoms : ArrayList<String>,
 val travelHistory : Boolean,
 val contactWithCovidPatient : Boolean,
 val evaluatedBy : String,
 var evaluationPercentage : String? = null,
 @ManyToOne var user: User? = null
)

EvaluationController.kt

@RestController
class EvaluationController (val evaluationService: IEvaluationService) 

@PostMapping("evaluate/userId")
fun evaluateUser(@PathVariable userId : String, @RequestBody evaluation: Evaluation) : ResponseEntity<Evaluation> =
    ResponseEntity.ok().body(evaluationService.addEvaluation(evaluation, userId))


请求正文 JSON


"id":"e_01",
"timeStamp":"123456789",
"pinCode":"123457",
"travelHistory":true,
"contactWithCovidPatient":true,
"evaluatedBy":"u_01",
"symptoms": ["Fever","Cough"]

响应 JSON


"id": "e_01",
"timeStamp": 123456789,
"symptoms": [
    "Fever",
    "Cough"
],
"travelHistory": true,
"contactWithCovidPatient": true,
"evaluatedBy": "u_01",
"evaluationPercentage": "95",
"user": 
    "id": "u_01",
    "name": "abc01",
    "phoneNumber": "9876543210",
    "pinCode": "123457",
    "covidResult": "Positive"


H2 数据库表

【问题讨论】:

【参考方案1】:

这是一个十六进制字符串,表示序列化的ArrayList 对象。有关 Java 中对象序列化的详细信息,请参阅Serializable Objects。

运行以下代码会产生相同的结果:

List<String> symptoms = new ArrayList<>(Arrays.asList("Fever", "Cough"));
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(symptoms);
byte[] serializedObject = byteArrayOutputStream.toByteArray();
String hex = Hex.encodeHexString(serializedObject); // Apache Commons Codec
System.out.println(hex);
aced0005737200136a6176612e7574696c2e41727261794c6973747881d21d99c7619d03000149000473697a657870000000027704000000027400054665766572740005436f75676878

【讨论】:

有什么办法可以将普通数组存储到数据库中?? 您通常使用@ElementCollection。在这种情况下,集合的元素存储在单独的表中。【参考方案2】:

在数据库中看到的原始字符串是一个序列化的对象。

实现这一点的一种方法是首先将您的字符串 ArrayList 加入一个分隔字符串,但我强烈建议不要这样做。 通常,将列表放入表的单个字段中是不好的做法。您应该做的是为与评估具有一对多关系的症状创建一个单独的表。

在使用 JPA 设计对象时,您需要注意非规范化。在您的情况下,请考虑以下问题:

    如果您想查询具有特定症状的评估会怎样? 如果要查询所有症状的列表会怎样? 如果您想使用其他一些细节(例如症状出现的日期)来扩展症状,会发生什么情况?

如果您曾经在 99.9999% 的情况下尝试将某个集合添加到数据库字段中,那么您做错了。症状应该是它们自己的实体,并且您在评估和症状之间具有一对多或多对多的关系,具体取决于您的需求。

编辑:

为了进一步澄清我的答案,在设计对象类时,请考虑字段是值对象还是实体。值对象是不能进一步分解的东西,可以用原语表示,例如 Date、String、Int 等。一些示例可能是对象 ID、名称、电话号码等。

实体是可以进一步扩展的对象,例如您创建的评估对象。在评估中,您有一个症状列表,并且您将症状视为一个值对象。它是一个价值对象吗?我可以立即想到一些可以放入 Symptom 对象的额外字段,并且通过按照您所做的方式对症状进行非规范化,您还将大量重复数据输入到数据库中。

在您的实现中包含 ["Fever", "Cough"] 的评估对象将作为一个字段输入到数据库中。但是另一个包含相同症状的评估对象将被输入到该评估的数据库中,因为您没有外键依赖项或表示症状的单独表。除了无法查询与评估相关的症状,或者无法自行查询症状。

【讨论】:

以上是关于为啥 Arraylist<String> 作为长且随机的字符串存储在 H2 数据库中?的主要内容,如果未能解决你的问题,请参考以下文章

为啥这一行不编译 List<Object> l = new ArrayList<String>()? [复制]

为啥 Arraylist<String> 作为长且随机的字符串存储在 H2 数据库中?

为啥 ArrayList 在仅适配器中总是相同的值?

为啥我不能有两个带有 ArrayList 参数的方法?

如何从 ArrayList<Hashtable<String, ArrayList<String>>> 获取值

Java - 为啥我不能部分输入变量?