'Spring Boot one to one mapping serialization
I just started out with Spring Boot and currently working on a home project. I have the following entities:
Currency
@Entity
@Table(name = "currencies")
data class Currency(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0,
val code: String,
val name: String,
val countryName: String,
val iconUrl: String
): java.io.Serializable
RateEntity
@Entity
@Table(name = "rates")
data class RateEntity(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0,
val date: Date,
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "currency_code", referencedColumnName = "code")
val currency: Currency,
val rate: Double
)
Everything seems to be working fine, but json response looks like this:
{
"id": 126,
"date": "2022-03-16",
"currency": null,
"rate": 1.0336
}
So the currency value is always null. In database everything looks normal:
+-----+------------+--------+---------------+
| id | date | rate | currency_code |
+-----+------------+--------+---------------+
| 126 | 2022-03-16 | 1.0336 | CHF |
+-----+------------+--------+---------------+
So I'm thinking that I made something wrong with relationship mapping. Or do I have to map RateEntity to a different data class in my Repo/Service ?
UPDATE:
Rates Controller, Service and Repo
@RestController
@RequestMapping("/rates")
class RatesController(private val service: RatesService) {
@GetMapping("/all")
fun getAllRates() = service.getAllRates()
}
@Service
class RatesService(private val repo: RatesRepository) {
fun getAllRates(): List<RateEntity> = repo.findAll()
fun addRate(rate: RateEntity){
repo.save(rate)
}
}
@Repository
interface RatesRepository: JpaRepository<RateEntity, Long>
I also probably wasn't clear what I want to achieve. I believe I don't need an annotation in Currency Entity at all, because I don't need bidirectional relation. I want only Rates Table to have a code from Currency Table and not by PK, but by code, which is also unique in this Table.
Update 2
I updated Entities code again (see above). I removed annotation from Currency Table. After this I was getting a Serializable Exception on Currency object each time I requested Rates endpoint result. I fixed it by extending the Currency Class with Serializable interface and now I am getting the following result:
{
"id": 1,
"date": "2022-03-18",
"currency": {
"id": 28,
"code": "CHF",
"name": "Swiss Franc\r",
"countryName": "Liechtenstein; Switzerland",
"iconUrl": ""
},
"rate": 1.025
}
So it serializes the whole Currency object. I would like to show only the code value under currency in Rates Table, but at the same time to keep it as Currency object.
I think I need a custom Json serializer component for Rate object or map it to a different one (like RateView object or something) before giving it to the controller.
Update 3: Using simple @JsonComponent I finally achieved what I wanted:
@JsonComponent
class RateJsonSerializer: JsonSerializer<RateEntity>() {
override fun serialize(rateEntity: RateEntity?, jsonGenerator: JsonGenerator?, p2: SerializerProvider?) {
jsonGenerator?.let { generator ->
generator.writeStartObject()
rateEntity?.let { rate ->
generator.writeNumberField("id", rate.id)
generator.writeStringField("date", rate.date.toString())
generator.writeStringField("currency", rate.currency.code)
generator.writeNumberField("rate", rate.rate)
}
generator.writeEndObject()
}
}
}
-----RESULT-----
{
"id": 2,
"date": "2022-03-17",
"currency": "CHF",
"rate": 1.0385
},
{
"id": 3,
"date": "2022-03-17",
"currency": "HRK",
"rate": 7.573
},
{
"id": 4,
"date": "2022-03-17",
"currency": "MXN",
"rate": 22.788
}
The question now, is it a viable solution? How are this kinds of thing usually done in Spring Boot?
Solution 1:[1]
Try this code snippets, hope it should work.
@Entity
@Table(name = "currencies")
data class Currency(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0,
val code: String,
val name: String,
val countryName: String,
val iconUrl: String,
@OneToOne(mappedBy = "currency")
val rateEntity: RateEntity
)
@Entity
@Table(name = "rates")
data class RateEntity(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0,
val date: Date,
val rate: Double,
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "currency_code", referencedColumnName = "id")
val currency: Currency
)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Zakaria Hossain |
