EntityLog throws NPE when creating new BaseIntIdentityIdEntity #1131

This commit is contained in:
Konstantin Krivopustov 2018-08-13 09:40:02 +04:00
parent 33f3b33a6a
commit 1343982c43
3 changed files with 157 additions and 20 deletions

View File

@ -38,6 +38,8 @@ import org.eclipse.persistence.internal.sessions.ObjectChangeSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
@ -112,7 +114,9 @@ public class EntityLog implements EntityLogAPI {
for (EntityLogItem item : items) {
List<EntityLogItem> sameEntityList = items.stream()
.filter(entityLogItem -> entityLogItem.getObjectEntityId().equals(item.getObjectEntityId()))
.filter(entityLogItem -> entityLogItem.getDbGeneratedIdEntity() != null ?
entityLogItem.getDbGeneratedIdEntity().equals(item.getDbGeneratedIdEntity()) :
entityLogItem.getObjectEntityId().equals(item.getObjectEntityId()))
.collect(Collectors.toList());
EntityLogItem itemToSave = sameEntityList.get(0);
computeChanges(itemToSave, sameEntityList);
@ -192,16 +196,31 @@ public class EntityLog implements EntityLogAPI {
protected void saveItem(EntityLogItem item) {
String storeName = metadataTools.getStoreName(metadata.getClassNN(item.getEntity()));
if (Stores.isMain(storeName)) {
EntityManager em = persistence.getEntityManager();
em.persist(item);
} else {
// Create a new transaction in main DB if we are saving an entity from additional data store
try (Transaction tx = persistence.createTransaction()) {
if (item.getDbGeneratedIdEntity() == null) {
if (Stores.isMain(storeName)) {
EntityManager em = persistence.getEntityManager();
em.persist(item);
tx.commit();
} else {
// Create a new transaction in main DB if we are saving an entity from additional data store
try (Transaction tx = persistence.createTransaction()) {
EntityManager em = persistence.getEntityManager();
em.persist(item);
tx.commit();
}
}
} else {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
Number id = item.getDbGeneratedIdEntity().getId().getNN();
item.setObjectEntityId(id);
try (Transaction tx = persistence.createTransaction()) {
EntityManager em = persistence.getEntityManager();
em.persist(item);
tx.commit();
}
}
});
}
}
@ -387,7 +406,11 @@ public class EntityLog implements EntityLogAPI {
item.setUser(findUser(em));
item.setType(EntityLogItem.Type.CREATE);
item.setEntity(entityName);
item.setObjectEntityId(referenceToEntitySupport.getReferenceId(entity));
if (entity instanceof BaseDbGeneratedIdEntity) {
item.setDbGeneratedIdEntity((BaseDbGeneratedIdEntity) entity);
} else {
item.setObjectEntityId(referenceToEntitySupport.getReferenceId(entity));
}
item.setAttributes(createLogAttributes(entity, attributes, null));
enqueueItem(item);

View File

@ -29,6 +29,8 @@ import com.haulmont.cuba.security.entity.Group
import com.haulmont.cuba.security.entity.LoggedAttribute
import com.haulmont.cuba.security.entity.LoggedEntity
import com.haulmont.cuba.security.entity.User
import com.haulmont.cuba.testmodel.primary_keys.IdentityEntity
import com.haulmont.cuba.testmodel.primary_keys.IntIdentityEntity
import com.haulmont.cuba.testsupport.TestContainer
import com.haulmont.cuba.testsupport.TestSupport
import org.junit.ClassRule
@ -79,6 +81,26 @@ class EntityLogTest extends Specification {
la.setEntity(le)
la.setName('type')
em.persist(la)
le = new LoggedEntity()
le.setName('test$IntIdentityEntity')
le.setAuto(true)
em.persist(le)
la = new LoggedAttribute()
la.setEntity(le)
la.setName('name')
em.persist(la)
le = new LoggedEntity()
le.setName('test$IdentityEntity')
le.setAuto(true)
em.persist(le)
la = new LoggedAttribute()
la.setEntity(le)
la.setName('name')
em.persist(la)
}
entityLog = AppBeans.get(EntityLogAPI.class)
entityLog.invalidateCache()
@ -102,16 +124,21 @@ class EntityLogTest extends Specification {
runner.update("delete from SEC_LOGGED_ENTITY")
}
private List<EntityLogItem> getEntityLogItems(def userId) {
private List<EntityLogItem> getEntityLogItems(String entityName, def entityId) {
Transaction tx
List<EntityLogItem> items
tx = cont.persistence().createTransaction()
try {
EntityManager em = cont.persistence().getEntityManager()
String entityIdField
if (entityId instanceof Integer) entityIdField = 'intEntityId'
else if (entityId instanceof Long) entityIdField = 'longEntityId'
else entityIdField = 'entityId'
TypedQuery<EntityLogItem> query = em.createQuery(
'select i from sec$EntityLog i where i.entity = ?1 and i.entityRef.entityId = ?2 order by i.eventTs desc', EntityLogItem.class)
query.setParameter(1, 'sec$User')
query.setParameter(2, userId)
"select i from sec\$EntityLog i where i.entity = ?1 and i.entityRef.$entityIdField = ?2 order by i.eventTs desc", EntityLogItem.class)
query.setParameter(1, entityName)
query.setParameter(2, entityId)
items = query.getResultList()
tx.commit()
@ -147,8 +174,8 @@ class EntityLogTest extends Specification {
then:
getEntityLogItems(user1Id).size() == 1
getEntityLogItems(user2Id).size() == 1
getEntityLogItems('sec$User', user1Id).size() == 1
getEntityLogItems('sec$User', user2Id).size() == 1
when:
@ -164,8 +191,8 @@ class EntityLogTest extends Specification {
then:
getEntityLogItems(user1Id).size() == 2
getEntityLogItems(user2Id).size() == 2
getEntityLogItems('sec$User', user1Id).size() == 2
getEntityLogItems('sec$User', user2Id).size() == 2
}
def "correct old value in case of flush in the middle"() {
@ -198,8 +225,8 @@ class EntityLogTest extends Specification {
then:
getEntityLogItems(user1Id).size() == 2
def item = getEntityLogItems(user1Id)[0] // latest
getEntityLogItems('sec$User', user1Id).size() == 2
def item = getEntityLogItems('sec$User', user1Id)[0] // latest
item.attributes.find({ it.name == 'email' }).value == 'email111'
item.attributes.find({ it.name == 'email' }).oldValue == 'email1'
@ -207,4 +234,80 @@ class EntityLogTest extends Specification {
item.attributes.find({ it.name == 'name' }).value == 'name11'
item.attributes.find({ it.name == 'name' }).oldValue == 'name1'
}
def "works for BaseIdentityIdEntity"() {
when:
IdentityEntity entity = cont.persistence().callInTransaction { em ->
def e = new IdentityEntity(name: 'test1')
em.persist(e)
e
}
then:
noExceptionThrown()
def item1 = getEntityLogItems('test$IdentityEntity', entity.id.get())[0]
item1.attributes.find({ it.name == 'name' }).value == 'test1'
item1.attributes.find({ it.name == 'name' }).oldValue == null
when:
cont.persistence().runInTransaction { em ->
def e = em.find(IdentityEntity, entity.id)
e.name = 'test2'
}
then:
def item2 = getEntityLogItems('test$IdentityEntity', entity.id.get())[0]
item2.attributes.find({ it.name == 'name' }).value == 'test2'
item2.attributes.find({ it.name == 'name' }).oldValue == 'test1'
cleanup:
if (entity != null && entity.getId().get() != null) {
new QueryRunner(cont.persistence().dataSource).update("delete from TEST_IDENTITY where id = ${entity.getId().get()}")
}
}
def "works for BaseIntIdentityIdEntity"() {
when:
IntIdentityEntity entity = cont.persistence().callInTransaction { em ->
def e = new IntIdentityEntity(name: 'test1')
em.persist(e)
e
}
then:
noExceptionThrown()
def item1 = getEntityLogItems('test$IntIdentityEntity', entity.id.get())[0]
item1.attributes.find({ it.name == 'name' }).value == 'test1'
item1.attributes.find({ it.name == 'name' }).oldValue == null
when:
cont.persistence().runInTransaction { em ->
def e = em.find(IntIdentityEntity, entity.id)
e.name = 'test2'
}
then:
def item2 = getEntityLogItems('test$IntIdentityEntity', entity.id.get())[0]
item2.attributes.find({ it.name == 'name' }).value == 'test2'
item2.attributes.find({ it.name == 'name' }).oldValue == 'test1'
cleanup:
if (entity != null && entity.getId().get() != null) {
new QueryRunner(cont.persistence().dataSource).update("delete from TEST_INT_IDENTITY where id = ${entity.getId().get()}")
}
}
}

View File

@ -18,6 +18,7 @@ package com.haulmont.cuba.security.entity;
import com.haulmont.chile.core.annotations.MetaProperty;
import com.haulmont.chile.core.datatypes.impl.EnumClass;
import com.haulmont.cuba.core.entity.BaseDbGeneratedIdEntity;
import com.haulmont.cuba.core.entity.BaseUuidEntity;
import com.haulmont.cuba.core.entity.Creatable;
import com.haulmont.cuba.core.entity.ReferenceToEntity;
@ -31,7 +32,6 @@ import javax.annotation.PostConstruct;
import javax.persistence.*;
import java.util.Date;
import java.util.Set;
import java.util.UUID;
/**
* Record containing information about entity lifecycle event.
@ -99,6 +99,9 @@ public class EntityLogItem extends BaseUuidEntity implements Creatable {
@EmbeddedParameters(nullAllowed = false)
private ReferenceToEntity entityRef;
@Transient
private transient BaseDbGeneratedIdEntity dbGeneratedIdEntity;
@Transient
@MetaProperty
private Set<EntityLogAttr> attributes;
@ -188,6 +191,14 @@ public class EntityLogItem extends BaseUuidEntity implements Creatable {
this.entityRef = entityRef;
}
public BaseDbGeneratedIdEntity getDbGeneratedIdEntity() {
return dbGeneratedIdEntity;
}
public void setDbGeneratedIdEntity(BaseDbGeneratedIdEntity dbGeneratedIdEntity) {
this.dbGeneratedIdEntity = dbGeneratedIdEntity;
}
public void setObjectEntityId(Object entity) {
if (entityRef == null) {
entityRef = AppBeans.get(Metadata.class).create(ReferenceToEntity.class);