I have an entity class in my Spring Boot application where I've implemented a custom ID generator for the ID field. However, the ID isn't generated upon constructing the entity using the default constructor. I need the ID to be auto-generated when I create a new instance of the entity, like this: TransactionsEntity myEntity = new TransactionsEntity();
and then call myEntity.getId()
to retrieve the generated ID.
Here's my Entity Class:
@Entity
@Table(name = "transactions")
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class TransactionsEntity {
@Id
@NonNull
@Column(name = "transactionNumber", columnDefinition = "varchar(18)", length = 18)
@GeneratedValue(generator = SequenceGenerator.generatorName)
@GenericGenerator(name = SequenceGenerator.generatorName, strategy = "com.vendify.Dependencies.Tools.Spring.Generators.SequenceGenerator")
private String transactionNumber;
}
And this is my Generator:
public class SequenceGenerator implements IdentifierGenerator {
public static final String generatorName = "6CharGenerator";
@Override
public Serializable generate(SharedSessionContractImplementor sharedSessionContractImplementor, Object o) throws HibernateException {
return RandomGenerator.GenerateRandomStringUpperCaseOnlyNoSimilarChars(6);
}
}
Despite this setup, the constructor doesn't generate an ID as intended. I would greatly appreciate any guidance or help in resolving this issue!
When you create the entity via constructor, Jpa does not know anything about it. It gets to know your new entity when you call p
EntityManager.persist()
.
This is the moment when your entity becomes "managed" and gets a key assigned. The key generation strategy determines if the the record in the database is created immediately or at some time later (somewhere before transaction commit, see Flush Strategies).
If you use Strategy @GeneratedValue(strategy = GenerationType.IDENTITY)
the generated primary key of the database is used and thus an immediate INSERT
is necessary. If you use sequence or a custom strategy, no database action is required for key generation and therefore insert can be deferred to a later point, which can have big performance plus.
Bottomline: Just calling the constructor of an entity triggers nothing with JPA. If you want the transactionNumber
assigned before the call to EntityManager.persist()
you have to do it in your constructor.
However, since it is marked as @Id this might trigger unexpexted results with the EntityManager thinking the entity is already persisted (as it has a persistent Id), but I am not so sure about that. You have to try...
Response to Comment
I think you have three choices:
@GeneratedValue
, then you have to persist the entity before accessing it. Benefit of @GeneratedValue
is, that the EntityManager
ensures it is always created. Even when the entity is created without explicit persist (e.g. @Cascade
)@Id
without @GeneratedValue
. In this case, your business logic must ensure creation (as with any other value).transactionNumber
is some kind of domain key. Using domain keys as primary key might not be the best idea (but that is another topic). So you could use a conventional @Id
with a sequence generator and define the transactionNumber
as a regular entity property with a unique constraint with @Column(unique=true)