I have a problem when I use Value object with JPA - java

I must use ValueObject in the project and JPA at the same time, but when changing the attribute to valueobject it gives me an error, I don't know yet how to solve the problem
this error: ('Id' attribute type should not be 'BrandCodigo') ('Basic' attribute type should not be 'BrandNombre' )
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
#Entity(name = "Brand")
#Table(name = "marcas",
uniqueConstraints = {
#UniqueConstraint(name = "uk_marcas_nombre",columnNames = "nombre")
})
public class Brand {
#Id
#Column(name = "codigo")
private BrandCodigo codigo;
#Column(name = "nombre",
nullable = false,
columnDefinition = "varchar(80)"
)
private BrandNombre nombre;
}

When you refer to lombok's #Value then this is not possible afaik. Lombok's #Value is for immutable objects.
But your entity needs to be mutable since the way JPA constructs it.
Furthermore value objects do not have an identity but database entities should have primary keys.

Related

Invalid Identifier in Hibernate Filter defaultCondition when trying to access a field of one of it's subfields

I am working on setting up some entities for a project that I am working on. My issue is that I am getting an invalid identifier error on my hibernate filters. Below is a simplified example:
#Data
#Entity
#Table(name = "###")
public class C1 {
#OneToMany(mappedBy = "###")
#Filter(name = "C2.set1Filter")
private Set<C2> set1;
#OneToMany(mappedBy = "###")
#Filter(name = "C2.set2Filter")
private Set<C2> set2;
#OneToMany(mappedBy = "###")
#Filter(name = "C2.set3Filter")
private Set<C2> set3;
}
#Data
#Entity
#Table(name = "###")
#FilterDefs({
#FilterDef(
name = "set1Filter"
defaultCondition = "c3.value = 'One'"
),
#FilterDef(
name = "set2Filter"
defaultCondition = "c3.value = 'Two'"
),
#FilterDef(
name = "set3Filter"
defaultCondition = "c3.value = 'Three'"
)
})
public class C2 {
#ManyToOne
#JoinColumn(name = "ID")
C3 c3;
}
#Data
#Entity
#Table(name = "###")
public class C3 {
#Id
#Column(name = "ID")
private Long id;
#Column(name = "VALUE")
private String value;
}
When the filters are off, they don't run, and therefore I don't get an error. However, when they are on, I am getting an error saying that c3.value is an invalid identifier.
I am obviously doing something wrong here. Is it possible to get the above to work?
As it stated in the documentation:
The #Filter condition uses a SQL condition and not a JPQL filtering predicate.
So, you should use actual tables and columns names.
#Filter(
name="setFilter",
condition="{c3}.VALUE = :val",
aliases = {
#SqlFragmentAlias( alias = "c3", table= "C3_TABLE_NAME")
}
)
As it stated here:
When using the #Filter annotation and working with entities that are mapped onto multiple database tables, you will need to use the #SqlFragmentAlias annotation if the #Filter defines a condition that uses predicates across multiple tables.

Strange Lombok behaviour with Quarkus and Jackson

at the moment i'm facing a strange issue. I use lombok in my Quarkus project to have getter, setter etc. generated automatically. When i compile Quarkus to a native image, Jackson refuses to serialize a Lombok-Data-Object, but serializes a different one without problems.
Even stranger is, that this error only occurs when i compile a native binary and embed it into a container. Running both examples in the "quarkus:dev" profile works flawless.
Objects from this class get serialized:
#Data
#Entity
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "accounts")
public class AccountEntity {
#Id
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
#Column(name = "id", updatable = false, nullable = false)
private UUID id;
#Column(unique = true, name = "username", nullable = false)
private String username;
#Column(unique = true, name = "mail", nullable = false)
private String mail;
#Column(name = "password", nullable = false)
private String password;
}
Objects from this class get not:
#Getter
#AllArgsConstructor
public class LoginResponse {
private final String token;
}
The error message:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class de.alexzimmer.pwa.model.LoginResponse and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
But even if i take a look into the generated class-files, i can see public getters for both classes getting generated. I'm thankful for any advices and thoughts of how this could happen.
Thanks!
You have to register this class for reflection by adding the #RegisterForReflection annotation.
It works for the first object as it's an entity and this is done automatically.
See https://quarkus.io/guides/writing-native-applications-tips#registering-for-reflection for a full explanation.
I will probably add the Jackson error message there so that it can be found more easily.

#Column and #Enumerated doesn't work in embeded entity

