/** Used for forced update of a bean. */ public PersistRequestBean( SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr, SpiTransaction t, PersistExecute persistExecute, Set<String> updateProps, ConcurrencyMode concurrencyMode) { super(server, t, persistExecute); this.beanManager = mgr; this.beanDescriptor = mgr.getBeanDescriptor(); this.beanPersistListener = beanDescriptor.getPersistListener(); this.bean = bean; this.parentBean = parentBean; this.controller = beanDescriptor.getPersistController(); this.concurrencyMode = beanDescriptor.getConcurrencyMode(); this.concurrencyMode = concurrencyMode; this.loadedProps = updateProps; this.changedProps = updateProps; this.vanilla = true; this.isDirty = true; this.oldValues = bean; if (bean instanceof EntityBean) { this.intercept = ((EntityBean) bean)._ebean_getIntercept(); } else { this.intercept = null; } }
@SuppressWarnings("unchecked") public PersistRequestBean( SpiEbeanServer server, T bean, Object parentBean, BeanManager<T> mgr, SpiTransaction t, PersistExecute persistExecute) { super(server, t, persistExecute); this.beanManager = mgr; this.beanDescriptor = mgr.getBeanDescriptor(); this.beanPersistListener = beanDescriptor.getPersistListener(); this.bean = bean; this.parentBean = parentBean; this.controller = beanDescriptor.getPersistController(); this.concurrencyMode = beanDescriptor.getConcurrencyMode(); if (bean instanceof EntityBean) { this.intercept = ((EntityBean) bean)._ebean_getIntercept(); if (intercept.isReference()) { // allowed to delete reference objects // with no concurrency checking this.concurrencyMode = ConcurrencyMode.NONE; } // this is ok to not use isNewOrDirty() as used for updates only this.isDirty = intercept.isDirty(); if (!isDirty) { this.changedProps = intercept.getChangedProps(); } else { // merge changed properties on the bean with changed embedded beans Set<String> beanChangedProps = intercept.getChangedProps(); Set<String> dirtyEmbedded = beanDescriptor.getDirtyEmbeddedProperties(bean); this.changedProps = mergeChangedProperties(beanChangedProps, dirtyEmbedded); } this.loadedProps = intercept.getLoadedProps(); this.oldValues = (T) intercept.getOldValues(); this.vanilla = false; } else { // have to assume the vanilla bean is dirty this.vanilla = true; this.isDirty = true; this.loadedProps = null; this.changedProps = null; this.intercept = null; // degrade concurrency checking to none for vanilla bean if (concurrencyMode.equals(ConcurrencyMode.ALL)) { this.concurrencyMode = ConcurrencyMode.NONE; } } }
/** * Create a GenerateDmlRequest used to generate the DML. * * <p>Will used changed properties or loaded properties depending on the * BeanDescriptor.isUpdateChangesOnly() value. */ public GenerateDmlRequest createGenerateDmlRequest(boolean emptyStringAsNull) { if (beanDescriptor.isUpdateChangesOnly()) { return new GenerateDmlRequest(emptyStringAsNull, changedProps, loadedProps, oldValues); } else { return new GenerateDmlRequest(emptyStringAsNull, loadedProps, loadedProps, oldValues); } }
/** Allocate the property to a list. */ private void allocateToList(BeanProperty prop) { if (prop.isTransient()) { transients.add(prop); if (prop.isDraft()) { draft = prop; } return; } if (prop.isId()) { ids.add(prop); return; } else { nonTransients.add(prop); } if (prop.isMutableScalarType()) { mutable.add(prop); } if (desc.getInheritInfo() != null && prop.isLocal()) { local.add(prop); } if (prop instanceof BeanPropertyAssocMany<?>) { manys.add((BeanPropertyAssocMany<?>) prop); } else { nonManys.add(prop); if (prop instanceof BeanPropertyAssocOne<?>) { BeanPropertyAssocOne<?> assocOne = (BeanPropertyAssocOne<?>) prop; if (prop.isEmbedded()) { embedded.add(assocOne); } else { ones.add(assocOne); if (!assocOne.isOneToOneExported()) { onesImported.add(assocOne); } } } else { // its a "base" property... if (prop.isVersion()) { if (versionProperty == null) { versionProperty = prop; } else { logger.warn( "Multiple @Version properties - property " + prop.getFullBeanName() + " not treated as a version property"); } } else if (prop.isDraftDirty()) { draftDirty = prop; } if (prop instanceof BeanPropertyCompound) { baseCompound.add((BeanPropertyCompound) prop); } else { baseScalar.add(prop); } } } }
public void notifyCache() { if (notifyCache) { switch (type) { case INSERT: beanDescriptor.cacheInsert(idValue, this); break; case UPDATE: beanDescriptor.cacheUpdate(idValue, this); break; case DELETE: beanDescriptor.cacheDelete(idValue, this); break; default: throw new IllegalStateException("Invalid type " + type); } } }
/** * Setup the load context at this path with OrmQueryProperties which is used to build the * appropriate query for +query or +lazy loading. */ private void registerSecondaryQuery(OrmQueryProperties props) { String propName = props.getPath(); ElPropertyValue elGetValue = rootDescriptor.getElGetValue(propName); boolean many = elGetValue.getBeanProperty().containsMany(); registerSecondaryNode(many, props); }
/** Generate and bind the insert statement. */ @Override public void bind() throws SQLException { BeanDescriptor<?> desc = persistRequest.getBeanDescriptor(); EntityBean bean = persistRequest.getEntityBean(); Object idValue = desc.getId(bean); boolean withId = !DmlUtil.isNullOrZero(idValue); // check to see if we are going to use generated keys if (!withId) { if (concatinatedKey) { // expecting a concatenated key that can // be built from supplied AssocOne beans withId = meta.deriveConcatenatedId(persistRequest); } else if (meta.supportsGetGeneratedKeys()) { // Identity with getGeneratedKeys useGeneratedKeys = true; } else { // use a query to get the last inserted id selectLastInsertedId = meta.getSelectLastInsertedId(); } } SpiTransaction t = persistRequest.getTransaction(); // get the appropriate sql sql = meta.getSql(withId); PreparedStatement pstmt; if (persistRequest.isBatched()) { pstmt = getPstmt(t, sql, persistRequest, useGeneratedKeys); } else { pstmt = getPstmt(t, sql, useGeneratedKeys); } dataBind = new DataBind(pstmt); // bind the bean property values meta.bind(this, bean, withId); logSql(sql); }
/** Set the type of this request. One of INSERT, UPDATE, DELETE, UPDATESQL or CALLABLESQL. */ @Override public void setType(Type type) { this.type = type; notifyCache = beanDescriptor.isCacheNotify(); if (type == Type.DELETE || type == Type.UPDATE) { if (oldValues == null) { oldValues = bean; } } }
/** Set the generated key back to the bean. Only used for inserts with getGeneratedKeys. */ public void setGeneratedKey(Object idValue) { if (idValue != null) { // set back to the bean so that we can use the same bean later // for update [refer ebeanIntercept.setLoaded(true)]. idValue = beanDescriptor.convertSetId(idValue, bean); // remember it for logging summary this.idValue = idValue; } }
/** * The hash used to register the bean with the transaction. * * <p>Takes into account the class type and id value. */ private Integer getBeanHash() { if (beanHash == null) { Object id = beanDescriptor.getId(bean); int hc = 31 * bean.getClass().getName().hashCode(); if (id != null) { hc += id.hashCode(); } beanHash = Integer.valueOf(hc); } return beanHash; }
protected void executeDerivedRelationships() { List<DerivedRelationshipData> derivedRelationships = persistRequest.getDerivedRelationships(); if (derivedRelationships != null) { SpiEbeanServer ebeanServer = (SpiEbeanServer) persistRequest.getEbeanServer(); for (int i = 0; i < derivedRelationships.size(); i++) { DerivedRelationshipData derivedRelationshipData = derivedRelationships.get(i); BeanDescriptor<?> beanDescriptor = ebeanServer.getBeanDescriptor(derivedRelationshipData.getBean().getClass()); BeanProperty prop = beanDescriptor.getBeanProperty(derivedRelationshipData.getLogicalName()); EntityBean entityBean = (EntityBean) derivedRelationshipData.getBean(); entityBean._ebean_getIntercept().markPropertyAsChanged(prop.getPropertyIndex()); ebeanServer.update(entityBean, transaction); } } }
/** * Add the bean to the collection passed. * * @param bean the bean to add * @param collection the collection or map to add the bean to */ @SuppressWarnings({"unchecked", "rawtypes"}) public void add(EntityBean bean, Object collection) { if (bean == null) { return; } rowCount++; if (isMap) { Object keyValue; if (mapKey != null) { // use the value for the property keyValue = desc.getValue(bean, mapKey); } else { // use the uniqueId for this keyValue = desc.getId(bean); } Map mapColl = (Map) collection; mapColl.put(keyValue, bean); } else { ((Collection) collection).add(bean); } }
/** * Determine the concurrency mode depending on fully/partially populated bean. * * <p>Specifically with version concurrency we want to check that the version property was one of * the loaded properties. */ public ConcurrencyMode determineConcurrencyMode() { if (loadedProps != null) { // 'partial bean' update/delete... if (concurrencyMode.equals(ConcurrencyMode.VERSION)) { // check the version property was loaded BeanProperty prop = beanDescriptor.firstVersionProperty(); if (prop != null && loadedProps.contains(prop.getName())) { // OK to use version property } else { concurrencyMode = ConcurrencyMode.NONE; } } } return concurrencyMode; }
/** Return a summary description of this query. */ public String getSummary() { StringBuilder sb = new StringBuilder(80); sb.append("FindIds exeMicros[") .append(executionTimeMicros) .append("] rows[") .append(rowCount) .append("] type[") .append(desc.getName()) .append("] predicates[") .append(predicates.getLogWhereSql()) .append("] bind[") .append(bindLog) .append("]"); return sb.toString(); }
private void logSummary() { String name = beanDescriptor.getName(); switch (type) { case INSERT: transaction.logInternal("Inserted [" + name + "] [" + idValue + "]"); break; case UPDATE: transaction.logInternal("Updated [" + name + "] [" + idValue + "]"); break; case DELETE: transaction.logInternal("Deleted [" + name + "] [" + idValue + "]"); break; default: break; } }
public void setLoadedState() { if (ebi != null) { // takes into account reference beans beanDescriptor.setLoadedProps(ebi, loadedProps); } }
/** There is a many property so we need to make sure the ordering is appropriate. */ private String deriveOrderByWithMany(BeanPropertyAssocMany<?> manyProp) { if (manyProp == null) { return parseOrderBy(); } String orderBy = parseOrderBy(); BeanDescriptor<?> desc = request.getBeanDescriptor(); String orderById = desc.getDefaultOrderBy(); if (orderBy == null) { orderBy = orderById; } // check for default ordering on the many property... String manyOrderBy = manyProp.getFetchOrderBy(); if (manyOrderBy != null) { orderBy = orderBy + ", " + CQueryBuilder.prefixOrderByFields(manyProp.getName(), manyOrderBy); } if (request.isFindById()) { // only one master bean so should be fine... return orderBy; } if (orderBy.startsWith(orderById)) { return orderBy; } // more than one top level row may be returned so // we need to make sure their is an order by on the // top level first (to ensure master/detail construction). int manyPos = orderBy.indexOf(manyProp.getName()); int idPos = orderBy.indexOf(" " + orderById); if (manyPos == -1) { // no ordering of the many if (idPos == -1) { // append the orderById so that master level objects are ordered // even if the orderBy is not unique for the master object return orderBy + ", " + orderById; } // orderById is already in the order by clause return orderBy; } if (idPos <= -1 || idPos >= manyPos) { if (idPos > manyPos) { // there was an error with the order by... String msg = "A Query on [" + desc + "] includes a join to a 'many' association [" + manyProp.getName(); msg += "] with an incorrect orderBy [" + orderBy + "]. The id property [" + orderById + "]"; msg += " must come before the many property [" + manyProp.getName() + "] in the orderBy."; msg += " Ebean has automatically modified the orderBy clause to do this."; logger.warn(msg); } // the id needs to come before the manyPropName orderBy = orderBy.substring(0, manyPos) + orderById + ", " + orderBy.substring(manyPos); } return orderBy; }
/** * Return true if the update DML/SQL must be dynamically generated. * * <p>This is the case for updates/deletes of partially populated beans. */ public boolean isDynamicUpdateSql() { return !vanilla && beanDescriptor.isUpdateChangesOnly() || (loadedProps != null); }
/** Validate the bean. This is not recursive and only runs the 'local' validation rules. */ public void validate() { InvalidValue errs = beanDescriptor.validate(false, bean); if (errs != null) { throw new ValidationException(errs); } }
/** Return the Id value for the bean. */ public Object getBeanId() { return beanDescriptor.getId(bean); }
/** * Returns a description of the request. This is typically the bean class name or the base table * for MapBeans. * * <p>Used to determine common persist requests for queueing and statement batching. */ public String getFullName() { return beanDescriptor.getFullName(); }
/** Execute the query returning the row count. */ public BeanIdList findIds() throws SQLException { long startNano = System.nanoTime(); try { // get the list that we are going to put the id's into. // This was already set so that it is available to be // read by other threads (it is a synchronised list) List<Object> idList = query.getIdList(); if (idList == null) { // running in foreground thread (not FutureIds query) idList = Collections.synchronizedList(new ArrayList<Object>()); query.setIdList(idList); } BeanIdList result = new BeanIdList(idList); SpiTransaction t = request.getTransaction(); Connection conn = t.getInternalConnection(); pstmt = conn.prepareStatement(sql); if (query.getBufferFetchSizeHint() > 0) { pstmt.setFetchSize(query.getBufferFetchSizeHint()); } if (query.getTimeout() > 0) { pstmt.setQueryTimeout(query.getTimeout()); } bindLog = predicates.bind(new DataBind(pstmt)); ResultSet rset = pstmt.executeQuery(); dataReader = new RsetDataReader(rset); boolean hitMaxRows = false; boolean hasMoreRows = false; rowCount = 0; DbReadContext ctx = new DbContext(); while (rset.next()) { Object idValue = desc.getIdBinder().read(ctx); idList.add(idValue); // reset back to 0 dataReader.resetColumnPosition(); rowCount++; if (maxRows > 0 && rowCount == maxRows) { hitMaxRows = true; hasMoreRows = rset.next(); break; } } if (hitMaxRows) { result.setHasMore(hasMoreRows); } long exeNano = System.nanoTime() - startNano; executionTimeMicros = (int) exeNano / 1000; return result; } finally { close(); } }
private BeanProperty getBeanProperty(BeanDescriptor<?> desc, String path) { return desc.getBeanPropertyFromPath(path); }