I have a table which has composite primary key consisting one sequence and two foreign keys I am able to persist My entity class but it is not generating according to the se
Just wanna to add my 2c. This works with composite and single primary keys. Prevent for creating sequences, instead, select max + 1 from the table.
Identifiable.java
package my.app.hibernate;
import java.io.Serializable;
public interface Identifiable {
T getId();
}
CompositeKeyEntity.java
package my.app.hibernate;
import java.io.Serializable;
public interface CompositeKeyEntity extends Identifiable {
}
SingleKeyEntity.java
package my.app.hibernate;
import java.io.Serializable;
public interface SingleKeyEntity extends Identifiable {
}
AssignedIdentityGenerator.java
package my.app.hibernate;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import org.hibernate.Criteria;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.id.IdentityGenerator;
import org.hibernate.internal.CriteriaImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.util.FieldUtils;
public class AssignedIdentityGenerator extends IdentityGenerator {
private static final String ID_FIELD_NAME = "id";
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
private Field sequenceField;
private String entityClassName;
@Override
public Serializable generate(SessionImplementor session, Object obj) {
@SuppressWarnings("unchecked")
Identifiable identifiable = (Identifiable)obj;
entityClassName = obj.getClass().getName();
Criteria criteria = new CriteriaImpl(entityClassName, session);
criteria.setReadOnly(true);
Object toSet = null;
if (identifiable instanceof CompositeKeyEntity) {
Serializable id = identifiable.getId();
if (id != null) {
String embaddebleClassName = id.getClass().getName();
buildCriteriaForEmbeddedId(id, embaddebleClassName, criteria);
toSet = id;
}
} else if (obj instanceof SingleKeyEntity) {
toSet = identifiable;
sequenceField = FieldUtils.getField(identifiable.getClass(), ID_FIELD_NAME);
buildCriteriaForSingleId(criteria);
}
Number one = castToSequenceNumberType(1L);
Number value = (Number) criteria.uniqueResult();
if(value != null) {
value = castToSequenceNumberType(value.longValue() + one.longValue());
setFieldValue(sequenceField, value, toSet);
} else {
value = one;
setFieldValue(sequenceField, value, toSet);
}
return identifiable.getId();
}
private void buildCriteriaForSingleId(Criteria criteria) {
criteria.setProjection(Projections.max(ID_FIELD_NAME).as("seq"));
}
private void buildCriteriaForEmbeddedId(Serializable id, String embaddebleClassName, Criteria criteria) {
List fields = Arrays.asList(id.getClass().getDeclaredFields());
class Utils {
Field field;
boolean numberFound = false;
}
final Utils utils = new Utils();
for (Field field : fields) {
if ("serialVersionUID".equals(field.getName()) || "$jacocoData".equals(field.getName())) {
continue;
}
if (Number.class.isAssignableFrom(field.getType())) {
if (utils.numberFound) {
throw new IllegalArgumentException(
embaddebleClassName + " has more then one sequence field: " + field.getName() + ", "
+ utils.field.getName() + ",...");
}
utils.numberFound = true;
utils.field = field;
sequenceField = field;
criteria.setProjection(Projections.max(ID_FIELD_NAME + "." + sequenceField.getName()).as("seq"));
} else {
criteria.add(Restrictions.eq(ID_FIELD_NAME + "." + field.getName(), getFieldValue(field, id)));
}
}
}
private Number castToSequenceNumberType(Number n) {
return (Number) sequenceField.getType().cast(n);
}
private void setFieldValue(Field field, Object value, Object to) {
try {
field.setAccessible(true);
field.set(to, value);
} catch (IllegalArgumentException | IllegalAccessException e) {
LOG.error(e.getMessage(), e);
}
}
private Object getFieldValue(Field field, Object from) {
try {
field.setAccessible(true);
return field.get(from);
} catch (IllegalArgumentException | IllegalAccessException e) {
LOG.error(e.getMessage(), e);
}
return null;
}
}
Customer.java
package my.app.entities;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.annotations.GenericGenerator;
import my.app.hibernate.SingleKeyEntity;
@Entity(name = "whatever_entity_name")
@GenericGenerator(name = "WHATEVER_NAMED_GENERATOR", strategy = "my.app.hibernate.AssignedIdentityGenerator")
public class Customer implements SingleKeyEntity {
@Id
@GeneratedValue(generator = "WHATEVER_NAMED_GENERATOR")
private Long id;
@Column(nullable = false)
private String name;
}
CustomerItemsId.java (Item.java ommited as it follows SingleKeyEntity example)
package my.app.entities;
import javax.persistence.Embeddable;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
@Embeddable
public class CustomerItemsId implements Serializable {
private static final long serialVersionUID = 1L; //generate one
@ManyToOne
@JoinColumn(name = "customer_id")
private Customer customer;
@ManyToOne
@JoinColumn(name = "item_id")
private Item item;
private Long seq; //name as you wish
}
CustomerItems.java
package my.app.entities;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.annotations.GenericGenerator;
import my.app.hibernate.CompositeKeyEntity;
@Entity(name = "whatever_entity_name")
@GenericGenerator(name = "WHATEVER_NAMED_GENERATOR", strategy = "my.app.hibernate.AssignedIdentityGenerator")
public class CustomerItems implements CompositeKeyEntity {
@GeneratedValue(generator = "WHATEVER_NAMED_GENERATOR")
private CustomerItems id;
@Column(nullable = false)
private String randomColumn1;
@Column(nullable = false)
private String randomColumn2;
}