How to declare Int as foreign key in Java Spring Boot - java

So per default its like this:
public class MyEntity{
private int id;
#OneToOneOrWhatever
private MyOtherEntity other;
}
You can make a relation simply by adding the object to it. But in my case, loading the whole object with eager loading would be simply overkill. I just want to retrieve the id of "MyOtherEntity" , nothing else.
Is something like this:
public class MyEntity{
private int id;
#SomeFancyAnnotationToRelateTo(MyOtherEntity.class)
private int foreignKeyId;
}
possible?

This may do it.
public class MyEntity{
private int id;
#OneToOne(fetch = FetchType.LAZY)
#Fetch(FetchMode.JOIN)
#JoinColumn(name = "myTypeId")
private MyType myType;
}

Simply use the FetchType.LAZY.
public class MyEntity{
private int id;
#ManyToOne(fetch = FetchType.LAZY)
private MyOtherEntity other;
}
And then you can access the id via myEntity.getMyOtherEntity().getId();
Some explanation:
If lazy-loading is set on a property of an entity, when Hibernate loads that entity from the DB, instead of creating the appropriate property Object and loading all of its properties from its own table, instead it creates a 'proxy' for that object and stores only the id in it.
When you later try to access any of the properties of this proxied object at the first time, it recognizes that the object is not initialized and does load the object.
They usually say the object will be loaded when you access it at the first time.
But the 'id' field is an exception. It is already present in the proxy object too! (It's quite logical: in order to load the whole entity from DB you need the id anyway).
So as long as you access only the id field the other fields won't be loaded. (there will be no access to OtherEntity's DB table)
Although a proxy object is created plus some additional JPA/hibernate magic happens in the background, it's still the most efficient way to access the id field. (Also, if you later still some other fields, they will be loaded automatically)

Related

ManyToOne relationship is always null when finding entity in the same transaction

I have a ConversationEntity with a ManyToOne relationship with ModeratorEntity
#Entity
#Table(name="CONVERSATIONS")
public class ConversationEntity {
#Id
private Integer id;
private Integer moderatorId;
#ManyToOne
#JoinColumn(name="moderatorId", insertable=false, updatable=false)
private ModeratorEntity moderator;
}
#Entity
#Table(name="MODERATORS")
public class ModeratorEntity {
#Id
private Integer id;
private Integer name;
}
And a service class with a transactional method that first of all saves the ModeratorEntity and after that the ConversationEntity with the moderatorId previously created
#Transactional
public void doStuff(Moderator moderator, Integer conversationId) {
Integer moderatorId = moderatorService.save(moderator);
Integer conversationId = conversationService.save(conversationId, moderatorId);
//do other stuff
Conversation conversation = conversationService.findById(conversationId);
}
When I'm trying to find the ConversationEntity by the id in the same transaction, a few lines below, I'm getting the ConversationEntity with the field moderatorId set but with the ModeratorEntity object = null.
If I do this outside the transaction I'm getting the ModeratorEntity object properly set.
I tried using saveAndFlush in ModeratorRepository and ConversationRepository and set FetchType.EAGER in the ManyToOne relationship but none of them worked
There are many questions about similar problems. For example #Transactional in bidirectional relation with Spring Data returns null and Hibernate: comparing current & previous record.
As long as you are within a single transaction you'll always get the same instance and no data is actually loaded from the database. And since (it seems at least) you never set the reference to the ModeratorEntity it stays null.
Once you are in a new transaction the database gets accessed and JPA populates a new instance, now including a ModeratorEntity reference.
The possible fixes therefore are:
Instead of an id let moderatorService.save return an entity and set that in the Conversation. You might as well drop the moderatorId. This is the idiomatic way to do things with JPA.
Perform the query in a separate transaction. Springs TransactionTemplate might come in handy. While this does work it causes JPA internals to bleed into your application which I recommend to avoid.

How to index a boolean #Field in Hibernate Search if the field is not in the db table

I have a problem with indexing the boolean #field in Hibernate Search, the problem is when the object has changed the rest of the fields are changed as well only the boolean field keeps the old state of the object.
#JsonIgnore
#Field(name = "isWarning", index = Index.YES)
#SortableField(forField = "isWarning")
private boolean isWarning() {
//some logic
}
what is the right way to approach this problem?
I assume this "logic" you mention accesses other entities. You need to tell Hibernate Search that those entities are included in the entity with the isWarning method.
Let's say the isWarning method is defined in an entity called MainEntity, and it accesses data from another entity called SomeOtherEntity.
In SomeOtherEntity, you will have the reverse side of the association:
public class SomeOtherEntity {
#ManyToOne // Or #OneToOne, or whatever
private MainEntity mainEntity;
}
Just add #ContainedIn and you should be good:
public class SomeOtherEntity {
#ManyToOne // Or #OneToOne, or whatever
#ContainedIn
private MainEntity mainEntity;
}
Note that, unfortunately, this can have a significant impact in terms of performance if SomeOtherEntity is frequently updated: Hibernate Search will not be aware of exactly which part of SomeOtherEntity is used in MainEntity, and thus will reindex MainEntity each time SomeOtherEntity changes, even if the changes in SomeOtherEntity don't affect the result of isWarning. A ticket has been filed to address this issue, but it's still pending.

Override Spring data JPA id annotation.

