I am getting an error that Id column is not able to generate.
which will have the Getters and Setters.
Note: In my case, Primary Key was bookingId.
"Id" has to be a sequence number.
Below is the save method.
At the time of save is was not able to generate value for the column.
Below is My Entity.
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Id
#Column(name = "booking_id")
private String bookingId;
#Column(name = "booked_by")
private String bookedBy;
#Column(name = "booking_status")
private int bookingStatus;
#Column(name = "booking_rcv")
private Date bookingRcv;
#Column(name = "chargeable_kgs")
private BigDecimal chargeableKgs;
}
Here we will have all the getters and setters, Constructors, hashcode, and equals methods.
#Transactional
#Override
public Booking saveBooking(Booking booking) {
Booking savedBooking = bookingRepository.save(booking);
if (savedBooking.getId() != null) {
booking.setId(savedBooking.getId());
}
booking.setVersion(savedBooking.getVersion());
bookingRepository.flush();
// bookingRepository.refresh(booking);
LOGGER.debug("savedbooking id::" + savedBooking.getId());
return savedBooking;
}
I am using postgre DB and Hibernate.
My use case is: I have a Product with multi language name. To have at most one name per language a translation should be identified by locale + productId.
My problem is to get it working with a generated productId. This are the entities:
e.g. in oracle i get: "ORA-01400: cannot insert null into ."PRODUCT_NAME"."FOREIGN_ID""
Product:
#Entity
#Data
public class Product {
#Id
#GeneratedValue
private Long id;
#Column(unique = true)
private String articleNumber;
/**
* Localized name of the product.
*/
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#MapKey(name = "nameId.locale")
private Map<Locale, ProductName> names = new HashMap<>();
}
ProductName:
#Entity
#Data
public class ProductName {
#EmbeddedId
private TranslationId nameId;
private String name;
}
TranslationId:
#Embeddable
#Data
public class TranslationId implements Serializable {
private static final long serialVersionUID = 3709634245257481449L;
#Convert(converter = LocaleConverter.class)
private Locale locale;
private Long foreignId; //<-- this is null, should reference product
}
Is there a way to get this working without having to save the product first without the name? Without a generated id it is working of course - i just set same id for both.
I would like to re-use the translation id for other translated fields of other entities.
The below examples show what I tried to reference an entity by a unique combination of columns that is not its primary key.
I want to do it that way because the referencing table contains all the necessary constituents of the referenced "Composite Secondary Key" but it does not contain the Primary Key.
How can I achieve this? (If possible without being Hibernate specific)
Base example
Suppose an entity has a primary key as well as a composite secondary key:
#Entity
#Table(name = "EXAMPLE_DATA",
uniqueConstraints = {
#UniqueConstraint(columnNames = {
"COMPOSITE_KEY_PART_1",
"COMPOSITE_KEY_PART_2"})
})
public class ExampleData {
#Id
#Column
private Long exampleDataId;
#Column(name = "COMPOSITE_KEY_PART_1")
private String compositeKeyPart1;
#Column(name = "COMPOSITE_KEY_PART_2")
private String compositeKeyPart2;
}
I would like to reference it from another table by its composite secondary key:
#Entity
#Table(name = "EXAMPLE")
public class Example {
#Id
#Column
private Long exampleId;
#Column(name = "COMPOSITE_KEY_PART_1")
private String compositeKeyPart1; // of ExampleData
#Column(name = "COMPOSITE_KEY_PART_2")
private String compositeKeyPart2; // of ExampleData
#MayToOne
#JoinColumn(name = "COMPOSITE_KEY_PART_1", insertable = false, updatable = false)
#JoinColumn(name = "COMPOSITE_KEY_PART_2", insertable = false, updatable = false)
private ExampleData exampleData;
}
However, this leads to
org.hibernate.AnnotationException:
A Foreign key refering com.example.ExampleData from com.example.Example has the wrong number of column. should be 1
Making a separate #Embeddable composite key
I tried making the secondary key embeddable
#Embeddable
public class CompositeKey implements Serializable {
#Column(name = "COMPOSITE_KEY_PART_1")
private String compositeKeyPart1;
#Column(name = "COMPOSITE_KEY_PART_2")
private String compositeKeyPart2;
}
and using it as an embedded object:
#Entity
#Table(name = "EXAMPLE_DATA",
uniqueConstraints = {
#UniqueConstraint(columnNames = {
"COMPOSITE_KEY_PART_1",
"COMPOSITE_KEY_PART_2"})
})
public class ExampleData {
#Id
#Column
private Long exampleDataId;
#Embedded
private CompositeKey compositeKey;
}
but that leads to the same exception:
org.hibernate.AnnotationException:
A Foreign key refering com.example.ExampleData from com.example.Example has the wrong number of column. should be 1
Using #EmbeddedId
Using #EmbeddedId instead of just #Embedded leads to issues with multiple keys
org.hibernate.AnnotationException:
com.example.CompositeKey must not have #Id properties when used as an #EmbeddedId: com.example.ExampleData.compositeKey
Having only a single key works but is undesirable
The only way I can actually make it work is by removing the primary key and making the composite key the primary key (but I don't want that)
#Entity
#Table(name = "EXAMPLE_DATA")
public class ExampleData {
// #Id // removing this primary key is undesirable
#Column
private Long exampleDataId;
#EmbeddedId // This now becomes the primary key
private CompositeKey compositeKey;
}
My application uses Hibernate 5.02 and Wildfly 10 with a PostgreSQL 9.5 database. I'm trying to enable a filter on a #OneToMany collection held within an entity that is constructed via a NamedQuery. Unfortunately, it seems as if the filter is just ignored. Here are the different components, redacted for ease of reading.
#NamedNativeQueries({
#NamedNativeQuery(
name = "getAnalystProcess",
query = "SELECT * FROM analysis.analystprocess WHERE id = :processId",
resultClass = AnalystProcessEntity.class
)})
#FilterDef(
name = "analystProcessUnanalyzedMsgsFilter",
parameters = { #ParamDef(name = "processIds", type = "integer"), #ParamDef(name = "analystIds", type = "integer") })
#Filter(name = "analystProcessUnanalyzedMsgsFilter", condition = "analystprocess_id IN (:processIds) AND id NOT IN (SELECT msg_id FROM analysis.analyzedmsg WHERE analyst_id IN (:analystIds) AND analystprocess_id IN (:processIds)) ORDER BY process_msg_id")
#Entity
#Table(name = "analystprocess", schema = "analyst")
public class AnalystProcessEntity implements JPAEntity {
public static final String GET_PROCESS = "getAnalystProcess";
public static final String MSG_FILTER = "analystProcessUnanalyzedMsgsFilter";
public static final String MSG_FILTER_PROC_ID_PARAM = "processIds";
public static final String MSG_FILTER_ANALYST_ID_PARAM = "analystIds";
private static final long serialVersionUID = 1L;
...
#OneToMany(fetch = FetchType.LAZY, orphanRemoval = true, mappedBy = "process")
#OrderColumn(name = "process_msg_id")
#LazyCollection(LazyCollectionOption.EXTRA)
private List<MsgEntity> msgList;
#Entity
#Table(name = "msg", schema = "analyst")
public class MsgEntity implements JPAEntity {
...
#ManyToOne(cascade = CascadeType.ALL, optional = false)
#JoinColumn(name = "analystprocess_id", referencedColumnName = "id")
private AnalystProcessEntity process;
#Column(name = "process_msg_id")
private Integer processMsgId;
private void buildAnalystProcess() {
LOG.info("Building AnalystProcessEntity");
analystUser.getJdbcSession().enableFilter(AnalystProcessEntity.MSG_FILTER)
.setParameter(AnalystProcessEntity.MSG_FILTER_PROC_ID_PARAM, analystProcessId)
.setParameter(AnalystProcessEntity.MSG_FILTER_ANALYST_ID_PARAM, analystUser.getId());
Query query = analystUser.getJdbcSession().getNamedQuery(AnalystProcessEntity.GET_PROCESS)
.setParameter("processId", analystProcessId);
// Query query = analystUser.getJdbcSession().createNativeQuery("SELECT * FROM analysis.analystprocess WHERE id = :processId")
// .setParameter("processId", analystProcessId)
// .addEntity(AnalystProcessEntity.class);
analystProcess = (AnalystProcessEntity) query.getSingleResult();
CREATE TABLE analysis.analystprocess (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
description TEXT,
created_date TIMESTAMP NOT NULL DEFAULT now(),
...
);
CREATE TABLE analysis.msg (
id SERIAL PRIMARY KEY,
analystprocess_id INTEGER NOT NULL REFERENCES analysis.analystprocess(id) ON DELETE CASCADE ON UPDATE CASCADE,
process_msg_id INTEGER NOT NULL,
constraint tbl_statusid_analystprocessid unique(status_id, analystprocess_id)
);
As seen above, I have also tried the filter on constructing the AnalystProcessEntity class via createNativeQuery instead of getNamedQuery and no luck.
I also added a defaultCondition with hardcoded values into the #FilterDef just to see if it would execute the default condition and it still didn't.
I've tried the #Filter above the entity definition as well as above the class definition. I even came across a blog post which made it sound like the condition references entity fields (variable names) and not table fields (column names). Trying to stick to Java naming conventions in the Entity and Postgres naming conventions in the table, so I tried switching the references in the condition and to no avail.
I have sql logging turned on in Hibernate and the condition doesn't show up anywhere, as if it's just simply being ignored.
Any help would be greatly appreciated!
So, the problem was that I had the #FilterDef applied to the wrong class. It was my presumption that because I was constructing the AnalystProcessEntity which holds the MsgEntity collection (which I am trying to filter), that the #FilterDef would be applied to the AnalystProcessEntity class. Instead, it needs to be applied to the entity that it's actually filtering (hindsight being 20/20, that's pretty obvious).
Also, the actual condition needed to be modified to use complete references within the sub-select query.
I hope this helps someone at some point...
#NamedNativeQueries({
#NamedNativeQuery(
name = "getAnalystProcess",
query = "SELECT * FROM analysis.analystprocess WHERE id = :processId",
resultClass = AnalystProcessEntity.class
)})
#Filter(name = "analystProcessUnanalyzedMsgsFilter", condition = "id NOT IN (SELECT amsg.msg_id FROM analysis.analyzedmsg amsg WHERE amsg.analyst_id IN (:analystIds) AND amsg.analystprocess_id IN (:processIds))")
#Entity
#Table(name = "analystprocess", schema = "analyst")
public class AnalystProcessEntity implements JPAEntity {
public static final String GET_PROCESS = "getAnalystProcess";
public static final String MSG_FILTER = "analystProcessUnanalyzedMsgsFilter";
public static final String MSG_FILTER_PROC_ID_PARAM = "processIds";
public static final String MSG_FILTER_ANALYST_ID_PARAM = "analystIds";
private static final long serialVersionUID = 1L;
...
#OneToMany(fetch = FetchType.LAZY, orphanRemoval = true, mappedBy = "process")
#OrderColumn(name = "process_msg_id")
#LazyCollection(LazyCollectionOption.EXTRA)
private List<MsgEntity> msgList;
#FilterDef(
name = "analystProcessUnanalyzedMsgsFilter",
parameters = { #ParamDef(name = "processIds", type = "integer"), #ParamDef(name = "analystIds", type = "integer") })
#Entity
#Table(name = "msg", schema = "analyst")
public class MsgEntity implements JPAEntity {
...
#ManyToOne(cascade = CascadeType.ALL, optional = false)
#JoinColumn(name = "analystprocess_id", referencedColumnName = "id")
private AnalystProcessEntity process;
#Column(name = "process_msg_id")
private Integer processMsgId;
Additionally, I ran into another problem with null's appearing in the collection, despite the fact that I am using an #OrderColumn, which I thought fixed that issue. It seems that with the use of the #Filter, null's are inserted in place of what ended up being filtered OUT (excluded).
I would like some advice on how to best layout my JPA entity classes. Suppose I have 2 tables I would like to model as entities, user and role.
Create Table users(user_id primary key,
role_id integer not null )
Create table role(role_id primary key,
description text,
)
I create the following two JPA Entities:
#Entity
#Table(name="users")
#Access(AccessType.PROPERTY)
public class User implements Serializable {
private Long userId;
private Long roleId;
private Role role;
#Column(name = "user_id")
#Id
public Long getUserId() {}
#Column(name = "role_id")
public Long getRoleId() {}
#ManyToOne()
JoinColumn(name="role_id")
public Role getRole() {}
}
Role Entity:
#Entity
#Table(name="Role")
#Access(AccessType.PROPERTY)
public class Role implements Serializable {
private String description;
private Long roleId;
#Column(name = "role_id")
#Id
public Long getRoleId() {}
#Column(name = "description")
public Long getDescrition(){}
#ManyToOne()
#JoinColumn(name="role_id")
public Role getRole() {}
}
Would the correct way to model this relationship be as above, or would I drop the private Long roleId; in Users? Any advice welcomed.
When I map it this way, I receive the following error:
org.hibernate.MappingException: Repeated column in mapping for entity:
Yes, you would drop the private Long roleId mapping when you have a #ManyToOne on the same column.
As the error implies, you can only map each column in an #Entity once. Since role_id is the #JoinColumn for the #ManyToOne reference, you cannot also map it as a property.
You can, however, add a convenience method to return the role ID, like
public Long getRoleId() {
return role.getId();
}