I had the same problem and I have decided the following ...// Worked and Tested on WindFly AS 38 with Jakarta EE 10
// When entity persist/merge/remove callbacks before - save @Transient fields saved on Singleton class (use current module's general Helper class)
// When entity persist/merge/remove callbacks during/after - use @Transient fields from Singleton class (use current module's general Helper class)
// When entity persist/merge/remove operation finished - restore @Transient fields to entity from Singleton class (use current module's general Helper class)
// Entity class@Entity
@EntityListeners(UNIFooEntityListener.class)
@Table(name = "foo_entity")
public class UNIFooEntity extends BaseEntity {
@Column(length = 128, nullable = false)
private String name;
@Column(length = 32)
private String code;
@Transient
private String optionStr;
@Transient
private Object optionObj;
public UNIFooEntity() {
super();
}
/** utils **/
public void restoreTransients() {
this.setOptionStr(UNI.getEntityState().getOptionStr(this));
this.setOptionObj(UNI.getEntityState().getOptionObj(this));
}
}
// Entity CallBacks like Listenerpublic class UNIFooEntityListener {
@PrePersist
public void prePersist(FooEntity entity) {
entity.restoreTransients();
UNI.getLog().debug("--- Pre Persist ---");
UNI.getLog().debug(entity.getUniqID() + " optionStr : " + entity.getOptionStr());
UNI.getLog().debug(entity.getUniqID() + " optionStr : " + entity.getOptionStr());
}
@PostPersist
public void posPersist(FooEntity entity) {
UNI.getLog().debug("--- Post Persist ---");
UNI.getLog().debug(entity.getUniqID() + " optionStr : " + entity.getOptionStr());
UNI.getLog().debug(entity.getUniqID() + " optionStr : " + entity.getOptionStr());
}
@PreUpdate
public void preUpdate(FooEntity entity) {
entity.restoreTransients();
UNI.getLog().debug("--- Pre Update ---");
UNI.getLog().debug(entity.getUniqID() + " optionStr : " + entity.getOptionStr());
UNI.getLog().debug(entity.getUniqID() + " optionStr : " + entity.getOptionStr());
}
@PostUpdate
public void posUpdate(FooEntity entity) {
UNI.getLog().debug("--- Post Update ---");
UNI.getLog().debug(entity.getUniqID() + " optionStr : " + entity.getOptionStr());
UNI.getLog().debug(entity.getUniqID() + " optionStr : " + entity.getOptionStr());
}
@PreRemove
public void preRemove(FooEntity entity) {
entity.restoreTransients();
UNI.getLog().debug("--- Pre Remove ---");
UNI.getLog().debug(entity.getUniqID() + " optionStr : " + entity.getOptionStr());
UNI.getLog().debug(entity.getUniqID() + " optionStr : " + entity.getOptionStr());
}
@PostRemove
public void posRemove(FooEntity entity) {
UNI.getLog().debug("--- Post Remove ---");
UNI.getLog().debug(entity.getUniqID() + " optionStr : " + entity.getOptionStr());
UNI.getLog().debug(entity.getUniqID() + " optionStr : " + entity.getOptionStr());
}
}
// Entity Service Implementation class like EntityRepository@TransactionManagement(TransactionManagementType.BEAN)
@Stateless(name = EntityService.JNDI)
public class UNIEntityServiceImpl extends BaseEntityService {
@PersistenceUnit(unitName = "
PERSISTENCE.UNIT.NAME")
private EntityManagerFactory EMF;
@Resource
private UserTransaction userTXN;
public FooEntity saveFoo(FooEntity entity) throws EntityException, EntityDuplicationException {
EntityManager em = this.EMF.createEntityManager();
try {
super.doEntityStateBegin(entity, this.userTXN, em);
if (entity.saveTypeIsPersis()) {
em.persist(entity);
} else if (entity.saveTypeIsMerge()) {
entity = em.merge(entity);
} else if (entity.saveTypeIsDelete()) {
em.remove(em.getReference(FooEntity.class, entity.getId()));
} else if (entity.saveTypeIsReference()) {
em.refresh(entity);
}
super.doEntityState(entity, this.userTXN);
} catch (Exception ex) {
super.doEntityStateError(entity, this.userTXN, ex);
} finally {
super.doEntityStateDone(entity, em);
}
return entity;
}
}
// Base Entity Mapped super class@MappedSuperclass
public class BaseEntity {
@Transient
private String saveType;
public boolean saveTypeIsPersis() {
return saveType.equals("PERSIS");
}
public boolean saveTypeIsMerge() {
return saveType.equals("MERGE");
}
public boolean saveTypeIsDelete() {
return saveType.equals("DELETE");
}
public boolean saveTypeIsReference() {
return saveType.equals("REFERENCE");
}
}
// Base Entity Servicepublic abstract class BaseEntityService {
public void doEntityStateBegin(BaseEntity entity, UserTransaction userTXN, EntityManager em) throws NotSupportedException, SystemException {
UNI.getEntityState().doEntityBegin(entity);
userTXN.begin();
em.joinTransaction();
}
public void doEntityState(BaseEntity entity, UserTransaction userTXN) throws SecurityException, IllegalStateException, RollbackException, HeuristicMixedException, HeuristicRollbackException, SystemException {
UNI.getEntityState().doEntity(entity);
userTXN.commit();
}
public void doEntityStateDone(BaseEntity entity, EntityManager em) {
em.close();
UNI.getEntityState().doEntityDone(entity);
}
}
// Entity State Manager (User Class)@Startup
@Singleton(name = "EntityStateManager")
public class EntityStateManager {
private static ConcurrentHashMap<String, String> ENTITY_OPTION_STRS = new ConcurrentHashMap<String, String>();
private static ConcurrentHashMap<String, Object> ENTITY_OPTION_OBJS = new ConcurrentHashMap<String, Object>();
@PostConstruct
public void doInitialize() {
ENTITY_OPTION_STRS = new ConcurrentHashMap<String, String>();
ENTITY_OPTION_OBJS = new ConcurrentHashMap<String, Object>();
}
public void doEntityBegin(BaseEntity entity) {
if (entity != null) {
this.getEntityOptionStrs().put(entity.UniqID(), entity.getOptionStr());
this.getEntityOptionObjs().put(entity.UniqID(), entity.getOptionObj());
}
}
public void doEntity(BaseEntity entity) {
try {
entity.setOptionStr(this.getEntityOptionStrs().get(entity.getUniqID()));
} catch (Exception ex) {
UNISustem.getLogger().fatal("[entity.state][" + entity.getUniqID() + "]", ex);
}
}
public void doEntityDone(BaseEntity entity) {
if (entity != null) {
this.getEntityOptionStrs().remove(entity.getUniqID());
this.getEntityOptionObjs().remove(entity.getUniqID());
}
}
/** getter.Manage **/
public String getOptionStr() {
return this.getEntityOptionStrs().get(entity.getUniqID());
}
public Object getOptionObj() {
return this.getEntityOptionObjs().get(entity.getUniqID());
}
/** getter **/
public Map<String, String> getEntityOptionStrs() {
return ENTITY_OPTION_STRS;
}
public Map<String, String> getEntityOptionObjs() {
return ENTITY_OPTION_OBJS;
}
}
// Project Domain / Module's General Helperpublic class UNI {
@EJB(beanName = CMNSustemEntityService.JNDI)
private static UNIEntityService entityService;
@EJB(beanName = "EntityStateManager")
private static EntityStateManager entityState;
static {
// May be Initialize entityService or stateManager
}
/** getter **/
public static UNIEntityService getEntity() {
return entityService;
}
public static EntityStateManager getEntityState() {
return entityState;
}
public static Logger getLog() {
return Logger.getLogger("UNI");