HibernateProxy.toString lazy initializacion exception - java

I'm getting a weird error while I'm debugging my POC.
I have 2 entities:
#Getter
#Setter
#Entity
#Table(name = "APPLICANT")
public class Applicant implements Serializable {
private static final long serialVersionUID = 6060170457948717553L;
#Id
#Column(name = "applicant_id", insertable = false, nullable = false)
private Long applicantId;
#Column(name = "application_id", unique = true)
private String applicationId;
#OneToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "application_id", referencedColumnName = "application_id", insertable =
false, updatable = false)
private ApplicationEntity applicationEntity;
#Getter
#Setter
#Entity
#Table(name = "APPLICATION")
public class ApplicationEntity implements Serializable{
private static final long serialVersionUID = 7300036359295729197L;
#Id
#Column(name = "APPLICATION_ID")
private String id;
#OneToOne(mappedBy = "applicationEntity", fetch = FetchType.LAZY)
private Applicant applicant;
These classes has the repositories interfaces extending from CrudRepository, and in the Applicant repository I have a custom method to get the entity with the applicationId:
Applicant findByApplicationId(String applicationId);
But, when I'm debugging, I see the following message in the intellij debuguer on the applicationEntity attribute:
Method threw 'org.hibernate.LazyInitializationException' exception. Cannot evaluate org.example.postgres.jpa.model.ApplicationEntity$HibernateProxy$qa4PKx8V.toString()
The value qa4PKx8V changes every time that I perform a new test.
I tried a lot of combinations in the #Join annotation, I've deleted the lombook annotations, I've used the #Transactional annotation either, but is always the same error.
A key point to note, is that I can get the data from the table with any error, I just see this message in the debugger, so my question is, this is a thing of intellij or something like that? Or I need to fix this with configuration or changing something in my code?
Thanks.

I am assuming you have an autogenerated toString() implementation?
In general, you should avoid referencing lazily-loaded properties in toString(), equals(), hashCode() etc. Failing to do so will result in LazyInitializationException surprises like the one you're facing, triggered by the aforementioned methods whenever they try to access lazy properties outside of an active transaction context.
(This is indeed 'a thing of intellij', in the sense that although the debugged code is probably surrounded by a transaction, the Intellij inspector evaluates the expression on a separate thread where no transaction is active = no persistence context is open. Also, it will only happen with #XxxToOne(optional = false) properties)

Related

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.

JPA i18n issue with mapping columns

I'm working on a project, where some of the data are stored in multiple languages. For persistence I use Spring Data JPA with Hibernate. My model is the following:
I have an embeddable class for storing a string in a given locale:
#Embeddable
public class MultilingualData {
#Column(name = "locale", nullable = false)
#Convert(converter = LocaleConverter.class)
private Locale locale;
#Column(name = "value")
private String value;
}
And I also have a class, where I want to use that MultilingualData as a value of a Map, and the Locale in the MultilingualData as key for a better processing.
#Entity
#Table(name = "multilingual_string")
public class MultilingualString extends BaseEntity {
private static final long serialVersionUID = -2434353448112062292L;
#ElementCollection(fetch = FetchType.EAGER)
#MapKeyJoinColumn(name = "locale", insertable = false, updatable = false)
#CollectionTable(name = "multilingual_string_map",
joinColumns = #JoinColumn(name = "string_id"))
private Map<Locale, MultilingualData> localizedStrings;
A class where I use the MultilingualString:
#Entity
#Table(name = "location")
public class Location extends BaseEntity {
private static final long serialVersionUID = 1L;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "name")
private MultilingualString name;
So when JPA generates the tables, in the 'multilingual_string_map' two columns are generated for the locale itself. One with the name 'locale', as given in the model, and it creates another one with 'localized_strings_key' as part of the primary key. So when I store a multilingual data, the locale is saved twice.
I want to get rid of that extra column, because it is redundant. I know I did not configured the mapping right, so I hope someone can fix my mistake here.
Thanks in advance!
Why would you define the join column as insertable = false? Is the map never going to be populated from within your code?
In any case, #MapKeyJoinColumn is only used when the map key is an entity. #MapKeyColumn is what you want.
(the fact that #MapKeyColumn is missing from your model means Hibernate was forced to use a default name for the key column i.e. localized_strings_key. The fact that the locale column also got created despite the map key not being an entity is, well, undocumented behaviour)

Why is Hibernate querying twice?

While testing implementation of JPA into Spring I found out that my query is querying twice instead of once.
#Data
#Entity
#Table(name = "superfan_star")
public class Star implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false)
private int id;
private String name;
private String nickname;
private String description;
private String thumbnail;
private String backgroundImage;
private Date created;
private Date updated;
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "starId", referencedColumnName = "id", insertable = false, updatable = false)
private Set<Media> medias;
}
This is model class.
#Service
public class SuperfanStarService
{
#Autowired
private StarRepository starRepository;
#PersistenceContext
private EntityManager em;
#Transactional
public List<Star> getStars()
{
QStar qStar = QStar.star;
QMedia qMedia = QMedia.media;
List<Star> stars =
new JPAQuery(em)
.from(qStar)
.where(qStar.id.eq(19))
.list(qStar);
return stars;
}
}
This is my service class.
20160915 20:52:59.119 [http-nio-8080-exec-1] DEBUG j.sqlonly - org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:82)
9. select star0_.id as id1_2_, star0_.background_image as backgrou2_2_, star0_.created as created3_2_, star0_.description as
descript4_2_, star0_.name as name5_2_, star0_.nickname as
nickname6_2_, star0_.thumbnail as thumbnai7_2_, star0_.updated as
updated8_2_ from superfan_star star0_ inner join superfan_media
medias1_ on star0_.id=medias1_.star_id where star0_.id=19
20160915 20:52:59.173 [http-nio-8080-exec-1] DEBUG j.sqlonly - org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:82)
9. select medias0_.star_id as star_id11_2_0_, medias0_.id as id1_1_0_, medias0_.id as id1_1_1_, medias0_.created as created2_1_1_,
medias0_.description as descript3_1_1_, medias0_.end_time as
end_time4_1_1_, medias0_.is_approve as is_appro5_1_1_,
medias0_.is_approved_final as is_appro6_1_1_, medias0_.is_pushed as
is_pushe7_1_1_, medias0_.is_represent as is_repre8_1_1_,
medias0_.length as length9_1_1_, medias0_.released as release10_1_1_,
medias0_.star_id as star_id11_1_1_, medias0_.teleport_media_id as
telepor12_1_1_, medias0_.thumbnail as thumbna13_1_1_, medias0_.title
as title14_1_1_, medias0_.work_end as work_en15_1_1_,
medias0_.work_start as work_st16_1_1_, medias0_.youtube_id as
youtube17_1_1_, medias0_.youtube_title as youtube18_1_1_ from
superfan_media medias0_ where medias0_.star_id=19
As you can see, it's querying twice instead of once, probably because of inverse update? Is there any way to make my JPA model query only once?
This works as expected. The first query gets the Star entity with id = 19 from the database, and the second query gets the linked Media entities for that Star entity from the database. (Carefully look at the log of the SQL statements to understand what is being queried).
Note that you specified FetchType.EAGER on the medias field in class Star:
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "starId", referencedColumnName = "id", insertable = false, updatable = false)
private Set<Media> medias;
Eager fetching means that when you do a query for one or more Star objects, Hibernate immediately gets the linked Media objects - as opposed to lazy fetching, which means that the second query will not be done immediately, but only when necessary (when you access the medias member variable).
While there is an accepted answer I suspect there maybe something else at play here. I note you have a Lombok #Data which I believe overrides equals() and hashcode() based on all fields which is dangerous in a JPA entity as it can trigger lots of additional data being loaded when associated items are added to hash based collections.
Yeah I found out that Lombok is causing problems for lists as it's querying medias for each Star. I'm trying to see if there's a way to use Lombok without querying everything but there doesn't seem to be a way.
Firstly, I would suggest not implementing equals() and hashcode() based on all fields of your entity: that is the root cause of your problem and makes no sense anyway - base them on a unique business key if you have one available. Essentially two entities are equal if they have the same id but see here however:
The JPA hashCode() / equals() dilemma.
Additionally, hashcode() should be based on immutable fields - see here:
http://blog.mgm-tp.com/2012/03/hashset-java-puzzler/.
Lombok's #Data just aggregates other individual annotations. So you can remove it, use the individual #Getter #Setter and #ToString Lombok annotations and write your own sensible implementations of equals() and hashcode() when required:
https://projectlombok.org/features/Data.html