I have main entity:
#Entity
#Table(name = "partners")
public class Partner {
#ElementCollection
#CollectionTable(
name = "external_login",
joinColumns = #JoinColumn(name = "partner_id")
)
private List<ExternalLogin> externalLogins;
...
}
And ExternalLogin is embeded entity
#Embeddable
public class ExternalLogin {
#Column(name = "type")
#Enumerated(value = EnumType.STRING)
private ExternalLoginType type;
#Column(name = "login")
private String login;
#Column(name = "password_value")
private String passwordValue;
}
public enum ExternalLoginType {
ABC;
}
#Column and #Enumerated not works in ExternalLogin entity.
For example in query will be external_login.passwordValue instead of external_login.password_value.
#Enumerated(value = EnumType.STRING) doesn't work too. Hibernate is trying to get int value of filed instead string.
Can anyone help me?
You misuse annotation #Embeddable. See description in oracle docs https://docs.oracle.com/javaee/6/api/javax/persistence/Embeddable.html
Defines a class whose instances are stored as an intrinsic part of an owning entity and share the identity of the entity. Each of the persistent properties or fields of the embedded object is mapped to the database table for the entit
#Embeddable annotation makes sense only for singular assotiation fields. Annotating list fields as #Embeddable is wrong.
Just replace
#Embeddable
public class ExternalLogin {
to
#Entity
public class ExternalLogin {
I had exactly the same issue just now.
The solution for me ended up being adding
#Access(FIELD)
To the Embeddable object.

Spring Boot JPA - OneToMany relationship causes infinite loop

I have a two objects with simple #OneToMany relationship which looks as follows:
parent:
#Entity
public class ParentAccount {
#Id
#GeneratedValue
private long id;
private String name;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "parentAccount")
private Set<LinkedAccount> linkedAccounts;
}
child:
#Entity
public class LinkedAccount {
#Id
#GeneratedValue
private long id;
#ManyToOne(optional = false)
private ParentAccount parentAccount;
private String name;
// empty constructor for JPA
public LinkedAccount() {
}
}
I ma using Spring CrudRepository to operate with these entities. However, when calling ParentAccount parent = parentAccountRepository.findOne(id);, some kind of infinite loop starts happening and hibernate spams this all over the console:
Hibernate: select linkedacco0_.parent_account_id as parent_a6_1_0_, linkedacco0_.id as id1_0_0_, linkedacco0_.id as id1_0_1_, linkedacco0_.aws_id as aws_id2_0_1_, linkedacco0_.key_id as key_id3_0_1_, linkedacco0_.name as name4_0_1_, linkedacco0_.parent_account_id as parent_a6_0_1_, linkedacco0_.secret_key as secret_k5_0_1_ from linked_account linkedacco0_ where linkedacco0_.parent_account_id=?
I tried changed the fetch type to LAZY but then I get this error:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.berrycloud.scheduler.model.ParentAccount.linkedAccounts, could not initialize proxy - no Session
(It seems that it is trying to do the lazy load outside of the transactional context).
This is my CRUD repository:
#Repository
public interface ParentAccountRepository extends CrudRepository<ParentAccount, Long> {
}
Could someone tell me how to resolve this issue? I would prefer the solution with EAGER fetch. Thank you for any tips
EDIT: here is the schema I am using
CREATE TABLE parent_account (
id BIGINT auto_increment,
name VARCHAR(80) null,
PRIMARY KEY (`id`)
);
CREATE TABLE linked_account (
id BIGINT auto_increment,
parent_account_id BIGINT,
name VARCHAR(80) null,
FOREIGN KEY (`parent_account_id`) REFERENCES `parent_account` (`id`),
PRIMARY KEY (`id`)
);
As the first answer suggests:
Do not use Lombok's #Data annotation on #Entity classes.
Reason: #Data generates hashcode(), equals() and toString() methods that use the generated getters. Using the getter means of course fetching new data even if the property was marked with FetchType=LAZY.
Somewhere along the way hibernate tries to log the data with toString() and it crashes.
Problem solved. I was using a custom #toString method in the LinkedAccount which was referencing the ParentAccount. I had no idea that this could cause any problem and therefor I did not include the toString in my question.
Apparently, this was causing an infinite loop of lazy loading and removing this reference fixed the problem.
As user1819111 told, #Data from Lombok is not compatible with #Entity and FetchType=LAZY. I had used Lombok.Data (#Data) and I was getting this error.
As I don't want do create all get/set, I just put the Lombok #Setter and #Getter in your class and all will work fine.
#Setter
#Getter
#Entity
#Table(name = "file")
#SequenceGenerator(name = "File_Sequence", allocationSize=1, sequenceName = "file_id_seq")
public class MyClass{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "File_Sequence")
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy = "file", cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
private Set<Base2FileDetail> details = new HashSet<>();
}
Something like this does not work?
#Entity
public class Account {
#Id
#GeneratedValue
private long id;
private String name;
#ManyToOne(cascade={CascadeType.ALL})
#JoinColumn(name="manager_id")
private Account manager;
#OneToMany((fetch = FetchType.EAGER, mappedBy="manager")
private Set<Account> linkedAccounts = new HashSet<Account>();
}
I recently had this issue due to a poorly defined Jackson2HttpMessageConverter.
I had done something like the following.
#Bean
RestTemplate restTemplate(#Qualifier("halJacksonHttpMessageConverter")
TypeConstrainedMappingJackson2HttpMessageConverter halConverter) {
final RestTemplate template = new RestTemplateBuilder().build();
halConverter.setSupportedMediaTypes(List.of(/* some media types */));
final List<HttpMessageConverter<?>> converters = template.getMessageConverters();
converters.add(halConverter);
template.setMessageConverters(converters);
return template;
}
This caused a problem because the media types did not include all the defaults. Changing it to the following fixed the issue for me.
halConverter.setSupportedMediaTypes(
new ImmutableList.Builder<MediaType>()
.addAll(halConverter.getSupportedMediaTypes())
.add(/* my custom media type */)
.build()
);
This simple way worked for me. Just use JsonIgnoreProperties .
#JsonIgnoreProperties(value = {"linkedAccounts"})
#ManyToOne(cascade = { CascadeType.PERSIST})
#JoinColumn(name = "abc", referencedColumnName = "abc")
private ParentAccount parentAccount;
This way worked for me without removing #ToSring annotation:
#Entity
#Getter
#Setter
#ToString
#RequiredArgsConstructor
#AllArgsConstructor
#Table(name = "parent_accounts")
public class ParentAccount {
#JsonIgnoreProperties({"parentAccount"})
#OneToMany(mappedBy = "parentAccount",
cascade = CascadeType.ALL,
orphanRemoval = true)
private List<LinkedAccount> linkedAcounts;
// ...
}
#Entity
#Getter
#Setter
#ToString
#RequiredArgsConstructor
#AllArgsConstructor
#Table(name = "linked_accounts")
public class LinkedAccount {
#JsonIgnoreProperties("linkedAcounts")
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "parentAccount_id")
private ParentAccount parentAccount;
// ...
}
PS: In #JsonIgnoreProperties You can also ignore more than one field for preventing infinite loop