I'm using Spring-data-Jpa where I've an entity
#Entity(name="person")
public class Person implements Serializable {
#javax.persistence.Id
private long dbId;
#Id
private final String id;
// others attributes removed
}
In above class I've two different ids id (marked with org.springframework.data.annotation.Id) and dbId(marked with javax.persistence.Id) , since my id field is always populated with a unique identifier (for Person class which I'm getting from somewhere else) so while using Spring JpaRepository it always tries to update the record and since it's not in db, nothing happens.
I've debug code and saw that it uses SimpleKeyValueRepository which gets the id field which is id, and thus it always gets a value and tries to update record, can I override this behavior to use dbId instead of id field? Is there any way to achieve same with some configuration or annotation, any help is greatly appreciated.
Each entity must have exactly one #Id. On the other hand, you might want to declare a column as unique. It can be done by:
#Entity(name="person")
public class Person implements Serializable {
#Id
private Long id;
#Column(unique = true)
private final String uuid;
// others attributes removed
}
Also remember, that Spring Data JPA id should be reference Long instead of a primitive as you want to save objects with id = null.
String id should probably be String uuid and be initialized as String uuid = UUID.randomUUID().toString();
Similar situation would be an unique email requirement for user. On one hand it'll be a primary key, but on the other, you won't mark it as #Id.
If you need further clarification or your environment is more complicated, just ask in comments section below.

Spring Data JPA get entity foreign key without causing the dependent entity lazy load

I have an #Entity A that references another entity B using OneToOne relation ship. I fetch entity A using spring data JpaRepository
A a = aRepository.findById(1);
int b_id = a.getB().getId();
As you can see I need to query ID of the B table, however in order to do that, I need to call getter of the B table, which will cause lazy-loading the B table itself. I do not want to do that because the only thing I need is the get ID, nothing else, and that ID is present in the first A table.
Is there any trick that will help me to get ID of the dependent table without triggering new query?
UPDATE
#Entity
class A {
#Id
private Long id;
#OneToOne
private B b;
}
#Entity
class {
#Id
private Long id;
}
Without looking at the entity mapping, I suspect, your entity classes might be using hibernate annotations on the field. With this if you call even the getId() method as in a.getB().getId() on the entity it will result in initializing the proxy (i.e., B object) and hits the database to fetch it.
So if the intent is only to get the id of the entity you can place the hibernate annotations on the getter methods instead. This doesn't result initializing the proxy (B object) to return the id. Although accessing any property other than id will result in hitting the database.
Have a look at related bug at HHH-3718
So, try using property/getter AccessType instead of field access. As an example instead of placing the annotations on field
#Id
#GeneratedValue(...)
private long id;
place them on the getters
#Id
#GeneratedValue(...)
public long getId() { ... }
Make sure you make similar changes to all the fields of B entity. Although you can explore #Access(AccessType.PROPERTY/FIELD) later.
There is already a related bug HHH-3718 regarding this behavior.
And a related topic on hibernate forum regarding field vs property access type that might be of interest for you Field Vs Property access
Posting your entities classes would help, if this doesn't resolve the issue.

Relation between type, attribute, instance and value

I'm developing an Java-application which stores its data via Hibernate in a database.
One feature of this application is to define templates like types, etc. for reuse. For instance the type has attributes and you can create instances of an type, which has values for the attributes.
The problem is, that I don't know how to ensure that only values for attributes can assigned which the type defines. In my solution there is a redundancy which cause the problem, but I don't know how to remove it.
My current (and problematic) approach looks like this:
#Entity
class Type
{
#Id
#Generated
private Long id;
#OneToMany(mappedBy="type")
private List<Attribute> attributes;
//...
}
#Entity
class Attribute
{
#Id
#Generated
private Long id;
#ManyToOne
private Type type;
//...
}
#Entity
class Instance
{
#Id
#Generated
private Long id;
#ManyToOne
private Type type;
//...
}
#Entity
class AttributeValue
{
#Id
#Embedded
private ResourceAttributValueId id;
#Column(name="val")
private String value;
//...
}
#Embeddable
public class ResourceAttributValueId implements Serializable
{
#ManyToOne
private ResourceStateImpl resource;
#ManyToOne
private ResourceAttributeImpl attribute;
//...
}
There the definition of the type is redundant: Type can be reached via AttributeValue->Attribute->Type and AttributeValue->Instance->Type
Another idea was to use type + attribute name as id of the attribute and instance + attribute name as id of the attribute value, but that doesn't solves my problem.
The key for correctly modeling "diamond-shaped" dependencies like this is the usage of identifying relationships:
(I took a liberty of renaming your entities slightly, to what I believe is a more consistent naming scheme.)
Note how we migrate the TYPE_ID from the top of the diamond, down both sides, all the way to the bottom and then merge it there. So, since there is only one ATTRIBUTE_INSTANCE.TYPE_ID field and is involved in both FKs, we can never have an attribute instance whose attribute type's type differs from instance's type.
While this avoids "mismatched" attributes, it still doesn't ensure the presence of attribute instances (if you support the concept of "required attribute"), which is best enforced at the application level. Theoretically you could enforce it at the database level, using circular deferred FKs, but not all DBMSes support that, and I doubt it would play nicely with ORMs.
Unfortunately, I'm not experienced enough with Hibernate to answer whether this can be mapped there and how.
See also:
Choosing from multiple candidate keys
How to keep foreign key relations consistent in a “diamond-shaped” system of relationships

Categories