Propongo la mia soluzione in una richiesta pull
L'idea è di cambiare Entità in:
import com.example.demo.pojo.SamplePojo
import com.vladmihalcea.hibernate.type.json.JsonBinaryType
import com.vladmihalcea.hibernate.type.json.JsonStringType
import org.hibernate.annotations.Type
import org.hibernate.annotations.TypeDef
import org.hibernate.annotations.TypeDefs
import javax.persistence.*
@Entity
@Table(name = "tests")
@TypeDefs(
TypeDef(name = "json", typeClass = JsonStringType::class),
TypeDef(name = "jsonb", typeClass = JsonBinaryType::class)
)
data class SampleEntity (
@Id @GeneratedValue
val id: Long?,
val name: String?,
@Type(type = "jsonb")
@Column(columnDefinition = "jsonb")
var data: Map<String, Any>?
) {
/**
* Dependently on use-case this can be done differently:
* https://stackoverflow.com/questions/37873995/how-to-create-empty-constructor-for-data-class-in-kotlin-android
*/
constructor(): this(null, null, null)
}
- Ogni entità dovrebbe avere un costruttore predefinito o avere valori predefiniti per tutti i suoi parametri
- Invece di salvare POJO, salva come
Map<String, Any>
digita
Dal momento che abbiamo il pieno controllo di ciò che sarà in POJO nella logica aziendale, l'unico pezzo mancante sarà convertire POJO in Mappa e Mappa in POJO
Implementazione di SamplePojo
data class SamplePojo(
val payload: String,
val flag: Boolean
) {
constructor(map: Map<String, Any>) : this(map["payload"] as String, map["flag"] as Boolean)
fun toMap() : Map<String, Any> {
return mapOf("payload" to payload, "flag" to flag)
}
}
Questa è piuttosto una soluzione alternativa, ma ci consente di lavorare con qualsiasi struttura a livello di profondità.
PS Ho notato che usi Serializer
e ridefinito equals, toString, hashCode
. Non è necessario se si utilizza data class
.
AGGIORNAMENTO:
Se hai bisogno di una struttura più flessibile di Map<String, Any>
, puoi usare JsonNode
. Esempio di codice
Entità:
import com.fasterxml.jackson.databind.JsonNode
import com.vladmihalcea.hibernate.type.json.JsonBinaryType
import com.vladmihalcea.hibernate.type.json.JsonStringType
import org.hibernate.annotations.Type
import org.hibernate.annotations.TypeDef
import org.hibernate.annotations.TypeDefs
import javax.persistence.*
@Entity
@Table(name = "tests")
@TypeDefs(
TypeDef(name = "json", typeClass = JsonStringType::class),
TypeDef(name = "jsonb", typeClass = JsonBinaryType::class)
)
data class SampleJsonNodeEntity (
@Id @GeneratedValue
val id: Long?,
val name: String?,
@Type(type = "jsonb")
@Column(columnDefinition = "jsonb")
var data: JsonNode?
) {
/**
* Dependently on use-case this can be done differently:
* https://stackoverflow.com/questions/37873995/how-to-create-empty-constructor-for-data-class-in-kotlin-android
*/
constructor(): this(null, null, null)
}
Modifica entità nel repository:
import com.example.demo.entity.SampleJsonNodeEntity
import org.springframework.data.jpa.repository.JpaRepository
interface SampleJsonNodeRepository: JpaRepository<SampleJsonNodeEntity, Long> {
}
Test per entrambi gli approcci:
import com.example.demo.DbTestInitializer
import com.example.demo.entity.SampleJsonNodeEntity
import com.example.demo.entity.SampleMapEntity
import com.example.demo.pojo.SamplePojo
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertNotNull
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.junit4.SpringRunner
@RunWith(SpringRunner::class)
@SpringBootTest
@ContextConfiguration(initializers = [DbTestInitializer::class])
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class SampleRepositoryTest {
@Autowired
lateinit var sampleMapRepository: SampleMapRepository
@Autowired
lateinit var sampleJsonNodeRepository: SampleJsonNodeRepository
lateinit var dto: SamplePojo
lateinit var mapEntity: SampleMapEntity
lateinit var jsonNodeEntity: SampleJsonNodeEntity
@Before
fun setUp() {
dto = SamplePojo("Test", true)
mapEntity = SampleMapEntity(null,
"POJO1",
dto.toMap()
)
jsonNodeEntity = SampleJsonNodeEntity(null,
"POJO2",
jacksonObjectMapper().valueToTree(dto)
)
}
@Test
fun createMapPojo() {
val id = sampleMapRepository.save(mapEntity).id!!
assertNotNull(sampleMapRepository.getOne(id))
assertEquals(sampleMapRepository.getOne(id).data?.let { SamplePojo(it) }, dto)
}
@Test
fun createJsonNodePojo() {
val id = sampleJsonNodeRepository.save(jsonNodeEntity).id!!
assertNotNull(sampleJsonNodeRepository.getOne(id))
assertEquals(jacksonObjectMapper().treeToValue(sampleJsonNodeRepository.getOne(id).data, SamplePojo::class.java), dto)
}
}