I'm building an application to create cards through the Trello API. I need it to persist the card information on local database, so the card entity is mapped like this:
public class Card {
#Id
#Column(nullable = false)
private String cardId;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
private BoardList list;
#Column
private String cardName;
#Lob
#Column
private String cardDesc;
}
I understand that hibernate needs to be sure that the id will be assigned, in fact, I've had a similar problem with other entities, which I fixed by simply using #Column(nullable = false).
I don't want to use Auto Generated values cause Trello provides me with a unique id for each created card that I can use for querying the API.
Nonetheless, with the Card entity I'm getting a JpaSystemException telling me that "ids for this class must be manually assigned before calling save()". How can I fix it?
Related
Update
I'd like to note that #sainr's answer Converting Hibernate proxy to real entity object does solve the problem. But the issue behind the scene is my SiteEntity having a final modifier of it's setControllerEntity and getControllerEntity, which I didn't raise in my question. And I apologize.
Remove the final modifier. Then Hibernate can initialize the proxy objects just fine.
The explanation can be found in another answer on Stack Overflow.
I have three entities as following
#Entity
#Table(name = "controller")
public class ControllerEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false, updatable = false)
private long id;
}
#Entity
#Table(name = "site")
public class SiteEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false)
private long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "controller_id", nullable = false)
private ControllerEntity controllerEntity;
}
#Entity
#Table(name = "device")
public class DeviceEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false)
private long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "site_id", nullable = true)
private SiteEntity siteEntity;
}
After I found the device entity, I try to get the controllerEntity directly from it.
final DeviceEntity deviceEntity1 = deviceRepository.findOne(1L);
System.out.println(deviceEntity1.getSiteEntity().getControllerEntity().getId());
But it results a java.lang.NullPointerException which is caused by the null controllerEntity in the siteEntity.
Also, even if I tried to use siteRepositoy to fetch the siteEntity again. the controllerEntity of it is still null.
After I removed the fetch = FetchType.LAZY from both the DeviceEntity and SiteEntity, NPE doesn't happen anymore.
But it seems odd and doesn't make sense. Can I use FetchType.LAZY while expecting hibernate fetch the correct value?
Thanks.
To give you an access to the field declared with FetchType.LAZY, Hibernate constructs a proxy with CGLIB. Consequently, when you're calling a getter for such field (in your case, getSiteEntity() or getControllerEntity()), you're not accessing the field value directly -- instead, the call is passed to the proxy object of Hibernate. In turn, Hibernate tries to load the actual value from the data store and in order to do this, it would require an active Hibernate session to access the DB. Most likely, in your case, the Hibernate session is already closed and such lazy load fails, giving you an effectively null value of the field.
There are basically two ways to solve this:
Use FetchType.EAGER that would load all field values along with the holding object DeviceEntity
Transform a proxy object into a real object (check Converting Hibernate proxy to real entity object) and access it in a regular way
Think about it, whether you really need a lazy load in your case. If you are not storing plenty of heavy objects in child fields to load them on demand, probably switching to FetchType.EAGER will be the easiest way.
Hope that helps.
Hibernate work with primitive types sometimes not very well. Try to replace
private long id
to
private Long id
For primary keys in Hibernate it is better to use wrapper classes instead of primitive types.
I have an JPA entity with contains a ManyToOne reference to another table, a simplified version of that entity is shown below:
#Entity
#Table(name = "ENTITIES")
public class Entity implements Serializable {
#Id #NotNull
private String id;
#JoinColumn(name = "REFERENCE", referencedColumnName = "ID")
#ManyToOne(optional = false)
private ReferencedEntity referencedEntity;
}
#Entity
#Table(name = "REFERENCES")
public class ReferencedEntity implements Serializable {
#Id #NotNull #Column(name = "ID")
private String id;
#Size(max = 50) #Column(name = "DSC")
private String description;
}
Finding entities works fine. Peristing entities also works fine, a bit too good in my particular setup, I need some extra validation.
Problem
My requirement is that the rows in table REFERENCES are static and should not be modified or new rows added.
Currently when I create a new Entity instance with a non-existing (yet) ReferencedEntity and persist that instance, a new row is added to REFERENCES.
Right now I've implemented this check in my own validate() method before calling the persist(), but I'd rather do it more elegantly.
Using an enum instead of a real entity is not an option, I want to add rows myself without a rebuild/redeployment several times in the future.
My question
What is the best way to implement a check like this?
Is there some BV annotation/constraint that helps me restrict this? Maybe a third party library?
It sounds like you need to first do a DB query to check if the value exists and then insert the record. This must be done in a transaction in order to ensure that the result of the query is still true at the time of insertion. I had a similar problem half a year back which might provide you with some leads on how to set up locking. Please see this SO question.
You should add this => insertable=false, updatable=false
And remove => optional=false , and maybe try nullable=true
I have created a trigger so that my entities ids are autogenerated with a sequence each time they're inserted into my Oracle database.
The problem comes with annotating these entities for Hibernate/JPA: I need to define a #GeneratedValue annotation but I don't want to specify the sequence name -- doing that will make Hibernate query the sequence first, then insert, which is a work that is already done by the trigger.
Is there any way to skip this sequence in the #GeneratedValue with the scenario I've proposed?
Exception I get if id is not provided:
org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save(): Pattern
Pattern class:
#Entity
#Table(name = "PATTERN")
public class Patron extends HistoricoAbstractEntity {
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ID_PATTERN")
private Integer idPattern;
#Column
private String description;
#Column(name = "NEGATIVE")
private Boolean isNegative;
#Column(name = "type")
private Integer type;
#Column(name = "N_DAYS")
private Integer numDays;
... (getters & setters)
}
From what your code,
What I can tell you is that its not related to #GeneratedValue, it specifies that the hibernate takes responsibility to generate and idetifier for your entity. In your case your are generating id your self, so you have to manually set the id for that particular entity. Then you won't get this error any more, the other thing that you can try is use of #PrePersist annotate a method with this and try assigning a value to id in it. I haven't tried this but this should work according to this answer on SO.
Assign Custom Identifier
If your id is being generated by the database then you should use #GeneratedValue(strategy=GenerationType.AUTO) on your id field along with your #Id annotation.
I am getting started with Hibernate Search/Lucene using Spring Boot and Spring Data, but I am having an issue with the index not getting updated (Checked with Luke tool).
I have 3 classes in my domain. This is Datasheet, my root entity:
#Entity
#Indexed
public class Datasheet
{
#Id
#GeneratedValue()
private long m_id;
#Field(name="name")
private String m_name;
#Field(name="description")
private String m_description;
#IndexedEmbedded(prefix = "documents.")
#OneToMany(cascade = CascadeType.REMOVE)
private Set<DatasheetDocument> m_documents;
}
Then DatasheetDocument:
#Entity
public class DatasheetDocument
{
#Id
#GeneratedValue()
private long m_id;
private String m_originalFileName;
#Field(name="componentName")
private String m_componentName;
#IndexedEmbedded(prefix = "manufacturer.")
#ManyToOne
private Manufacturer m_manufacturer;
}
And finally Manufacturer:
#Entity
public class Manufacturer
{
#Id
#GeneratedValue()
private long m_id;
#Field(name="name", analyze = Analyze.NO)
private String m_name;
private String m_website;
}
When I explicitly call startAndWait() on the indexer (org.hibernate.search.MassIndexer), then everything is as expected in the index. It contains the fields name, description, documents.componentName and documents.manufacturer.name.
However, when I now do updates through my #RestController classes that call into Spring Data CrudRepository classes, the index only changes when changing a direct field of Datasheet (E.g. name or description). Changing something to the DatasheetDocument instances does not update the index. Any idea why this might be?
Note that I have tried to add backreferences to the parent. For DatasheetDocument:
#ManyToOne
#ContainedIn
private Datasheet m_datasheet;
And for Manufacturer:
#ManyToMany
#ContainedIn
private Set<DatasheetDocument> m_datasheetDocuments;
But that does not help.
I am using Spring boot 1.0.1 which includes Hibernate 4.3.1. I added Hibernate Search 4.5.1. I see that Lucense 3.6.2 gets added transitively as well.
You need the back references for sure. Without them and in particular without #ContainedIn there is no way for Search to know that it has to update the Datasheet index when the DatasheetDocument instance changes.
Have you added mappedBy to the one to many side?
#OneToMany(cascade = CascadeType.REMOVE, mappedBy="m_datasheet")
private Set<DatasheetDocument> m_documents;
Also, how to you update DatasheetDocument? Can you show the code? Either way, you will need to make the associations bi-directional to start with.
FullTextSession fullTextSession = Search.getFullTextSession(session);
fullTextSession.openSession()
Object customer = fullTextSession.load( Datasheet.class, datasheetDocument.getDatasheet.getId() );
fullTextSession.index(customer);
fullTextSession.flushIndex();
I am using Hibernate and JPA. If I have two simple entities:
#Entity
#Table(name = "container")
public class Container {
#Id
#Column(name="guid")
private String guid;
}
#Entity
#Table(name="item")
public class Item {
#Id
#Column(name="guid")
private String guid;
#Column(name="container_guid")
private String containerGuid;
}
and I want to insure that inserting an Item fails if the referenced Container does not exist. I would prefer not to have a Container object populated inside the item object (ManyToOne), how would I do this if it is possible to do?
You can declare arbitrary constraint using columnDefinition attribute:
#Column(name="container_guid",
columnDefinition = "VARCHAR(255) REFERENCES container(guid)")
private String containerGuid;
Note, however, that Hibernate doesn't know anything about this constraint, so that, for example, it may not perform inserts in proper order with respect of it and so on.
Therefore it would be better to create a #ManyToOne relationship. If you are afraid of extra SQL query for Container needed to set this property, you can use Session.load()/EntityManager.getReference() to get a proxy without issuing actulal query.
Try using below relationship mapping
RelationShip Mapping
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#ManyToOne()
#ManyToMany()
<>
#JoinColumn(name="<>")