ObjectTypeConverter not found within persistence unit - spring

In my project I use an enum in some entities. The enum is to be stored in the database as an integer. To achieve this I use EclipseLink's ObjectTypeConverter.
I'd like to use annotations since I use Spring to omit the persistence.xml. The annotation to configure the ObjectTypeConverter must be specified on an entity. I don't feel the need to specify the annotation on all classes that use this enum as this is redundant and not maintainable. Specifying it once on some entity would work but this doesn't make sense in an OOP design (or any design for that mater). A solution would be to annotate the enum with #ObjectTypeConverter, but this doesn't work since the enum isn't an entity.
Example that isn't working but would be ideal:
#Entity
public class ExampleEntity
{
#Id
private Long id;
#Convert("exampleenum")
private ExampleEnum ee;
}
#ObjectTypeConverter(name = "exampleenum", objectType = ExampleEnum.class, dataType = Integer.class,
conversionValues =
{
#ConversionValue(objectValue = "A", dataValue = "100"),
#ConversionValue(objectValue = "B", dataValue = "200"),
#ConversionValue(objectValue = "C", dataValue = "300")
})
public enum ExampleEnum
{
A, B, C;
}
Example results in the following exception:
Exception Description: The converter with name [exampleenum] used with the element [field ee] in the class [class com.example.ExampleEntity] was not found within the persistence unit. Please ensure you have provided the correct converter name.
Since I'm using Spring, JPA and EclipseLink I accept any answer using these frameworks.

Upon reading the documentation (which I should have done more carefully in the first place) I noticed:
An ObjectTypeConverter must be be uniquely identified by name and can be defined at the class, field and property level and can be specified within an Entity, MappedSuperclass and Embeddable class.
I couldn't annotate the enum with #Entity (as this requires a table) or #MappedSuperclass (as this doesn't make sense), but #Embeddable would make sense in a way. Marking the enum with #Embeddable did the trick:
#Embeddable
#ObjectTypeConverter(...)
public enum ExampleEnum
...

Related

MappedSuperclass produces 'has an unbound type and no explicit target entity'

I have this entity. It should work on both, internal and external, users.
#Entity(name = "TokenAuthentication")
class TokenAuthenticationEntity<T>(
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null,
...
#NotNull
#ManyToOne(fetch = FetchType.EAGER)
val user: T,
) : BaseEntity()
When I run this, I get
Property TokenAuthenticationEntity.user has an unbound type and no explicit target entity. Resolve this Generic usage issue or set an explicit target attribute
So Hibernate tells me to f*** off with my generics, it needs explicit definitions. Any of you have an idea how to get this running with generics?
This is not possible. The reason being T type erasure at compile-time. This T type is used at compile-time to check types and guarantee type safety, but then it is removed. Having said that, each instance of TokenAuthenticationEntity can have a different value for T, and as explained before this information is lost.
What you can actually do is the following:
#MappedSuperclass
public abstract class TokenAuthenticationEntity<T> {
private T user;
}
Now you can create your entities based on this generic superclass:
#Entity
public class InternalUser extends TokenAuthenticationEntity<IUser> { }
#Entity
public class ExternalUser extends TokenAuthenticationEntity<EUser> { }
Why? Because each concrete subclass of TokenAuthenticationEntity has a type T which is defined and is retained and unique inside the subclasses. In this case, JPA will store your subclasses in one or more tables, depending on the chosen #InheritanceStrategy (if the Inheritance annotation is not specified or if no inheritance type is specified for an entity class hierarchy, the SINGLE_TABLE mapping strategy is used). Additional details at https://www.baeldung.com/hibernate-inheritance#single-table.

Is there a way to create one JPA entity based on many database tables and do I really have to do this or is it a bad practice?

