/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Rodrigo Westrupp */ package com.caucho.amber.type; import com.caucho.amber.AmberRuntimeException; import com.caucho.amber.cfg.AbstractConfigIntrospector; import com.caucho.amber.entity.AmberCompletion; import com.caucho.amber.entity.AmberEntityHome; import com.caucho.amber.entity.Entity; import com.caucho.amber.entity.EntityItem; import com.caucho.amber.entity.Listener; import com.caucho.amber.field.*; import com.caucho.amber.gen.EntityComponent; import com.caucho.amber.idgen.AmberTableGenerator; import com.caucho.amber.idgen.IdGenerator; import com.caucho.amber.idgen.SequenceIdGenerator; import com.caucho.amber.manager.AmberConnection; import com.caucho.amber.manager.AmberPersistenceUnit; import com.caucho.amber.table.AmberColumn; import com.caucho.amber.table.AmberTable; import com.caucho.config.ConfigException; import com.caucho.jdbc.*; import com.caucho.java.JavaWriter; import com.caucho.java.gen.ClassComponent; import com.caucho.lifecycle.Lifecycle; import com.caucho.util.CharBuffer; import com.caucho.util.L10N; import java.io.IOException; import java.lang.reflect.*; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.logging.Logger; /** * Base for entity or mapped-superclass types. */ public class EntityType extends BeanType { private static final Logger log = Logger.getLogger(EntityType.class.getName()); private static final L10N L = new L10N(EntityType.class); private EntityType _parentType; AmberTable _table; private String _rootTableName; private ArrayList<AmberTable> _secondaryTables = new ArrayList<AmberTable>(); private ArrayList<ListenerType> _listeners = new ArrayList<ListenerType>(); private ArrayList<ListenerCallback> _postLoadCallbacks = new ArrayList<ListenerCallback>(); private ArrayList<ListenerCallback> _prePersistCallbacks = new ArrayList<ListenerCallback>(); private ArrayList<ListenerCallback> _postPersistCallbacks = new ArrayList<ListenerCallback>(); private ArrayList<ListenerCallback> _preUpdateCallbacks = new ArrayList<ListenerCallback>(); private ArrayList<ListenerCallback> _postUpdateCallbacks = new ArrayList<ListenerCallback>(); private ArrayList<ListenerCallback> _preRemoveCallbacks = new ArrayList<ListenerCallback>(); private ArrayList<ListenerCallback> _postRemoveCallbacks = new ArrayList<ListenerCallback>(); private Id _id; private String _discriminatorValue; private boolean _isJoinedSubClass; private HashSet<String> _eagerFieldNames; private HashMap<String,EntityType> _subEntities; private ArrayList<AmberField> _mappedSuperclassFields = new ArrayList<AmberField>(); private ArrayList<AmberField> _fields; private boolean _hasDependent; private Class _proxyClass; private AmberEntityHome _home; protected int _defaultLoadGroupIndex; protected int _loadGroupIndex; protected int _minDirtyIndex; protected int _dirtyIndex; private boolean _excludeDefaultListeners; private boolean _excludeSuperclassListeners; protected boolean _hasLoadCallback; private HashMap<String,IdGenerator> _idGenMap = new HashMap<String,IdGenerator>(); private final Lifecycle _lifecycle = new Lifecycle(); private VersionField _versionField; private int _flushPriority; private boolean _isIdentityGenerator; private boolean _isSequenceGenerator; public EntityType(AmberPersistenceUnit amberPersistenceUnit) { super(amberPersistenceUnit); } /** * returns true for a loadable entity */ @Override public boolean isEntity() { return ! Modifier.isAbstract(getBeanClass().getModifiers()); } /** * Sets the table. */ public void setTable(AmberTable table) { _table = table; // jpa/0gg0 if (table == null) return; table.setType(this); if (_rootTableName == null) _rootTableName = table.getName(); } /** * Returns the table. */ public AmberTable getTable() { // jpa/0gg0 if (_table == null && ! isAbstractClass()) { String sqlName = AbstractConfigIntrospector.toSqlName(getName()); setTable(_amberPersistenceUnit.createTable(sqlName)); } return _table; } /** * Gets the instance class. */ public Class getInstanceClass() { return getInstanceClass(Entity.class); } /** * Returns the component interface name. */ @Override public String getComponentInterfaceName() { return "com.caucho.amber.entity.Entity"; } /** * Gets a component generator. */ @Override public ClassComponent getComponentGenerator() { return new EntityComponent(); } /** * Returns the flush priority. */ public int getFlushPriority() { return _flushPriority; } /** * Adds a mapped superclass field. */ public void addMappedSuperclassField(AmberField field) { if (_mappedSuperclassFields.contains(field)) return; _mappedSuperclassFields.add(field); Collections.sort(_mappedSuperclassFields, new AmberFieldCompare()); } /** * Returns the mapped superclass fields. */ public ArrayList<AmberField> getMappedSuperclassFields() { return _mappedSuperclassFields; } /** * Returns the mapped superclass field with a given name. */ public AmberField getMappedSuperclassField(String name) { for (int i = 0; i < _mappedSuperclassFields.size(); i++) { AmberField field = _mappedSuperclassFields.get(i); if (field.getName().equals(name)) return field; } return null; } /** * returns the merged fields */ @Override public ArrayList<AmberField> getFields() { if (_fields != null) return _fields; else return super.getFields(); } /** * Returns the root table name. */ public String getRootTableName() { return _rootTableName; } /** * Sets the root table name. */ public void setRootTableName(String rootTableName) { _rootTableName = rootTableName; } /** * Returns the version field. */ public VersionField getVersionField() { return _versionField; } /** * Sets the version field. */ public void setVersionField(VersionField versionField) { addField(versionField); _versionField = versionField; } /** * Adds a secondary table. */ public void addSecondaryTable(AmberTable table) { if (! _secondaryTables.contains(table)) { _secondaryTables.add(table); } table.setType(this); } /** * Gets the secondary tables. */ public ArrayList<AmberTable> getSecondaryTables() { return _secondaryTables; } /** * Adds an entity listener. */ public void addListener(ListenerType listener) { if (_listeners.contains(listener)) return; _listeners.add(listener); } /** * Gets the entity listeners. */ public ArrayList<ListenerType> getListeners() { return _listeners; } /** * Gets a secondary table. */ public AmberTable getSecondaryTable(String name) { for (AmberTable table : _secondaryTables) { if (table.getName().equals(name)) return table; } return null; } /** * Returns true if and only if it has a * many-to-one, one-to-one or embedded field/property. */ public boolean hasDependent() { return _hasDependent; } /** * Sets true if and only if it has a * many-to-one, one-to-one or embedded field/property. */ public void setHasDependent(boolean hasDependent) { _hasDependent = hasDependent; } /** * Returns the java type. */ @Override public String getForeignTypeName() { return getId().getForeignTypeName(); } /** * Gets the proxy class. */ public Class getProxyClass() { if (_proxyClass != null) return _proxyClass; else return _tBeanClass; } /** * Gets the proxy class. */ public void setProxyClass(Class proxyClass) { _proxyClass = proxyClass; } /** * Returns true if the corresponding class is abstract. */ public boolean isAbstractClass() { // ejb/0600 - EJB 2.1 are not abstract in this sense return (Modifier.isAbstract(getBeanClass().getModifiers()) && _proxyClass == null); } /** * Sets the id. */ public void setId(Id id) { _id = id; } /** * Returns the id. */ public Id getId() { return _id; } /** * Set true for joined-subclass */ public void setJoinedSubClass(boolean isJoinedSubClass) { _isJoinedSubClass = isJoinedSubClass; } /** * Set true for joined-subclass */ public boolean isJoinedSubClass() { if (getParentType() != null) return getParentType().isJoinedSubClass(); else return _isJoinedSubClass; } /** * Sets the discriminator value. */ public String getDiscriminatorValue() { if (_discriminatorValue != null) return _discriminatorValue; else { return getBeanClass().getSimpleName(); } } /** * Sets the discriminator value. */ public void setDiscriminatorValue(String value) { _discriminatorValue = value; } /** * Returns true if read-only */ public boolean isReadOnly() { return getTable().isReadOnly(); } /** * Sets true if read-only */ public void setReadOnly(boolean isReadOnly) { getTable().setReadOnly(isReadOnly); } /** * Returns the cache timeout. */ public long getCacheTimeout() { return getTable().getCacheTimeout(); } /** * Sets the cache timeout. */ public void setCacheTimeout(long timeout) { getTable().setCacheTimeout(timeout); } /** * Adds a new field. */ @Override public void addField(AmberField field) { super.addField(field); if (! field.isLazy()) { if (_eagerFieldNames == null) _eagerFieldNames = new HashSet<String>(); _eagerFieldNames.add(field.getName()); } } /** * Gets the EAGER field names. */ public HashSet<String> getEagerFieldNames() { return _eagerFieldNames; } /** * Returns the field with a given name. */ @Override public AmberField getField(String name) { if (_id != null) { ArrayList<IdField> keys = _id.getKeys(); for (int i = 0; i < keys.size(); i++) { IdField key = keys.get(i); if (key.getName().equals(name)) return key; } } return super.getField(name); } /** * Returns the columns. */ public ArrayList<AmberColumn> getColumns() { // jpa/0gg0 if (getTable() == null) return null; return getTable().getColumns(); } /** * Gets the exclude default listeners flag. */ public boolean getExcludeDefaultListeners() { return _excludeDefaultListeners; } /** * Sets the exclude default listeners flag. */ public void setExcludeDefaultListeners(boolean b) { _excludeDefaultListeners = b; } /** * Gets the exclude superclass listeners flag. */ public boolean getExcludeSuperclassListeners() { return _excludeSuperclassListeners; } /** * Sets the exclude superclass listeners flag. */ public void setExcludeSuperclassListeners(boolean b) { _excludeSuperclassListeners = b; } /** * True if the load lifecycle callback should be generated. */ public void setHasLoadCallback(boolean hasCallback) { _hasLoadCallback = hasCallback; } /** * True if the load lifecycle callback should be generated. */ public boolean getHasLoadCallback() { return _hasLoadCallback; } /** * Returns the root type. */ public EntityType getRootType() { EntityType parent = getParentType(); if (parent != null) return parent.getRootType(); else return this; } /** * Returns the parent type. */ public EntityType getParentType() { return _parentType; } /** * Returns the parent type. */ public void setParentType(EntityType parentType) { _parentType = parentType; } /** * Adds a sub-class. */ public void addSubClass(EntityType type) { if (_subEntities == null) _subEntities = new HashMap<String,EntityType>(); _subEntities.put(type.getDiscriminatorValue(), type); } /** * Gets a sub-class. */ public EntityType getSubClass(String discriminator) { if (_subEntities == null) return this; EntityType subType = _subEntities.get(discriminator); if (subType != null) return subType; else { // jpa/0l15 for (EntityType subEntity : _subEntities.values()) { subType = subEntity.getSubClass(discriminator); if (subType != subEntity) return subType; } return this; } } /** * Creates a new entity for this specific instance type. */ public Entity createBean() { try { Entity entity = (Entity) getInstanceClass().newInstance(); return entity; } catch (Exception e) { throw new AmberRuntimeException(e); } } /** * Returns the home. */ public AmberEntityHome getHome() { if (_home == null) { _home = getPersistenceUnit().getEntityHome(getName()); } return _home; } /** * Returns the next load group. */ public int nextLoadGroupIndex() { int nextLoadGroupIndex = getLoadGroupIndex() + 1; _loadGroupIndex = nextLoadGroupIndex; return nextLoadGroupIndex; } /** * Returns the current load group. */ public int getLoadGroupIndex() { return _loadGroupIndex; } /** * Sets the next default loadGroupIndex */ public void nextDefaultLoadGroupIndex() { _defaultLoadGroupIndex = nextLoadGroupIndex(); } /** * Returns the current load group. */ public int getDefaultLoadGroupIndex() { return _defaultLoadGroupIndex; } /** * Returns true if the load group is owned by this type (not a subtype). */ public boolean isLoadGroupOwnedByType(int i) { return getDefaultLoadGroupIndex() <= i && i <= getLoadGroupIndex(); } /** * Returns the next dirty index */ public int nextDirtyIndex() { int dirtyIndex = getDirtyIndex(); _dirtyIndex = dirtyIndex + 1; return dirtyIndex; } /** * Returns the current dirty group. */ public int getDirtyIndex() { return _dirtyIndex; } /** * Returns the min dirty group. */ public int getMinDirtyIndex() { return _minDirtyIndex; } /** * Returns true if the load group is owned by this type (not a subtype). */ public boolean isDirtyIndexOwnedByType(int i) { return getMinDirtyIndex() <= i && i < getDirtyIndex(); } /** * Initialize the entity. */ @Override public void init() throws ConfigException { if (getConfigException() != null) return; if (! _lifecycle.toInit()) return; super.init(); // forces table lazy load getTable(); initId(); _fields = getMergedFields(); for (AmberField field : getFields()) { if (field.isUpdateable()) field.setIndex(nextDirtyIndex()); field.init(); } if (getMappedSuperclassFields() == null) return; for (AmberField field : getMappedSuperclassFields()) { if (field.isUpdateable()) field.setIndex(nextDirtyIndex()); field.init(); } } protected void initId() { assert getId() != null : "null id for " + getName(); getId().init(); } protected ArrayList<AmberField> getMergedFields() { ArrayList<AmberField> mappedFields = getMappedSuperclassFields(); if (mappedFields == null) return getFields(); ArrayList<AmberField> resultFields = new ArrayList<AmberField>(); resultFields.addAll(getFields()); for (AmberField field : mappedFields) { resultFields.add(field); } Collections.sort(resultFields, new AmberFieldCompare()); return resultFields; } /** * Start the entry. */ public void start() throws ConfigException { init(); startGenerator(); startImpl(); if (! _lifecycle.toActive()) return; } private void startGenerator() { IdField idGenField = getId().getGeneratedIdField(); if (idGenField == null) return; JdbcMetaData md = getPersistenceUnit().getMetaData(); if ("sequence".equals(idGenField.getGenerator())) { _isIdentityGenerator = false; _isSequenceGenerator = true; if (! md.supportsSequences()) throw new ConfigException(L.l("'{0}' does not support sequences", md.getDatabaseName())); } else if ("identity".equals(idGenField.getGenerator())) { _isIdentityGenerator = true; _isSequenceGenerator = false; if (! md.supportsIdentity()) throw new ConfigException(L.l("'{0}' does not support identity", md.getDatabaseName())); } else if ("auto".equals(idGenField.getGenerator())) { if (md.supportsIdentity()) _isIdentityGenerator = true; else if (md.supportsSequences()) _isSequenceGenerator = true; } if (! _isIdentityGenerator && getGenerator(idGenField.getName()) == null) { IdGenerator gen; if (_isSequenceGenerator) { String name = getTable().getName() + "_cseq"; gen = getPersistenceUnit().createSequenceGenerator(name, 1); } else gen = getPersistenceUnit().getTableGenerator("caucho"); _idGenMap.put(idGenField.getName(), gen); } // XXX: really needs to be called from the table-init code for (IdGenerator idGen : _idGenMap.values()) { try { if (idGen instanceof SequenceIdGenerator) { ((SequenceIdGenerator) idGen).init(_amberPersistenceUnit); } else if (idGen instanceof AmberTableGenerator) { // jpa/0g60 ((AmberTableGenerator) idGen).init(_amberPersistenceUnit); } } catch (SQLException e) { throw ConfigException.create(e); } } } private void startImpl() { try { ClassLoader loader = Thread.currentThread().getContextClassLoader(); for (ListenerType listenerType : getListeners()) { String listenerClass = listenerType.getBeanClass().getName(); Class cl = Class.forName(listenerClass, false, loader); Object listener; try { listener = cl.newInstance(); } catch (InstantiationException e) { throw new ConfigException(L.l("'{0}' could not be instantiated.", cl)); } for (Method jMethod : listenerType.getCallbacks(Listener.PRE_PERSIST)) { Method method = getListenerMethod(cl, jMethod.getName()); if (method != null) _prePersistCallbacks.add(new ListenerCallback(listener, method)); } for (Method jMethod : listenerType.getCallbacks(Listener.POST_PERSIST)) { Method method = getListenerMethod(cl, jMethod.getName()); if (method != null) _postPersistCallbacks.add(new ListenerCallback(listener, method)); } for (Method jMethod : listenerType.getCallbacks(Listener.POST_LOAD)) { Method method = getListenerMethod(cl, jMethod.getName()); if (method != null) _postLoadCallbacks.add(new ListenerCallback(listener, method)); } } } catch (RuntimeException e) { throw e; } catch (Exception e) { throw ConfigException.create(e); } } private Method getListenerMethod(Class cl, String methodName) { if (cl == null || cl.equals(Object.class)) return null; Method []methods = cl.getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { Class []paramTypes = methods[i].getParameterTypes(); if (methods[i].getName().equals(methodName) && paramTypes.length == 1 && paramTypes[0].isAssignableFrom(getBeanClass())) { return methods[i]; } } return getListenerMethod(cl.getSuperclass(), methodName); } /** * Generates a string to load the field. */ @Override public int generateLoad(JavaWriter out, String rs, String indexVar, int index) throws IOException { out.print("(" + getInstanceClassName() + ") "); index = getId().generateLoadForeign(out, rs, indexVar, index); return index; } /** * Returns true if there's a field with the matching load group. */ public boolean hasLoadGroup(int loadGroupIndex) { if (loadGroupIndex == 0) return true; for (AmberField field : getFields()) { if (field.hasLoadGroup(loadGroupIndex)) return true; } return false; } /** * Generates loading code after the basic fields. */ public int generatePostLoadSelect(JavaWriter out, int index, int loadGroupIndex) throws IOException { if (loadGroupIndex == 0 && getDiscriminator() != null) index++; // jpa/0l40 for (EntityType type = this; type != null; type = type.getParentType()) { index = generatePostLoadSelect(out, index, type.getMappedSuperclassFields()); index = generatePostLoadSelect(out, index, type.getFields()); } return index; } private int generatePostLoadSelect(JavaWriter out, int index, ArrayList<AmberField> fields) throws IOException { if (fields != null) { for (int i = 0; i < fields.size(); i++) { AmberField field = fields.get(i); // jpa/0l40 if (field.getLoadGroupIndex() == loadGroupIndex) index = field.generatePostLoadSelect(out, index); } } return index; } /** * Generates the load code for native fields */ public void generateLoadNative(JavaWriter out) throws IOException { int index = 0; for (AmberField field : getFields()) { index = field.generateLoadNative(out, index); } } /** * Generates the load code for native fields */ public void generateNativeColumnNames(ArrayList<String> names) throws IOException { for (AmberField field : getFields()) { field.generateNativeColumnNames(names); } } /** * Generates a string to set the field. */ @Override public void generateSet(JavaWriter out, String pstmt, String index, String value) throws IOException { if (getId() != null) getId().generateSet(out, pstmt, index, value); } /** * Gets the value. */ @Override public Object getObject(AmberConnection aConn, ResultSet rs, int index) throws SQLException { return getHome().loadLazy(aConn, rs, index); } /** * Finds the object */ @Override public EntityItem findItem(AmberConnection aConn, ResultSet rs, int index) throws SQLException { return getHome().findItem(aConn, rs, index); } /** * Gets the value. */ public Object getLoadObject(AmberConnection aConn, ResultSet rs, int index) throws SQLException { return getHome().loadFull(aConn, rs, index); } /** * Returns true for sequence generator */ public boolean isSequenceGenerator() { return _isSequenceGenerator; } /** * Returns true for sequence generator */ public boolean isIdentityGenerator() { return _isIdentityGenerator; } /** * Sets the named generator. */ public void setGenerator(String name, IdGenerator gen) { _idGenMap.put(name, gen); } /** * Sets the named generator. */ public IdGenerator getGenerator(String name) { return _idGenMap.get(name); } /** * Gets the named generator. */ public long nextGeneratorId(AmberConnection aConn, String name) throws SQLException { IdGenerator idGen = _idGenMap.get(name); return idGen.allocate(aConn); } /** * Loads from an object. */ public void generateLoadFromObject(JavaWriter out, String obj) throws IOException { getId().generateLoadFromObject(out, obj); for (AmberField field : getFields()) { field.generateLoadFromObject(out, obj); } } /** * Copy from an object. */ public void generateCopyLoadObject(JavaWriter out, String dst, String src, int loadGroup) throws IOException { if (getParentType() != null) // jpa/0ge3 getParentType().generateCopyLoadObject(out, dst, src, loadGroup); ArrayList<AmberField> fields = getFields(); for (AmberField field : fields) { // XXX: setter issue, too field.generateCopyLoadObject(out, dst, src, loadGroup); } } /** * Copy from an object. */ public void generateMergeFrom(JavaWriter out, String dst, String src) throws IOException { if (getParentType() != null) getParentType().generateMergeFrom(out, dst, src); ArrayList<AmberField> fields = getFields(); for (int i = 0; i < fields.size(); i++) { AmberField field = fields.get(i); field.generateMergeFrom(out, dst, src); } } /** * Copy from an object. */ public void generateCopyUpdateObject(JavaWriter out, String dst, String src, int updateIndex) throws IOException { if (getParentType() != null) getParentType().generateCopyUpdateObject(out, dst, src, updateIndex); ArrayList<AmberField> fields = getFields(); for (int i = 0; i < fields.size(); i++) { AmberField field = fields.get(i); field.generateCopyUpdateObject(out, dst, src, updateIndex); } } /** * Checks entity-relationships from an object. */ public void generateDumpRelationships(JavaWriter out, int updateIndex) throws IOException { if (getParentType() != null) // jpa/0ge3 getParentType().generateDumpRelationships(out, updateIndex); ArrayList<AmberField> fields = getFields(); for (int i = 0; i < fields.size(); i++) { AmberField field = fields.get(i); field.generateDumpRelationships(out, updateIndex); } } /** * Generates the select clause for a load. */ public String generateKeyLoadSelect(String id) { String select = getId().generateLoadSelect(id); if (getDiscriminator() != null) { if (select != null && ! select.equals("")) select = select + ", "; select = select + getDiscriminator().getName(); } return select; } /** * Generates the select clause for a load. */ public String generateFullLoadSelect(String id) { CharBuffer cb = CharBuffer.allocate(); String idSelect = getId().generateSelect(id); if (idSelect != null) cb.append(idSelect); String loadSelect = generateLoadSelect(id); if (! idSelect.equals("") && ! loadSelect.equals("")) cb.append(","); cb.append(loadSelect); return cb.close(); } /** * Generates the select clause for a load. */ public String generateLoadSelect(String id) { return generateLoadSelect(getTable(), id); } /** * Generates the select clause for a load. */ public String generateLoadSelect(AmberTable table, String id) { StringBuilder sb = new StringBuilder(); // jpa/0l11 if (getTable() == table && getDiscriminator() != null) { if (id != null) { if (getDiscriminator().getTable() == getTable()) { sb.append(id + "."); sb.append(getDiscriminator().getName()); } else { // jpa/0l4b sb.append("'" + getDiscriminatorValue() + "'"); } } } generateLoadSelect(sb, table, id, 0); if (sb.length() > 0) return sb.toString(); else return null; } /** * Generates the select clause for a load. */ @Override public void generateLoadSelect(StringBuilder sb, AmberTable table, String id, int loadGroup) { if (_parentType != null) _parentType.generateLoadSelect(sb, table, id, loadGroup); super.generateLoadSelect(sb, table, id, loadGroup); } /** * Generates the auto insert sql. */ public String generateAutoCreateSQL(AmberTable table) { return generateCreateSQL(table, true); } /** * Generates the insert sql. */ public String generateCreateSQL(AmberTable table) { return generateCreateSQL(table, false); } /** * Generates the insert sql. */ private String generateCreateSQL(AmberTable table, boolean isAuto) { CharBuffer sql = new CharBuffer(); sql.append("insert into "); sql.append(JavaWriter.escapeJavaString(table.getName()) + " ("); boolean isFirst = true; ArrayList<String> idColumns = new ArrayList<String>(); for (IdField field : getId().getKeys()) { if (isAuto && field.getGenerator() != null) continue; for (AmberColumn key : field.getColumns()) { String name; if (table == key.getTable()) name = key.getName(); else name = table.getDependentIdLink().getSourceColumn(key).getName(); idColumns.add(name); if (! isFirst) sql.append(", "); isFirst = false; sql.append(name); } } if (table == getTable() && getDiscriminator() != null) { if (! isFirst) sql.append(", "); isFirst = false; sql.append(getDiscriminator().getName()); } ArrayList<String> columns = new ArrayList<String>(); generateInsertColumns(table, columns); for (String columnName : columns) { if (! isFirst) sql.append(", "); isFirst = false; sql.append(columnName); } sql.append(") values ("); isFirst = true; for (int i = 0; i < idColumns.size(); i++) { if (! isFirst) sql.append(", "); isFirst = false; sql.append("?"); } if (table == getTable() && getDiscriminator() != null) { if (! isFirst) sql.append(", "); isFirst = false; sql.append("'" + getDiscriminatorValue() + "'"); } for (int i = 0; i < columns.size(); i++) { if (! isFirst) sql.append(", "); isFirst = false; sql.append("?"); } sql.append(")"); return sql.toString(); } protected void generateInsertColumns(AmberTable table, ArrayList<String> columns) { if (getParentType() != null) getParentType().generateInsertColumns(table, columns); for (AmberField field : getFields()) { if (field.getTable() == table) field.generateInsertColumns(columns); } } /** * Generates the update sql. */ public void generateInsertSet(JavaWriter out, AmberTable table, String pstmt, String query, String obj) throws IOException { if (getParentType() != null) getParentType().generateInsertSet(out, table, pstmt, query, obj); for (AmberField field : getFields()) { if (field.getTable() == table) field.generateInsertSet(out, pstmt, query, obj); } } /** * Generates the select clause for a load. */ public String generateIdSelect(String id) { CharBuffer cb = CharBuffer.allocate(); cb.append(getId().generateSelect(id)); if (getDiscriminator() != null) { cb.append(", "); cb.append(getDiscriminator().getName()); } return cb.close(); } /** * Generates the update sql. */ public void generateUpdateSQLPrefix(CharBuffer sql) { sql.append("update " + getTable().getName() + " set "); } /** * Generates the update sql. * * @param sql the partially built sql * @param group the dirty group * @param mask the group's mask * @param isFirst marks the first set group */ public boolean generateUpdateSQLComponent(CharBuffer sql, int group, long mask, boolean isFirst) { ArrayList<AmberField> fields = getFields(); while (mask != 0) { int i = 0; for (i = 0; (mask & (1L << i)) == 0; i++) { } mask &= ~(1L << i); AmberField field = null; for (int j = 0; j < fields.size(); j++) { field = fields.get(j); if (field.getIndex() == i + group * 64) break; else field = null; } if (field != null) { // jpa/0x00 if (field instanceof VersionField) continue; if (! isFirst) sql.append(", "); isFirst = false; field.generateUpdate(sql); } } // jpa/0x00 for (int j = 0; j < fields.size(); j++) { AmberField field = fields.get(j); if (field instanceof VersionField) { if (! isFirst) sql.append(", "); isFirst = false; field.generateUpdate(sql); break; } } return isFirst; } /** * Generates the update sql. */ public void generateUpdateSQLSuffix(CharBuffer sql) { sql.append(" where "); sql.append(getId().generateMatchArgWhere(null)); // optimistic locking if (_versionField != null) { sql.append(" and "); sql.append(_versionField.generateMatchArgWhere(null)); } } /** * Generates the update sql. */ public String generateUpdateSQL(long mask) { if (mask == 0) return null; CharBuffer sql = CharBuffer.allocate(); sql.append("update " + getTable().getName() + " set "); boolean isFirst = true; ArrayList<AmberField> fields = getFields(); while (mask != 0) { int i = 0; for (i = 0; (mask & (1L << i)) == 0; i++) { } mask &= ~(1L << i); AmberField field = null; for (int j = 0; j < fields.size(); j++) { field = fields.get(j); if (field.getIndex() == i) break; else field = null; } if (field != null) { if (! isFirst) sql.append(", "); isFirst = false; field.generateUpdate(sql); } } if (isFirst) return null; sql.append(" where "); sql.append(getId().generateMatchArgWhere(null)); return sql.toString(); } /** * Generates code after the remove. */ public void generatePreDelete(JavaWriter out) throws IOException { for (AmberField field : getFields()) { field.generatePreDelete(out); } } /** * Generates code after the remove. */ public void generatePostDelete(JavaWriter out) throws IOException { for (AmberField field : getFields()) { field.generatePostDelete(out); } } /** * Deletes by the primary key. */ public void delete(AmberConnection aConn, Object key) throws SQLException { getHome().delete(aConn, key); } /** * Deletes by the primary key. */ public void update(Entity entity) throws SQLException { // aConn.addCompletion(_tableCompletion); } /** * Updates global (persistence unit) entity priorities * for flushing. */ public int updateFlushPriority(ArrayList<EntityType> updatingEntities) { // jpa/0h25, jpa/0h26, jpa/0h29, jpa/0j67 _flushPriority = 0; ArrayList<AmberField> fields = getFields(); for (int i = 0; i < fields.size(); i++) { AmberField field = fields.get(i); if (field instanceof ManyToOneField) { ManyToOneField manyToOne = (ManyToOneField) field; EntityType targetRelatedType = manyToOne.getEntityTargetType(); if (targetRelatedType instanceof EntityType) { EntityType targetType = (EntityType) targetRelatedType; if (! updatingEntities.contains(targetType)) { updatingEntities.add(targetType); targetType.updateFlushPriority(updatingEntities); } int targetPriority = targetType.getFlushPriority(); if (targetPriority >= _flushPriority) { EntityType type = null; // jpa/0j67 if (! manyToOne.isAnnotatedManyToOne()) { for (AmberField targetField : targetType.getFields()) { if (targetField instanceof ManyToOneField) { ManyToOneField targetManyToOne = (ManyToOneField) targetField; type = targetManyToOne.getEntityTargetType(); if (this == type) { if (targetManyToOne.isAnnotatedManyToOne()) { break; } } } } } if (this == type) continue; _flushPriority = targetPriority + 1; } } } } return _flushPriority; } /** * Returns a completion for the given field. */ public AmberCompletion createManyToOneCompletion(String name, Entity source, Object newTarget) { AmberField field = getField(name); EntityType parentType = this; // jpa/0l40 while (field == null) { parentType = parentType.getParentType(); if (parentType == null) break; field = parentType.getField(name); } if (field instanceof ManyToOneField) { ManyToOneField manyToOne = (ManyToOneField) field; return getTable().getInvalidateCompletion(); } else throw new IllegalStateException(); } /** * XXX: temp hack. */ public boolean isEJBProxy(String typeName) { return (getBeanClass() != getProxyClass() && getProxyClass().getName().equals(typeName)); } // // callbacks // /** * Callbacks before an entity is persisted */ public void prePersist(Entity entity) { for (int i = 0; i < _prePersistCallbacks.size(); i++) _prePersistCallbacks.get(i).invoke(entity); } /** * Callbacks after an entity is persisted */ public void postPersist(Entity entity) { for (int i = 0; i < _postPersistCallbacks.size(); i++) _postPersistCallbacks.get(i).invoke(entity); } /** * Callbacks before an entity is updateed */ public void preUpdate(Entity entity) { for (int i = 0; i < _preUpdateCallbacks.size(); i++) _preUpdateCallbacks.get(i).invoke(entity); } /** * Callbacks after an entity is updated */ public void postUpdate(Entity entity) { for (int i = 0; i < _postUpdateCallbacks.size(); i++) _postUpdateCallbacks.get(i).invoke(entity); } /** * Callbacks before an entity is removeed */ public void preRemove(Entity entity) { for (int i = 0; i < _preRemoveCallbacks.size(); i++) _preRemoveCallbacks.get(i).invoke(entity); } /** * Callbacks after an entity is removeed */ public void postRemove(Entity entity) { for (int i = 0; i < _postRemoveCallbacks.size(); i++) _postRemoveCallbacks.get(i).invoke(entity); } /** * Callbacks after an entity is loaded */ public void postLoad(Entity entity) { for (int i = 0; i < _postLoadCallbacks.size(); i++) _postLoadCallbacks.get(i).invoke(entity); } }