I'm having problems persisting an entity with a #OneToOne relationship with another entity. We've recently upgraded to Java 8 with Spring 4 and Hibernate 4 so I'm sure there's some annotation or configuration that has changed.
Relevant code
PermitState.java:
#Entity
#Table(schema = "dbo", name = "PermitState")
public class PermitState implements Serializable {
private String oid;
private Permit permit;
private Integer invoiced;
private Integer used;
private Integer ordered;
private String permitId;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PERMITID")
public Permit getPermit() {
return permit;
}
#Id
#Column(name = "PERMITID", nullable = true, length = 8,insertable = false,updatable = false)
public String getPermitId() {
return permitId;
}
... more getters and setters (irrelevant)
Permit.java:
#Entity
#Table(schema = "dbo")
public class Permit {
private String permitid;
private PermitState permitState;
...some more attributes, irrelevant
#Id
#Column(name = "PERMITID", length = 8)
public String getPermitid() {
return permitid;
}
#OneToOne(mappedBy="permit", cascade=CascadeType.ALL,fetch = FetchType.LAZY)
public PermitState getPermitState() {
return permitState;
}
Stacktrace
Caused by: org.hibernate.exception.GenericJDBCException: could not insert: [dao.srs.model.PermitState]
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:54)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:124)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3099)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3521)
at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:88)
at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:395)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:387)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:303)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:349)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:56)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1195)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:404)
at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:175)
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:75)
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:515)
... 65 more
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: The index 6 is out of range.
at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDriverError(SQLServerException.java:171)
at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.setterGetParam(SQLServerPreparedStatement.java:700)
at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.setValue(SQLServerPreparedStatement.java:709)
at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.setString(SQLServerPreparedStatement.java:1034)
at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.setString(NewProxyPreparedStatement.java:963)
at org.hibernate.type.descriptor.sql.VarcharTypeDescriptor$1.doBind(VarcharTypeDescriptor.java:57)
at org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:93)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:284)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:279)
at org.hibernate.type.ComponentType.nullSafeSet(ComponentType.java:343)
at org.hibernate.persister.entity.AbstractEntityPersister.dehydrateId(AbstractEntityPersister.java:2835)
at org.hibernate.persister.entity.AbstractEntityPersister.dehydrate(AbstractEntityPersister.java:2804)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3076)
... 78 more
As you can see, the PermitState entity has both a relation to Permit (via PERMITID, which is the PK in Permit) AND a field called PermitId, which is supposed to the connected Permit's ID. This doesn't seem to work though, it seems that it's trying to insert all 6 attributes even though Permit shouldn't be inserted into the database. I tried to annotate Permit in PermitState with #Transient, but then I get a AnnotationException:
Caused by: org.hibernate.AnnotationException: Unknown mappedBy in: dao.srs.model.Permit.permitState, referenced property unknown: dao.srs.model.PermitState.permit
Any help would be greatly appreciated!
Update
Just to clarify, the DB only have 5 fields - OID, PERMITID, INVOICED, USED and ORDERED - which is why I'm thinking its trying to insert the Permit entity as well. The exact same code without modifications worked when we used Java 7, Spring 3, hibernate-annotations 3.3.1.GA and hibernate-entitymanager 3.3.2.GA. We now use Java 8, Spring 4 and Hibernate core+entitymanager+ehcache 4.2.19.
It seem to me, judging from the exception, that you tried #Transient while still having the #OneToOne and #JoinColumn. That does not really make sense, as you tell Hibernate you want to handle it yourself and that it should handle it also.
Also I find it a bit confusing you both have the Permit entity and the permitId in PermitState. You tell it that you don't want to update permitId, but you still use it as the join column?
I recommend removing the permitId and only have the Permit entity in PermitState. You can still have the getPermitId method if you like, but treat it as an agregate instead.
If all else fails try and log the SQL generated by Hibernate to see what is going on. Perhaps pasting it for us to see.
I think I solved it. I'm still not entirely sure why this works, but it does:
I removed PermitId (the String property) from PermitState.java, and instead used the PermitId from the Permit entitiy in PermitState.
It looks like this:
Permit.java:
private String permitid;
private PermitState permitState;
#OneToOne(mappedBy="permit", cascade=CascadeType.ALL,fetch = FetchType.LAZY)
public PermitState getPermitState() {
return permitState;
}
PermitState.java
private Permit permit;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PERMITID")
public Permit getPermit() {
return permit;
}
Thanks Martin and Bilbo for your help.
Related
When I run the spring boot project I get the following error
" Invocation of init method failed; nested exception is org.hibernate.AnnotationException: No identifier specified for entity:"
I have a few other classes with multiple primary keys and foreign keys but they didn't run to an error.
import javax.persistence.*;
#Entity
#Table(name="roles_has_features")
public class RoleFeatures {
#Column(name = "role_id_fk")
private Long roleIdFk;
#Column(name = "feature_id_fk")
private Long featureIdFk;
public Long getRoleIdFk() { return roleIdFk; }
public void setRoleIdFk(Long roleIdFk) { this.roleIdFk = roleIdFk; }
public Long getFeatureIdFk() { return featureIdFk; }
public void setFeatureIdFk(Long featureIdFk) { this.featureIdFk = featureIdFk; }
}
This actually has nothing to do with Spring. This is an error thrown by Hibernate, because JPA specification requires an Identity for each entity. As for your case, I would not suggest to create a separate entity, because as far as I understand from your column names, it's just a mapping for a relation between role and feature tables. I'd suggest to JPA Many-To-Many relationship. Take a look at #ManyToMany and #JoinTable annotations.
Also this looks as a really good tutorial for me
Hibernate – Many-to-Many example
The error message describes the issue pretty well:
No identifier specified for entity
You do not have an #Id annotated column in your RoleFeatures entity. Thus, hibernate is unable to identify an entity in the database and refuses to start.
Your so-called entity looks more like an Many-To-Many relationship. Maybe it's better to go this way.
Something like this:
#Entity
public class Role {
#Id
#Column(name = "role_id")
private Long id;
#ManyToMany
#JoinTable(name = "roles_has_features",
joinColumns = #JoinColumn(name = "feature_id_fk", referencedColumnName = "feature_id"),
inverseJoinColumns = #JoinColumn(name = "role_id_fk", referencedColumnName = "role_id"))
private List<Feature> features;
...
}
See also: https://www.baeldung.com/jpa-many-to-many
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
Posting this here as I wasn't seeing much interest here: http://www.java-forums.org/jpa/96175-openjpa-one-many-within-one-many-merge-problems.html
Trying to figure out if this is a problem with OpenJPA or something I may be doing wrong...
I'm facing a problem when trying to use OpenJPA to update an Entity that contains a One to Many relationship to another Entity, that has a One to Many relationship to another. Here's a quick example of what I'm talking about:
#Entity
#Table(name = "school")
public class School {
#Column(name = "id")
protected Long id;
#Column(name = "name")
protected String name;
#OneToMany(mappedBy = "school", orphanRemoval = true, cascade = CascadeType.ALL)
protected Collection<ClassRoom> classRooms;
}
#Entity
#Table(name = "classroom")
public class ClassRoom {
#Column(name = "id")
protected Long id;
#Column(name = "room_number")
protected String roomNumber;
#ManyToOne
#JoinColumn(name = "school_id")
protected School school;
#OneToMany(mappedBy = "classRoom", orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
protected Collection<Desk> desks;
}
#Entity
#Table(name = "desk")
public class Desk {
#Column(name = "id")
protected Long id;
#ManyToOne
#JoinColumn(name = "classroom_id")
protected ClassRoom classRoom;
}
In the SchoolService class, I have the following update method:
#Transactional
public void update(School school) {
em.merge(school);
}
I'm trying to remove a Class Room from the School. I remove it from the classRooms collection and call update. I'm noticing if the Class Room has no desks, there are no issues. But if the Class Room has desks, it throws a constraint error as it seems to try to delete the Class Room first, then the Desks. (There is a foreign key constraint for the classroom_id column)
Am I going about this the wrong way? Is there some setting I'm missing to get it to delete the interior "Desk" instances first before deleting the Class Room instance that was removed?
Any help would be appreciated. If you need any more info, please just let me know.
Thanks,
There are various bug reports around FK violations in OpenJPA when cascading remove operations to child entities:
The OpenJPA FAQ notes that the following:
http://openjpa.apache.org/faq.html#reorder
Can OpenJPA reorder SQL statements to satisfy database foreign key
constraints?
Yes. OpenJPA can reorder and/or batch the SQL statements using
different configurable strategies. The default strategy is capable of
reordering the SQL statements to satisfy foreign key constraints.
However ,you must tell OpenJPA to read the existing foreign key
information from the database schema:
It would seem you can force the correct ordering of the statements by either setting the following property in your OpenJPA config
<property name="openjpa.jdbc.SchemaFactory"> value="native(ForeignKeys=true)"/>
or by adding the org.apache.openjpa.persistence.jdbc.ForeignKey annotation to the mapping:
#OneToMany(mappedBy = "classRoom", orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#org.apache.openjpa.persistence.jdbc.ForeignKey
protected Collection<Desk> desks;
See also:
https://issues.apache.org/jira/browse/OPENJPA-1936
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.
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.