I'm quite new to Spring Data JPA technology and currently facing one task I can't deal with. I am seeking best practice for such cases.
In my Postgres database I have a two tables connected with one-to-many relation. Table 'account' has a field 'type_id' which is foreign key references to field 'id' of table 'account_type':
So the 'account_type' table only plays a role of dictionary. Accordingly to that I've created to JPA entities (Kotlin code):
#Entity
class Account(
#Id #GeneratedValue var id: Long? = null,
var amount: Int,
#ManyToOne var accountType: AccountType
)
#Entity
class AccountType(
#Id #GeneratedValue var id: Long? = null,
var type: String
)
In my Spring Boot application I'd like to have a RestConroller which will be responsible for giving all accounts in JSON format. To do that I made entities classes serializable and wrote a simple restcontroller:
#GetMapping("/getAllAccounts", produces = [APPLICATION_JSON_VALUE])
fun getAccountsData(): String {
val accountsList = accountRepository.findAll().toMutableList()
return json.stringify(Account.serializer().list, accountsList)
}
where accountRepository is just an interface which extends CrudRepository<Account, Long>.
And now if I go to :8080/getAllAccounts, I'll get the Json of the following format (sorry for formatting):
[
{"id":1,
"amount":0,
"accountType":{
"id":1,
"type":"DBT"
}
},
{"id":2,
"amount":0,
"accountType":{
"id":2,
"type":"CRD"
}
}
]
But what I really want from that controller is just
[
{"id":1,
"amount":0,
"type":"DBT"
},
{"id":2,
"amount":0,
"type":"CRD"
}
]
Of course I can create new serializable class for accounts which will have String field instead of AccountType field and can map JPA Account class to that class extracting account type string from AccountType field. But for me it looks like unnecessary overhead and I believe that there could be a better pattern for such cases.
For example what I have in my head is that probably somehow I can create one JPA entity class (with String field representing account type) which will be based on two database tables and unnecessary complexity of having inner object will be reduced automagically each time I call repository methods :) Moreover I will be able to use this entity class in my business logic without any additional 'wrappers'.
P.s. I read about #SecondaryTable annotation but it looks like it can only work in cases where there is one-to-one relation between two tables which is not my case.
There are a couple of options whic allow clean separation without a DTO.
Firstly, you could look at using a projection which is kind of like a DTO mentioned in other answers but without many of the drawbacks:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections
#Projection(
name = "accountSummary",
types = { Account.class })
public Interface AccountSummaryProjection{
Long getId();
Integer getAmount();
#Value("#{target.accountType.type}")
String getType();
}
You then simply need to update your controller to call either query method with a List return type or write a method which takes a the proection class as an arg.
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projection.dynamic
#GetMapping("/getAllAccounts", produces = [APPLICATION_JSON_VALUE])
#ResponseBody
fun getAccountsData(): List<AccountSummaryProjection>{
return accountRepository.findAllAsSummary();
}
An alternative approach is to use the Jackson annotations. I note in your question you are manually tranforming the result to a JSON String and returning a String from your controller. You don't need to do that if the Jackson Json library is on the classpath. See my controller above.
So if you leave the serialization to Jackson you can separate the view from the entity using a couple of annotations. Note that I would apply these using a Jackson mixin rather than having to pollute the Entity model with Json processing instructions however you can look that up:
#Entity
class Account(
//in real life I would apply these using a Jacksin mix
//to prevent polluting the domain model with view concerns.
#JsonDeserializer(converter = StringToAccountTypeConverter.class)
#JsonSerializer(converter = AccountTypeToStringConverter.class
#Id #GeneratedValue var id: Long? = null,
var amount: Int,
#ManyToOne var accountType: AccountType
)
You then simply create the necessary converters:
public class StringToAccountTypeConverter extends StdConverter<String, CountryType>
implements org.springframework.core.convert.converter.Converter<String, AccountType> {
#Autowired
private AccountTypeRepository repo;
#Override
public AccountType convert(String value) {
//look up in repo and return
}
}
and vice versa:
public class AccountTypeToStringConverter extends StdConverter<String, CountryType>
implements org.springframework.core.convert.converter.Converter<AccountType, String> {
#Override
public String convert(AccountType value) {
return value.getName();
}
}
One of the least complicated ways to achieve what you are aiming for - from the external clients' point of view, at least - has to do with custom serialisation, what you seem to be aware of and what #YoManTaMero has extended upon.
Obtaining the desired class structure might not be possible. The closest I've managed to find is related to the #SecondaryTable annotation but the caveat is this only works for #OneToOne relationships.
In general, I'd pinpoint your problem to the issue of DTOs and Entities. The idea behind JPA is to map the schema and content of your database to code in an accessible but accurate way. It takes away the heavy-lifting of managing SQL queries, but it is designed mostly to reflect your DB's structure, not to map it to a different set of domains.
If the organisation of your DB schema does not exactly match the needs of your system's I/O communication, this might be a sign that:
Your DB has not been designed correctly;
Your DB is fine, but the manageable entities (tables) in it simply do not match directly to the business entities (models) in your external communication.
Should second be the case, Entities should be mapped to DTOs which can then be passed around. Single Entity may map to a few different DTOs. Single DTO might take more than one (related!) entities to be created. This is a good practice for medium-to-large systems in the first place - handing out references to the object that's the direct access point to your database is a risk.
Mind that simply because the id of the accountType is not taking part in your external communication does not mean it will never be a part of your business logic.
To sum up: JPA is designed with ease of database access in mind, not for smoothing out external communication. For that, other tools - such as e.g. Jackson serializer - are used, or certain design patterns - like DTO - are being employed.
One approach to solve this is to #JsonIgnore accountType and create getType method like
#JsonProperty("type")
var getType() {
return accountType.getType();
}

Is the Builder Pattern not available when updating(CRUD) in a spring boot? Can only be used to create objects(for constructor)? [duplicate]

