Home > Blockchain >  How to remove the change check in hibernate
How to remove the change check in hibernate

Time:06-17

Hibernate checks the state of entities when committing transactions. This is useless and performance-critical when fetching large amounts of data to send to the client.

I found a solution with

 entityManager.setFlushMode(FlushModeType.COMMIT);

I put back auto after the query. Are there any normal more compact solutions.

Now I'm thinking of doing something similar to aspectJ so I don't clutter up the code and I can annotate both the service and the repository. What do you think about this and how do you solve such a problem?

Example:

@Override
public Collection<ZoomCallInfoDTO> findZoomCalls(Collection<ContactId> invitedUsers, String name, Date startTime, Date endTime, int offset, int limit, boolean asc, callId callId)
{
    // Why we did it?
    // Due to bad logic of finding calls in time interval (ZoomCallRepository#findAllByProfileAndcallWithinRange)
    // which loaded all calls for user to compute periodical calls there are TOO many entities in hibernate.
    // So during converting calls to DTOs we also have N x M queries (N - number of call to return, M - number of queries for each call conversion).
    // Each query makes hibernate checks all loaded entities for autoFlush policy. And we have bad performance ((
    // Possible decisions:
    // 1. Switch off autoFlush policy for GET methods:
    //    1.1 - Use readOnly transaction (not good for us now because stating top transaction logic is not managed)
    //    1.2 - Set flushMode manually (not good as persistence decision but the cheapest now) - CURRENTLY USED
    // 2. Decrease number of loaded to hibernate entities - it need to redesign logic of computing periodical calls
    // 3. Merge of 1 and 2 decisions - SHOULD BE IMPLEMENTED IN FUTURE
    entityManager.setFlushMode(FlushModeType.COMMIT);

    if (invitedUsers != null && !invitedUsers.isEmpty())
    {
        throw new RuntimeException("Search by invited users is not supported.");
    }

    UserProfile profile = callUtil.getCurrentUserProfile();

    List<ZoomCallInfoDTO> callDTOs = new ArrayList<>();
    Map<callId, callCacheItem> callCache = new HashMap<>();
    for (ZoomCall call : ZoomCallRepository.findAllByProfileAndcallWithinRange(profile.getId(), name, startTime, endTime, offset, limit, asc, callId))
    {
        callDTOs.add(create(call, profile.getId(), callCache));
    }

    entityManager.setFlushMode(FlushModeType.AUTO);

    return callDTOs;
}

I noticed that after such operations there is an "autoflush" there are too many of them

CodePudding user response:

One solution is implementing CustomEntityDirtinessStrategy

properties.setProperty("hibernate.entity_dirtiness_strategy", EntityDirtinessStrategy.class.getName());

Details in:

How to customize Hibernate dirty checking mechanism

More in:

Options for Entity Dirtness Checking

CodePudding user response:

The CustomEntityDirtinessStrategy is a recent Hibernate API addition, allowing us to provide an application-specific dirty checking mechanism. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class EntityDirtinessStrategy implements CustomEntityDirtinessStrategy {

@Override
public boolean canDirtyCheck(Object entity, EntityPersister persister, Session session) {
    return entity instanceof DirtyAware;
}

@Override
public boolean isDirty(Object entity, EntityPersister persister, Session session) {
    return !cast(entity).getDirtyProperties().isEmpty();
}

@Override
public void resetDirty(Object entity, EntityPersister persister, Session session) {
    cast(entity).clearDirtyProperties();
}

@Override
public void findDirty(Object entity, EntityPersister persister, Session session, DirtyCheckContext dirtyCheckContext) {
    final DirtyAware dirtyAware = cast(entity);
    dirtyCheckContext.doDirtyChecking(
        new AttributeChecker() {
            @Override
            public boolean isDirty(AttributeInformation attributeInformation) {
                String propertyName = attributeInformation.getName();
                boolean dirty = dirtyAware.getDirtyProperties().contains( propertyName );
                if (dirty) {
                    LOGGER.info("The {} property is dirty", propertyName);
                }
                return dirty;
            }
        }
    );
}

private DirtyAware cast(Object entity) {
    return DirtyAware.class.cast(entity);
}

}

  • Related