JPA column with incorrect underscore

I use JPA for database access and annotated every column with the correct name. Now if I execute a query (e.g. findAll()) it returns
Unknown column 'program0_.program_id' in 'field list'
The error message is correct program_id is unknown because the real name is programId.
Models: Program
#Entity
#Table(name = "programs")
#XmlRootElement
public class Program implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "programId")
private Long programId;
#ManyToMany
#JoinTable(
name = "programlabels",
joinColumns = {
#JoinColumn(name = "program", referencedColumnName = "programId")},
inverseJoinColumns = {
#JoinColumn(name = "label", referencedColumnName = "labelId")})
private Collection<Label> labels;
}
Label
#Entity
#Table(name = "labels")
#XmlRootElement
public class Label implements Serializable {
#Id
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 100)
#Column(name = "labelId")
private String labelId;
}
Query
select program0_.program_id as program_1_5_, ...
Is there a reason why JPA changes "programId" to "program_id" or am I missing any configuration?
thanks
Edit: Oh sorry forgot to add query code/information.
I use the Spring Data's JpaRepository interface and tried the findAll() query.
#Repository
public interface ProgramRepository extends JpaRepository<Program, Long> {}
http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
spring.jpa.hibernate.naming.strategy is not a supported property for Spring JPA implementation using Hibernate 5.
Use the below property in application.properties
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
As described in spring-boot-jpa-column-name-annotation-ignored, your column name is being converted to snake case.
Possible solutions:
Setup a Naming Strategy
Use lowercase column names in your annotations
Were able to map
#Column(name = "PersonFullName")
private String PersonFullName;
to the database table column name "PersonFullName" without the underscore.
The below worked for me. Add this in the application settings and then use #Column to specify the physical database column name for the model's property.
#Column(name = "PersonFullName")
In Application.properties
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
use below in application.properties
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

Categories

Resources