Lets say I have a lombok annotated class like
#Builder
class Band {
String name;
String type;
}
I know I can do:
Band rollingStones = Band.builder().name("Rolling Stones").type("Rock Band").build();
Is there an easy way to create an object of Foo using the existing object as a template and changing one of it's properties?
Something like:
Band nirvana = Band.builder(rollingStones).name("Nirvana");
I can't find this in the lombok documentation.
You can use the toBuilder parameter to give your instances a toBuilder() method.
#Builder(toBuilder=true)
class Foo {
int x;
...
}
Foo f0 = Foo.builder().build();
Foo f1 = f0.toBuilder().x(42).build();
From the documentation:
If using #Builder to generate builders to produce instances of your own class (this is always the case unless adding #Builder to a method that doesn't return your own type), you can use #Builder(toBuilder = true) to also generate an instance method in your class called toBuilder(); it creates a new builder that starts out with all the values of this instance.
Disclaimer: I am a lombok developer.
Is there an easy way to create an object of Foo using the existing object as a template and changing one of it's properties?
(emphasis mine)
If you really want to change a single property, then there's a nicer and more efficient way:
#With
class Band {
String name;
String type;
}
Band nirvana = rollingStones.withName("Nirvana");
The wither creates no garbage, but it can change just a single field. For changing many fields, you could use
withA(a).withB(b).withC(c)....
and produce tons of garbage (all intermediate results) but than toBuilder is more efficient and more natural.
NOTE: Older versions of lombok have used #Wither annotation. See beginning of documentation.
You might also want do a copy of the object using com.fasterxml.jackson.databind.ObjectMapper
#AllArgsConstructor
#Setter
class Band {
String name;
String type;
}
ObjectMapper objectMapper = new ObjectMapper(); //it's configurable
objectMapper.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false );
objectMapper.configure( SerializationFeature.FAIL_ON_EMPTY_BEANS, false );
Band rollingStones = new Band("Rolling Stones", "Rock Band");
Band nirvana = objectMapper.convertValue( rollingStones, Band.class);
nirvana.setName("Nirvana");
it can be easily wrapped in some utility method to be used all over the project like ConvertUtils.clone(rollingStones, Band.class)

Does Room support entity inheritance?

I am trying to migrate our project to use Room, which, by the way, I think is an awesome step forward.
I have the following structure:
public class Entity extends BaseObservable {
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "_id", typeAffinity = ColumnInfo.INTEGER)
private long mId;
#ColumnInfo(name = "is_dirty")
#TypeConverters(BooleanTypeConverter.class)
private boolean mIsDirty;
// default constructor and accessors omitted for brevity
}
#Entity(tableName = "some_entities")
public class SomeEntity extends Entity {
#ColumnInfo(name = "type", typeAffinity = ColumnInfo.TEXT)
private String mType;
#ColumnInfo(name = "timestamp", typeAffinity = ColumnInfo.INTEGER)
private long mTimestamp;
// constructor, accessors
}
When I try to compile my project, it fails with no specific error.
If I try to compile it with a flat entity hierarchy, all is well.
So, my main question is:
Does Room support entity inheritance? Will it be able to get the column definitions from the parent Entity class?
I would also like to know if extending BaseObservable (which I need to get the Data Binding working) can cause problems with Room? BaseObservable has one private transient field, so maybe this is causing some issues with the code generation.
Are there any recommended patterns to deal with this, or will I just have to flatten my entity hierarchy?
After further investigation it turns out that Room Entities should not extend the BaseObservable class. It contains fields that can't be marked with #Ignore and break the code generation.
Room works well with inheritance. The annotations are processed as expected and the DB operations behave normally. You can extend from both an Entity and a POJO.

How to explictly state that an Entity is new (transient) in JPA?

I am using a Spring Data JpaRepository, with Hibernate as JPA provider.
Normally when working directly with Hibernate, the decision between EntityManager#persist() and EntityManager#save() is up to the programmer. With Spring Data repositories, there is only save(). I do not want to discuss the pros and cons here. Let us consider the following, simple base class:
#MappedSuperclass
public abstract class PersistableObject {
#Id
private String id;
public PersistableObject(){
this.id = UUID.randomUUID().toString();
}
// hashCode() and equals() are implemented based on equality of 'id'
}
Using this base class, the Spring Data repository cannot tell which Entities are "new" (have not been saved to DB yet), as the regular check for id == null clearly does not work in this case, because the UUIDs are eagerly assigned to ensure the correctness of equals() and hashCode(). So what the repository seems to do is to always invoke EntityManager#merge() - which is clearly inefficient for transient entities.
The question is: how do I tell JPA (or Spring Data) that an Entity is new, such that it uses EntityManager#persist() instead of #merge() if possible?
I was thinking about something along these lines (using JPA lifecycle callbacks):
#MappedSuperclass
public abstract class PersistableObject {
#Transient
private boolean isNew = true; // by default, treat entity as new
#PostLoad
private void loaded(){
// a loaded entity is never new
this.isNew = false;
}
#PostPersist
private void saved(){
// a saved entity is not new anymore
this.isNew = false;
}
// how do I get JPA (or Spring Data) to use this method?
public boolean isNew(){
return this.isNew;
}
// all other properties, constructor, hashCode() and equals same as above
}
I'd like to add one more remark here. Even though it only works for Spring Data and not for general JPA, I think it's worth mentioning that Spring provides the Persistable<T> interface which has two methods:
T getId();
boolean isNew();
By implementing this interface (e.g. as in the opening question post), the Spring Data JpaRepositories will ask the entity itself if it is new or not, which can be pretty handy in certain cases.
Maybe you should add #Version column:
#Version
private Long version
in the case of new entity it will be null

Resources