Reputation: 343
I have this class with UUID as primary key:
@Entity
@Table(name = "JOURNAL")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class JournalEntity extends AbstractEntity<UUID> {
@Id
@GeneratedValue(generator = "UUID")
@GenericGenerator(name = "UUID",
strategy = "it.frogo.journal.dao.model.UUIDGenerator")
@Column(name = "id", updatable = false, nullable = false)
UUID id;
String name;
@Override
public UUID getId() {
return id;
}
}
I want to be able to persist a new entity with his own id if it's not null or generate a random one otherwise.
I've found this question:
How to generate ID only if it's null on persisting
so i created a custom IdentifierGenerator like this:
public class UUIDGenerator implements IdentifierGenerator {
@Override
public Serializable generate(SharedSessionContractImplementor session, Object arg) throws HibernateException {
log.info("UUID generation");
try {
final Method m = arg.getClass().getMethod("getId");
if (!m.getReturnType().equals(UUID.class)) {
throw new NoSuchMethodException();
}
final UUID invoke = (UUID)m.invoke(arg);
return invoke == null ? UUID.randomUUID() : invoke;
} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
throw new HibernateException("invalid entity");
}
}
}
What happens is that if i try to save an instance through the following repository
@Repository
public interface JournalRepository extends JpaRepository<JournalEntity, UUID>, JpaSpecificationExecutor<JournalEntity> {
}
is that the custom generator is called only if id is null on the entity.
When i set a pre-generated uuid on the entity the custom generator is not called and an exception is thrown:
org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: it.frogo.journal.dao.model.JournalEntity; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: it.frogo.journal.dao.model.JournalEntity
Using directly the entityManager i should probably be able to merge it without issues, but imho that defeats the purpose of having a repository in the first place.
So, am i missing something? There is really no other way to make this work with JpaRepository only?
Thank you.
Upvotes: 1
Views: 5932
Reputation: 21
I faced the same situation and I used this approach and it worked for me :
@Id
@Column(name = "id")
UUID id;
use @Prepersist annotation to get random UUID if null :
@PrePersist
protected void onCreate() {
if (Objects.isNull(this.id)) {
this.id = UUID.randomUUID();
}
}
Upvotes: 2
Reputation: 16400
Spring Data JPA has custom means to decide if it does an update or persist: https://stackoverflow.com/a/44494970/412446
Upvotes: 0
Reputation: 1442
Try this one.
@Entity
@Table(name = "JOURNAL")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class JournalEntity extends AbstractEntity<UUID> {
@Id
@GeneratedValue(generator = "UUID")
@GenericGenerator(name = "UUID",
strategy = "it.frogo.journal.dao.model.UUIDGenerator")
@Column(name = "id", updatable = false, nullable = false)
UUID id;
String name;
@Override
public UUID getId() {
return id;
}
@PrePersist
@PreUpdate
void setIdIfMissing() {
if (id == null) {
id = UUID.randomUUID();
}
}
}
If you need to apply the behaviour to several entities, then you can declare a separate entity listener.
@EntityListener
class UUIDEntityListener {
@PrePersist
@PreUpdate
void setIdIfMissing(Object entity) {
if (entity instanceof UUIDEntity) {
UUIDEntity e = (UUIDEntity) e;
if (e.getId() == null) {
e.setId(UUID.randomUUID())
}
}
}
}
Upvotes: 0