I use hibernate sequences to generate id of an entity. I use PostgreSQL 9.1.
Is it possible to get entity id before it is saved to database? How?
You explicitely create a separate sequence, get its value, then insert an object with id based on that value. You will have more code, but the ID will be available before the insertion and the guarantees for sequences are exactly the same as for serially given IDs, because they are essentially the same.
In other words:
create your own sequence
make a primary key a simple int not serial
get a number from sequence
use it as an ID for your object
This question has an answer saying how to get next sequence value.
save() method returns the id of the entity that is saved. You can use it!
reference:-> http://docs.jboss.org/hibernate/annotations/3.5/api/org/hibernate/Session.html
You can implement the interface org.hibernate.id.IdentifierGenerator and create a Id generator.
Example:
import com.fasterxml.uuid.Generators;
import com.fasterxml.uuid.impl.TimeBasedGenerator;
public class TimeBasedIDGenerator implements IdentifierGenerator {
private static TimeBasedGenerator generator = Generators.timeBasedGenerator();
private static TimeBasedIDGenerator SINGLETON = new TimeBasedIDGenerator();
public static UUID generate() {
return SINGLETON.generateUUID();
}
#Override
public Serializable generate(SessionImplementor session, Object parent) throws HibernateException {
return generator.generate();;
}
}
This can be used in your Entities like this. So the id is generated by the constructor:
#Entity
public EntityClassName {
private UUID uuid;
private Integer mandatoryField;
public EntityClassName() {
}
public EntityClassName(Integer mandatoryField) {
this.uuid = TimeBasedIDGenerator.generate();
this.mandatoryField = mandatoryField;
}
#Id
#Column(name = COLUMN_XXX_UUID)
#Type(type = "java.util.UUID")
public UUID getUuid() {
return uuid;
}
// setter + other properties
}
Related
I have a custom ID generator that generates an UUID string with a prefix for my entities ID, but since I'm using different prefix for each entity I'm having to create one ID generation class for each Entity, is there a way to only use one class for this?
My ID generation class is this:
import java.io.Serializable;
import java.util.UUID;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;
public class ProductIdGenerator implements IdentifierGenerator{
public static final String generatorName = "produtcIdGenerator";
#Override
public Serializable generate(SharedSessionContractImplementor arg0, Object arg1) throws
HibernateException {
String prefix = "PROD";
String uuid = UUID.randomUUID().toString().substring(0, 8);
return prefix + uuid;
}
}
My Entities looks like this:
#Entity
public class Product {
#Id
#GeneratedValue(generator = ProductIdGenerator.generatorName)
#GenericGenerator(name = ProductIdGenerator.generatorName, strategy = "net.ddns.mrq.util.ProductIdGenerator")
#Column(name = "product_id")
private String id;
private String name;
.
.
.
I have 8 entities and I had to create 8 classes like this for each of one them with different prefix.
Is there a way to make this more dynamic and less "time consuming"?
Is there a way to only change the prefix for each class without creating multiple id generation classes?
I can think of a couple of ways to solve this (which is basically the need for a custom IdentifierGenerator to be parameterized).
One idea involves each entity implementing an interface that can return the appropriate ID prefix for that entity type. Since the target entity is passed to the generator's generate() method, the generator could cast it to that interface and ask it for the prefix to use.
Another solution takes advantage of the fact that IdentifierGenerators can implement the org.hibernate.id.Configurable interface to have configuration "injected" into them, and the #GenericGenerator annotation supports setting those as #Parameters in the annotation. That way, each usage of #GenericGenerator can dictate what prefix it wants the custom generator to use. It would look something like this (note, this is untested code):
public class ProductIdGenerator implements IdentifierGenerator, org.hibernate.id.Configurable {
public static final String GENERATOR_NAME = "produtcIdGenerator";
public static final String PREFIX_PARAM = "prefix";
private String prefix = "";
#Override
public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException {
this.prefix = params.getProperty(PREFIX_PARAM, "");
}
#Override
public Serializable generate(SharedSessionContractImplementor session, Object entityObject) throws HibernateException {
String uuid = UUID.randomUUID().toString().substring(0, 8);
return prefix + uuid;
}
}
References to it would look like this:
#Id
#GeneratedValue(generator = ProductIdGenerator.GENERATOR_NAME)
#GenericGenerator(
name = ProductIdGenerator.GENERATOR_NAME,
strategy = "net.ddns.mrq.util.ProductIdGenerator",
parameters = {#Parameter(name = ProductIdGenerator.PREFIX_PARAM, value = "foo")})
private String id;
Personally, I find the second idea a little cleaner, but there's nothing wrong with the first that I can see. It's a matter of style.
I'm trying to use querydsl to build a query which joins two tables. However, a slight discrepancy between the corresponding Java class data types seems to prevent me from directly comparing the two columns values in my query. The data type within the object corresponding to table A is java.util.UUID while the data type of the object corresponding to table B is String.
I have something like the following:
#Table(name = "TABLE_A")
public class TableA {
#Column(name = "uuid")
#Type(type = "uuid-char")
private UUID uuid;
}
#Table(name = "TABLE_B")
public class TableB {
#Column(name = "uuid")
private String uuid;
}
#Service
public class QueryService {
private final JPQLQueryFactory queryFactory;
public UUID getData(UUID input) {
return queryFactory.select(QTableA.tableA.uuid)
.from(QTableA.tableA)
.innerJoin(QTableB.tableB)
.on(QTableB.tableB.uuid.eq(QTableA.tableA.uuid.toString()))
.where(QTableA.tableA.uuid.eq(input))
.fetchOne();
}
}
The above code does not return anything. However, the below code seems to work:
#Service
public class QueryService {
private final JPQLQueryFactory queryFactory;
public UUID getData(UUID input) {
return queryFactory.select(QTableA.tableA.uuid)
.from(QTableA.tableA)
.innerJoin(QTableB.tableB)
.on(QTableA.tableA.uuid.eq(input)
.and(QTableB.tableB.uuid.eq(input.toString()))
.where(QTableA.tableA.uuid.eq(input))
.fetchOne();
}
}
I don't understand why directly comparing the columns doesn't work, but comparing them to a common variable does work. Would QTableA.tableA.uuid.toString() not call the proper toString() method?
Please try:
.on(QTableB.tableB.uuid.toString().eq(QTableA.tableA.uuid.toString()))
Apply toString on QTableB.tableB.uuid
It's looking like a direct comparison is not possible with the current version. Querydsl's type checking is too strict for a direct comparison to be done on these kinds of differing data types.
I have an TimelineEntity entity, that uses HoTimelineType enum with custom integer value. That custom integer value is stored in the database. Implemented via Using #PostLoad and #PrePersist Annotations
Sprint JPA Repository is used to save and get entities.
Here is the issue:
#Entity
#Table(name = TABLE_NAME)
#IdClass(TimelineKey.class)
public class TimelineEntity {
public interface Persistence {
String TABLE_NAME = "timelines";
}
#Id
#Column(name = "node_id")
private Long nodeId;
#Id
#Column(name = "timeline_id")
private Long timelineId;
#Column(name = "ho_timeline_type")
private Integer hoTimelineTypeValue;
#Transient
private HoTimelineType hoTimelineType;
public Long getNodeId() {
return nodeId;
}
public void setNodeId(Long nodeId) {
this.nodeId = nodeId;
}
public Long getTimelineId() {
return timelineId;
}
public void setTimelineId(Long timelineId) {
this.timelineId = timelineId;
}
public HoTimelineType getHoTimelineType() {
return hoTimelineType;
}
public void setHoTimelineType(HoTimelineType hoTimelineType) {
this.hoTimelineType = hoTimelineType;
}
public Integer getHoTimelineTypeValue() {
return hoTimelineTypeValue;
}
public void setHoTimelineTypeValue(Integer hoTimelineTypeValue) {
this.hoTimelineTypeValue = hoTimelineTypeValue;
}
#PostLoad
private void postLoad() {
this.hoTimelineType = HoTimelineType.of(hoTimelineTypeValue);
}
#PrePersist
private void prePersist() {
this.hoTimelineTypeValue = hoTimelineType.getValue();
}
}
#Eager
public interface TimelineEntityRepository extends JpaRepository<TimelineEntity, TimelineKey> {
List<TimelineEntity> findByNodeId(Long nodeId);
}
#Autowired
private TimelineEntityRepository timelineEntityRepository;
...
TimelineEntity newTE = new TimelineEntity();
newTE.setNodeId(10L);
newTE.setTimelineId(22L);
newTE.setHoTimelineType(HoTimelineType.TYPE_1);
newTE = timelineEntityRepository.save(newTE);
When the newTE entity is saved, prePersist is invoked, and inside this method, the hoTimelineType is null and I get NPE. nodeId and timelineId are not nulls. If I stay with a debugger on the last line, outside of prePersist, I see that hoTimelineType has the value, I set before.
When I load entities, inserted with test data, everything works fine and both hoTimelineType and hoTimelineTypeValue have not nullable values.
I skipped the code of TimelineKey and HoTimelineType to simplify the example. Can add it, if needed.
What could reset hoTimelineType? What do I miss?
It seems there is no way to control the saving behaviour of spring jpa repository proxy.
Possible solutions for issue:
Via javax.persistence.Converter. It is pretty clear, the structe of an entity is simple. Can confirm it works fine with Spring Jpa Repository generation.
Explicitely set hoTimelineTypeValue before you save an entity. Error-prone solution. Everytime you save an entity you must think about the difference between the hoTimelineTypeValue and hoTimelineType.
You could enrich setters and getters of the entity class, to explicitely control the consistency between the fields. It makes implementation of entity classes not so obvious. You get more compicated solution for nothing. As a result error-prone solution. Do not recommend it as well.
Cause of disadvantages of #2 and #3 I do not provide examples. It makes no sense.
Example of the solution #1 can be found here: Using JPA 2.1 #Converter Annotation
I have two fields of an entity class which I don't want to be unique but to instead be used as composite fields for a key which must itself be unique. For example I have two fields (name and version) which can be the same for other records but together they must be unique. What is the best way to do that using Hibernate (with annotations)? I am using Hibernate Validator for other fields but I am not sure of a way to use that to validate that two fields together compose a unique key. I am using a generic entity class which has an id generic type which can be swapped out for a composite key class but I have yet to get that to work very well.
This will create a unique key on the database:
#Table( name = "MYTABLE",
uniqueConstraints = { #UniqueConstraint( columnNames = { "NAME", "VERSION" } ) } )
This will be enforced by the database on a update or persist.
You'd need to write your own custom validator if you wanted to enforce this using Hibernate Validator.
We usually will wrap the two fields in an inner key class which is marked as #Embeddable. For example:
#Entity
public class Foo {
#EmbeddedId()
private Key key;
...
#Embeddable
public static class Key {
#Column(nullable=false)
private String name;
#Column(nullable=false)
private int version;
protected Key () {
// for hibernate
}
public Key (String name, int version) {
this.name = name;
this.version = version;
}
...
// You probably want .equals and .hashcode methods
}
}
I'm using java persistence to save a list of entities that are associated to another entity. Here's a quick rundown of where I'm having some problems.
#Entity public class Offer implements Serializable {
#Id private Long offerId;
#OneToMany
#Column List<OfferCategory> offerCategories;
}
#Entity public class OfferCategory implements Serializable {
#Embeddable public static class Id implements Serializable
{
#Column(name="offer_id")
private Long offerId;
#Column(name="category_id")
private Long categoryId;
public Id() {}
public Id(Long offerId, Long categoryId) {
this.offerId = offerId;
this.categoryId = categoryId;
}
public boolean equals(Object o) {
if(o != null && o instanceof Id) {
Id other = (Id) o;
return this.offerId.equals(other.offerId) &&
this.categoryId.equals(other.categoryId);
}
else
return false;
}
public int hashCode() {
return offerId.hashCode() + categoryId.hashCode();
}
}
#EmbeddedId private Id id = new Id();
}
Essentially, due to an architecture I cannot change, I need to save a list of Categories as being assigned to an Offer.
Right now, I'm getting a list of Categories from the user and then putting them into the offerCategories field of Offer. However, this doesn't work for new Offers, because there's no way for me to set the ID of a new item.
I'm new to JPA and Seam, so if someone could give me a nudge in the right direction it would be greatly appreciated.
I have not tried using a composite ID before, but one thing to note is that #Column is only used to change the properties of the database column the field is using. It doesn't stipulate a relation, so you still need something like this:
#OneToMany
List<OfferCategory> offerCategories;
As I looked into tutorial I found this:
You cannot use an IdentifierGenerator to generate composite keys. Instead the application must assign its own identifiers.
So you have to assign the id by yourself. Maybe you can make a DB sequence and fetch its values with native query?
And one remark - if you want to use List mapping (order of Categories in Offer is defined by database), you need an index column to contain the index in list. If the order of categories is not important, Set would be more convenient.
Well, my solution for now is that I persist each one (creating keys for new entries), then stuff them into a list, then stuff them into their container object, then persist that object.