JPA 2 Hibernate mapping with composite key in primary key using #IdClass with 3 tier structure

This question is very similar to: JPA (Hibernate, EclipseLink) mapping: why doesn't this code work (chain of 2 relationships using JPA 2.0, #EmbeddedId composite PK-FK)?
Actually my only (from meaningful that I spotted) difference is that I use #IdClass and that I most probably won't be able to switch to a different provider than hibernate.
but anyway here is the code (removed parts that where unimportant):
PermissionContextType.java:
#Entity
#IdClass(PermissionContextTypePk.class)
public class PermissionContextType{
#Id
private String id;
#Id
#JoinColumn (name = "PROJECT", referencedColumnName = "ID")
#ManyToOne ()
private Project project;
public static class PermissionContextTypePk implements Serializable{
public String project;
public String id;
// ... eq and hashCode here ...
}
}
PermissionContext.java:
#Entity
#IdClass(PermissionContextPk.class)
public class PermissionContext{
#Id
private String id;
#Id
#JoinColumns ({
#JoinColumn (name = "PROJECT", referencedColumnName = "PROJECT"),
#JoinColumn (name = "PERMISSIONCONTEXTTYPE", referencedColumnName = "ID")
})
#ManyToOne
private PermissionContextType permissionContextType;
public static class PermissionContextPk implements Serializable{
public String id;
public PermissionContextTypePk permissionContextType;
// ... eq and hashCode here ...
}
}
Permission.java:
#Entity
#IdClass(PermissionPk.class)
public class Permission{
#Id
private String id;
#Id
#JoinColumns ({
#JoinColumn (name = "PROJECT", referencedColumnName = "PROJECT"),
#JoinColumn (name = "PERMISSIONCONTEXTTYPE", referencedColumnName = "PERMISSIONCONTEXTTYPE"),
#JoinColumn (name = "PERMISSIONCONTEXT", referencedColumnName = "ID")
})
#ManyToOne
private PermissionContext permissionContext;
public static class PermissionPk implements Serializable{
public String id;
public PermissionContextPk permissionContext;
// ... eq and hashCode here ...
}
}
and what I get is:
org.hibernate.AssertionFailure: Unexpected nested component on the referenced entity when mapping a #MapsId: PermissionContext
Caused by: org.hibernate.AssertionFailure: org.hibernate.AssertionFailure: Unexpected nested component on the referenced entity when mapping a #MapsId: PermissionContext
does anybody know if this is a hibernate bug and I should post it on their issue tracking system (and pray that I would be able to update to given hibernate version) or is there something fundamentally wrong with my way of binding the entities?
I've checked it with the hibernate implementation on EAP 6.1 (4.2.0) as well as on wildfly (don't really know which one.)
Ok, so this is what I found so far :
Thanks fr my friend : https://hibernate.atlassian.net/browse/HHH-5764 which most probably is the reason for this behaviour.
And I found a workaround :
Permission.java:
#Entity
#IdClass(PermissionPk.class)
public class Permission{
#Id
private String id;
// for the next 3 fields there are no public acessors, so the public API of the class was not changed !
#Id
#Column(name = "PROJECT")
private String projectId;
#Id
#Column(name = "PERMISSIONCONTEXTTYPE")
private String permissionContextTypeId;
#Id
#Column(name = "PERMISSIONCONTEXT")
private String permissionContextId;
#JoinColumns ({
#JoinColumn (name = "PROJECT", referencedColumnName = "PROJECT", updatable = false, insertable = false),
#JoinColumn (name = "PERMISSIONCONTEXTTYPE", referencedColumnName = "PERMISSIONCONTEXTTYPE", updatable = false, insertable = false),
#JoinColumn (name = "PERMISSIONCONTEXT", referencedColumnName = "ID", updatable = false, insertable = false)
})
#ManyToOne
private PermissionContext permissionContext;
public static class PermissionPk implements Serializable{
// previously they where private as well, but removed public constructor for the sake of simplicity of the question - so no changes where necesary in public API of the class !
private String id;
private String projectId;
private String permissionContextTypeId;
private String permissionContextId;
public PermissionPk () {}
public PermissionPk (String aId, PermissionContextPk aPermissionContext) {
this.id = aId;
permissionContextId = aPermissionContext.id;
permissionContextTypeId = aPermissionContext.permissionContextType.id;
projectId = aPermissionContext.permissionContextType.project;
}
... eq and hashCode here ...
}
}
The good thing about this workaround is that it does not change the public API of the class in any way
(the only change was that I needed to make fields in Pk's of context and contexttype visible to the PermissionPk - they where private before with only a public constructor [but again simplified for the question]), nor did it change the jpql queries, and at the same time workaround is scalable (to any tier amount - as long as every even pk does not contain another pk), so if the bug will be resolved it will be easy to remove the workaround.
I would still gladly accept any comments on either my workaround or the question in itself.
Today I found another workaround :)
You can omit #IdClass entirely and use hibernate specific ability to create composite keys on the fly as apparently it is not affected by this bug.
The drawback here is that:
it is entirely Hibernate specific not covered by JPA at all.
you cannot do em.find(ClassName.class,new ClassPk(args...)) as there is no ClassPk at all.
But if you could use anything else than hibernate you could just as well use something without this bug - so probably 1 is not a problem really. and there is a possibility that you don't really need the em.find for this entity (or can live with creating it thru session or jpql query).

