All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.ebeaninternal.server.persist.MergeHandler Maven / Gradle / Ivy

There is a newer version: 15.8.0
Show newest version
package io.ebeaninternal.server.persist;

import io.ebean.CacheMode;
import io.ebean.MergeOptions;
import io.ebean.PersistenceContextScope;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssoc;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;

import jakarta.persistence.PersistenceException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

/**
 * Drives the merge processing.
 */
final class MergeHandler {

  private static final Pattern PATH_SPLIT = Pattern.compile("\\.");

  private final SpiEbeanServer server;
  private final BeanDescriptor desc;
  private final EntityBean bean;
  private final MergeOptions options;
  private final SpiTransaction transaction;
  private final Map nodes = new LinkedHashMap<>();

  MergeHandler(SpiEbeanServer server, BeanDescriptor desc, EntityBean bean, MergeOptions options, SpiTransaction transaction) {
    this.server = server;
    this.desc = desc;
    this.bean = bean;
    this.options = options;
    this.transaction = transaction;
  }

  /**
   * Fetch the Ids for the graph and use them to determine inserts, updates and deletes for the merge paths.
   */
  List merge() {
    Set paths = options.paths();
    if (desc.isIdGeneratedValue() && paths.isEmpty() && !options.isClientGeneratedIds()) {
      // just do a single insert or update based on Id value present
      Object id = desc.getId(bean);
      if (id != null) {
        bean._ebean_getIntercept().setForceUpdate(true);
      }
      return Collections.emptyList();
    }

    EntityBean outline = fetchOutline(paths);
    if (outline == null) {
      // considered an insert ...
      return Collections.emptyList();
    }

    // the top level bean is an update
    bean._ebean_getIntercept().setForceUpdate(true);

    // detect what beans are updates and recursively set forceUpdate as needed
    // and outline beans not in the merge graph are generally considered as deletes
    MergeContext context = new MergeContext(server, transaction, options.isClientGeneratedIds());
    MergeRequest request = new MergeRequest(context, bean, outline);
    for (MergeNode value : nodes.values()) {
      value.merge(request);
    }
    return context.getDeletedBeans();
  }

  /**
   * Fetch the outline bean with associated one and associated many beans loaded with Id values only.
   * 

* We use the Id values to determine what are inserts, updates and deletes as part of the merge. */ private EntityBean fetchOutline(Set paths) { SpiQuery query = server.createQuery(desc.type()); query.setBeanCacheMode(CacheMode.OFF); query.setPersistenceContextScope(PersistenceContextScope.QUERY); query.setId(desc.getId(bean)); query.select(desc.idProperty().name()); for (String path : paths) { MergeNode node = buildNode(path); node.addSelectId(query); } query.usingTransaction(transaction); return (EntityBean) server.findOne(query); } private MergeNode buildNode(String path) { String[] split = PATH_SPLIT.split(path); if (split.length == 1) { return addRootLevelNode(split[0]); } else { return addSubNode(path, split); } } private MergeNode addSubNode(String fullPath, String[] split) { MergeNode parent = nodes.get(split[0]); if (parent == null) { throw new PersistenceException("Unable to find parent path " + split[0] + " in merge paths?"); } for (int i = 1; i < split.length - 1; i++) { parent = parent.get(split[i]); if (parent == null) { throw new PersistenceException("Unable to find parent path " + split[0] + " in merge paths?"); } } return parent.addChild(fullPath, split[split.length - 1]); } private MergeNode addRootLevelNode(String rootPath) { MergeNode node = createMergeNode(rootPath, desc, rootPath); nodes.put(rootPath, node); return node; } static MergeNode createMergeNode(String fullPath, BeanDescriptor targetDesc, String path) { BeanProperty prop = targetDesc.beanProperty(path); if (!(prop instanceof BeanPropertyAssoc)) { throw new PersistenceException("merge path [" + path + "] is not a ToMany or ToOne property of " + targetDesc.fullName()); } if (prop instanceof BeanPropertyAssocMany) { BeanPropertyAssocMany assocMany = (BeanPropertyAssocMany) prop; if (assocMany.isManyToMany()) { return new MergeNodeAssocManyToMany(fullPath, assocMany); } else { return new MergeNodeAssocOneToMany(fullPath, assocMany); } } else { return new MergeNodeAssocOne(fullPath, (BeanPropertyAssocOne) prop); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy