I’ve recently come across a problem with a class that has an embedded id. Whenever I want to update an existing entry in the database, I get the error “java.lang.RuntimeException: No #javax.persistence.Id field found in class”. I only get this error, when I use update() or save() on an object that's already an exciting db entry. Using save() to insert a new entry, works without a problem and so does deleting an existing entry with delete().
Someone else posted a question about this problem in the Play Framework Google Group, but sadly it never got answered. So I thought I'd try asking for help here.
Here's how my code basically looks:
#Entity
#Table(name = "files_location")
public class FilesLocation extends Model {
#EmbeddedId
public FilesLocationPK ids;
#Column(name="status")
public Character status;
#ManyToOne
#MapsId("fileId")
#JoinColumn(name = "file_id", referencedColumnName = "id", insertable = false, updatable = false)
public File file;
#ManyToOne
#MapsId("locationId")
#JoinColumn(name = "location_id", referencedColumnName = "id", insertable = false, updatable = false)
public Location location;
}
#Embeddable
public class FilesLocationPK {
#Column(name="file_id")
public Integer fileId;
#Column(name="location_id")
public Integer locationId;
...
}
The Error looks like this:
java.lang.RuntimeException: No #javax.persistence.Id field found in class [class models.FilesLocation]
at play.db.ebean.Model._idAccessors(Model.java:39)
at play.db.ebean.Model._getId(Model.java:52)
at play.db.ebean.Model.hashCode(Model.java:183)
at java.lang.Object.toString(Object.java:219)
at java.text.MessageFormat.subformat(Unknown Source)
at java.text.MessageFormat.format(Unknown Source)
at java.text.Format.format(Unknown Source)
at java.text.MessageFormat.format(Unknown Source)
at com.avaje.ebeaninternal.server.core.Message.msg(Message.java:39)
...
You need to use the #Id annotation on the key columns in your PK class.
Ebean is going to want to locate a sequence to use to generate these, as well.
Related
So I have a table with a column that has non-foreign key (no actual table reference) reference on another table but the other table might not have a matching row
class Component {
#OneToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "PART_ID", referencedColumnName="PART_ID", nullable = true, insertable = false, updatable = false)
#NotFound(action = NotFoundAction.IGNORE)
private Part part
#Id
#Column(name = "COMPONENT_ID")
private Long id;
}
Part class
class Part {
#Id
#Column(name = "PART_ID")
private Long id;
private String name;
}
without this #NotFound(action = NotFoundAction.IGNORE) I am getting an error
but with this I am not getting an error and get null value but I need the id to be present
e.g) I am looking for this
{"component": {"id":12, "part":{"id":100,"name":null}}}
but I am getting this (if no match)
{"component": {"id":12, "part":null}}
but I am getting this (if match)
{"component": {"id":12, "part":{"id":100,"name":"part_name"}}}
Tried with nullable=false and some combinations for updatable and insertable and still nothing works
If table has reference on another table but the other table might not have a matching row , the database structure is probably broken.
Do you really need what you're asking for?
The database has 2 part_id. One in Component and the other in Part. Java code has only one - Part.
You can make an additional field
сlass Component {
private Long partId;
...
and in the #PostLoad method - create a new Part with the desired id.
#PostLoad
public void postLoad(){
if(part == null && partId!=null){
part= new Part();
part.setId(partId)
}
}
}
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)
I'm getting a org.hibernate.PropertyValueException in Inscrito.postoAtendimento when I'm trying to save an instace of Inscricao.
public class Inscricao {
...
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "incCodigo", referencedColumnName = "incCodigo")
private Inscrito inscrito;
...
}
public class Inscrito {
...
#Column(name = "incPostoAtendimento")
private Boolean postoAtendimento;
...
}
However, the property postoAtendimento never is null (I'm able to get it on debuger) and I'm not using a bidirectional mapping.
I change my column on database to nullable and the object is save without problem and the value of incPostoAtendimento is set to false.
This doesn't make sense to me. Any help is welcome.
Thanks in advance.
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).
I am attempting to get Hibernate to lazy load some clobs. The loading portion is working just fine. The issue is when I try to create a new one. I started with advice from Blob lazy loading
Here are my mappings (Note the table structure is really really bad, there are multiple clobs on this table -- this example is simplified from my real model...).
#Entity #Table("TABLE_1")
public class BadDBDesign {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column("table_id")
private long key;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "table_id", referencedColumnName = "table_id",
insertable = true, updatable = false)
private BlobWrapperA;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "table_id", referencedColumnName = "table_id",
insertable = true, updatable = false)
private BlobWrapperB;
}
#Entity #Table(name = "TABLE_1")
public class BlobWrapperA {
#Lob
#Column(name = "col_A", nullable = false)
#Type(type = "org.springframework.orm.hibernate3.support.BlobByteArrayType")
private byte[] blobColA;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "table_id")
private long Key;
}
#Entity #Table(name = "TABLE_1")
public class BlobWrapperB {
#Lob
#Column(name = "col_B", nullable = false)
#Type(type = "org.springframework.orm.hibernate3.support.BlobByteArrayType")
private byte[] blobColB;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "table_id")
private long Key;
}
Application boots just fine, am able to retrieve the data without loading the clobs (am able to retrieve them when needed via lazy loading), but when I attempt to create the new ones I receive the following stacktrace:
Hibernate:
insert
into
TABLE_1
(key, col_A, col_B)
values
(?, ?, ?)
2011-08-31 17:35:09,089 [http-8080-1] DEBUG org.springframework.jdbc.support.lob.DefaultLobHandler IP134.167.141.34 CV#f2a597b2-a185-4e89 P#71252 - Set bytes for BLOB with length 7136
2011-08-31 17:35:16,441 [http-8080-1] DEBUG org.springframework.jdbc.support.lob.DefaultLobHandler IP134.167.141.34 CV#f2a597b2-a185-4e89 P#71252 - Set bytes for BLOB with length 10946
Aug 31, 2011 5:35:50 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet online threw exception java.sql.SQLIntegrityConstraintViolationException: ORA-01400: cannot insert NULL into ("SCHEMA"."TABLE_1"."COL_A")
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:440)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:396)
at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:837)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:445)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:191)
at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:523)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:207)
at oracle.jdbc.driver.T4CPreparedStatement.executeForRows(T4CPreparedStatement.java:1010)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1315)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3576)
at oracle.jdbc.driver.OraclePreparedStatement.executeUpdate(OraclePreparedStatement.java:3657)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeUpdate(OraclePreparedStatementWrapper.java:1350)
at org.apache.tomcat.dbcp.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
at org.apache.tomcat.dbcp.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
Note the important piece where we see the lengths of the clobs immediately after the Hibernate insert statement from the generated SQL.
Edit: After looking at this early this morning, I realized that the issue was due to one of the Blobs had to be mapped with #JoinColumn(insertable = false, updatable = false), otherwise Hibernate would not start. As such of course it was attempting to insert Null into this column. So the new question becomes, can you lazily MULTIPLE clobs on a single table (using the same key). I'm guessing without a table redesign, I'm pretty much out of luck unless Oracle fixes the driver.
As much as it makes me want to vomit we needed to get this functionality without modifying the Database.
As such, I pulled out the common pieces into an Abstract class like such:
#MappedSuperclass #Table("TABLE_1")
public class BadDBDesign {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column("table_id")
private long key;
#Column("small_value")
private String smallVarChar2Field;
}
The problem is I then have to extend this class for each of our blobs :( Thus our extended classes loook like:
public class BlobA extends BadDBDesign {
#Lob #Column("col_a")
#Type(type ="org.springframework.orm.hibernate3.support.BlobByteArrayType")
private byte[] blobColA;
}
public class BlobB extends BadDBDesign {
#Lob #Column("col_b")
#Type(type ="org.springframework.orm.hibernate3.support.BlobByteArrayType")
private byte[] blobColB;
}
Luckily we don't have any location where we more than one clob on any given page. This is still a maintenance nightmare, but was an acceptable trade-off (for the time-being) on getting the loads done more efficiently. I created DAO's for these, which the project didn't have prior; hopefully this will push the team in a good direction towards a proper abstraction layer, and we can hopefully completely remove these wasted POJOs in a future release.
Looks like in your BlobWrapperA class you have "nullable = false" set on that column. Or the column has a null constraint on the table itself in the database.
Oracle and Hibernate hate each other when it comes to LOB types, which stems from the fact that the Oracle driver is garbage. I believe I've run across this before, you should try setting the following system properties:
hibernate.jdbc.use_streams_for_binary=true
hibernate.jdbc.batch_size=0