Need an example of a primary-key #OneToOne mapping in Hibernate

Can somebody please give me an example of a unidirectional #OneToOne primary-key mapping in Hibernate ? I've tried numerous combinations, and so far the best thing I've gotten is this :
#Entity
#Table(name = "paper_cheque_stop_metadata")
#org.hibernate.annotations.Entity(mutable = false)
public class PaperChequeStopMetadata implements Serializable, SecurityEventAware {
private static final long serialVersionUID = 1L;
#Id
#JoinColumn(name = "paper_cheque_id")
#OneToOne(cascade = {}, fetch = FetchType.EAGER, optional = false, targetEntity = PaperCheque.class)
private PaperCheque paperCheque;
}
Whenever Hibernate tries to automatically generate the schema for the above mapping, it tries to create the primary key as a blob, instead of as a long, which is the id type of PaperCheque. Can somebody please help me ? If I can't get an exact solution, something close would do, but I'd appreciate any response.
I saved this discussion when I implemented a couple of #OneToOne mappings, I hope it can be of use to you too, but we don't let Hibernate create the database for us.
Note the GenericGenerator annotation.
Anyway, I have this code working:
#Entity
#Table(name = "message")
public class Message implements java.io.Serializable
{
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#PrimaryKeyJoinColumn(name = "id", referencedColumnName = "message_id")
public MessageContent getMessageContent()
{
return messageContent;
}
}
#Entity
#Table(name = "message_content")
#GenericGenerator(name = "MessageContent", strategy = "foreign",
parameters =
{
#org.hibernate.annotations.Parameter
(
name = "property", value = "message"
)
}
)
public class MessageContent implements java.io.Serializable
{
#Id
#Column(name = "message_id", unique = true, nullable = false)
// See http://forum.hibernate.org/viewtopic.php?p=2381079
#GeneratedValue(generator = "MessageContent")
public Integer getMessageId()
{
return this.messageId;
}
}
Your intention is to have a 1-1 relationship between PaperChequeStopMetaData and PaperCheque? If that's so, you can't define the PaperCheque instance as the #Id of PaperChequeStopMetaData, you have to define a separate #Id column in PaperChequeStopMetaData.
Thank you both for your answers. I kept experimenting, and here's what I got working :
#Entity
#Table(name = "paper_cheque_stop_metadata")
#org.hibernate.annotations.Entity(mutable = false)
public class PaperChequeStopMetadata implements Serializable, SecurityEventAware {
private static final long serialVersionUID = 1L;
#SuppressWarnings("unused")
#Id
#Column(name = "paper_cheque_id")
#AccessType("property")
private long id;
#OneToOne(cascade = {}, fetch = FetchType.EAGER, optional = false, targetEntity = PaperCheque.class)
#PrimaryKeyJoinColumn(name = "paper_cheque_id")
#JoinColumn(name = "paper_cheque_id", insertable = true)
#NotNull
private PaperCheque paperCheque;
#XmlAttribute(namespace = XMLNS, name = "paper-cheque-id", required = true)
public final long getId() {
return this.paperCheque.getId();
}
public final void setId(long id) {
//this.id = id;
//NOOP, this is essentially a pseudo-property
}
}
This is, by all means, a disgusting hack, but it gets me everything I wanted. The paperCheque property accessors are as normal (not shown). I've run into this kind of unidirectional OneToOne mapping problem before and settled for much worse solutions, but this time I decided I was going to figure out out, so I kept hacking away at it. Once again, thank you both for your answers, it's much appreciated.
Just updating this question for future views.
When this question was made i think there wasn't a proper solution for this problem. But since JPA 2.0 you can use #MapsId to solve this problem.
Reference with proper explanation: https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/
You should stay away from hibernate's OneToOne mapping, it is very dangerous. see http://opensource.atlassian.com/projects/hibernate/browse/HHH-2128
you are better off using ManyToOne mappings.

Categories

Resources