| /** |
| * <copyright> Copyright (c) 2005, 2006, 2007, 2008 Springsite BV (The Netherlands) and others All rights |
| * reserved. This program and the accompanying materials are made available under the terms of the |
| * Eclipse Public License v1.0 which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html Contributors: Martin Taal </copyright> $Id: |
| * AbstractAssociationMapper.java,v 1.17 2007/04/17 15:49:50 mtaal Exp $ |
| */ |
| |
| package org.eclipse.emf.teneo.hibernate.mapper; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.eclipse.emf.ecore.EAttribute; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EReference; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEAttribute; |
| import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEClass; |
| import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEReference; |
| import org.eclipse.emf.teneo.annotations.pamodel.PAnnotatedEStructuralFeature; |
| import org.eclipse.emf.teneo.annotations.pannotation.AssociationOverride; |
| import org.eclipse.emf.teneo.annotations.pannotation.CascadeType; |
| import org.eclipse.emf.teneo.annotations.pannotation.Column; |
| import org.eclipse.emf.teneo.annotations.pannotation.FetchType; |
| import org.eclipse.emf.teneo.annotations.pannotation.JoinColumn; |
| import org.eclipse.emf.teneo.annotations.pannotation.JoinTable; |
| import org.eclipse.emf.teneo.annotations.pannotation.ManyToOne; |
| import org.eclipse.emf.teneo.annotations.pannotation.MapKey; |
| import org.eclipse.emf.teneo.hibernate.hbannotation.Cascade; |
| import org.eclipse.emf.teneo.hibernate.hbannotation.HbCascadeType; |
| import org.eclipse.emf.teneo.hibernate.hbannotation.IdBag; |
| import org.eclipse.emf.teneo.hibernate.hbannotation.Index; |
| import org.eclipse.emf.teneo.hibernate.hbannotation.MapKeyManyToMany; |
| import org.eclipse.emf.teneo.hibernate.hbannotation.Parameter; |
| import org.eclipse.emf.teneo.hibernate.hbannotation.Type; |
| import org.eclipse.emf.teneo.hibernate.hbmodel.HbAnnotatedEClass; |
| import org.eclipse.emf.teneo.hibernate.hbmodel.HbAnnotatedEReference; |
| import org.eclipse.emf.teneo.hibernate.hbmodel.HbAnnotatedETypeElement; |
| import org.eclipse.emf.teneo.mapping.strategy.SQLNameStrategy; |
| import org.eclipse.emf.teneo.mapping.strategy.impl.ClassicSQLNameStrategy; |
| import org.eclipse.emf.teneo.simpledom.Element; |
| import org.eclipse.emf.teneo.util.StoreUtil; |
| |
| /** |
| * Class contains different convenience methods which are of use for association mapping. |
| * |
| * @author <a href="mailto:mtaal at elver.org">Martin Taal</a> |
| */ |
| public abstract class AbstractAssociationMapper extends AbstractMapper { |
| |
| /** Logger */ |
| private static final Log log = LogFactory.getLog(AbstractAssociationMapper.class); |
| |
| /** |
| * @return |
| */ |
| protected Element addOneToOne(PAnnotatedEReference aReference, String assocName, |
| String targetEntity) { |
| final PAnnotatedEClass referedToAClass = aReference.getAReferenceType(); |
| final Element element; |
| if (referedToAClass.isOnlyMapAsEntity() || !getHbmContext().forceUseOfInstance(referedToAClass)) { |
| element = getHbmContext().getCurrent().addElement("one-to-one") |
| .addAttribute("name", assocName).addAttribute("entity-name", targetEntity); |
| } else { |
| element = getHbmContext() |
| .getCurrent() |
| .addElement("one-to-one") |
| .addAttribute("name", assocName) |
| .addAttribute("class", |
| getHbmContext().getInstanceClassName(referedToAClass.getModelEClass())); |
| } |
| |
| if (aReference instanceof HbAnnotatedEReference) { |
| final HbAnnotatedEReference hae = (HbAnnotatedEReference) aReference; |
| if (hae.getHbFetch() != null) { |
| element.addAttribute("fetch", hae.getHbFetch().getValue().getName().toLowerCase()); |
| } |
| } |
| return element; |
| } |
| |
| // translates jpa CascadeType to HbCascadeType |
| protected List<HbCascadeType> getCascades(Cascade cascade, List<CascadeType> cascades, |
| boolean orphanRemoval) { |
| final List<HbCascadeType> result = cascade != null ? cascade.getValue() |
| : convertCascade(cascades); |
| if (orphanRemoval |
| && !(result.contains(HbCascadeType.ALL) || result.contains(HbCascadeType.DELETE_ORPHAN))) { |
| result.add(HbCascadeType.DELETE_ORPHAN); |
| } |
| return result; |
| } |
| |
| protected List<HbCascadeType> convertCascade(List<CascadeType> cascades) { |
| final List<HbCascadeType> res = new ArrayList<HbCascadeType>(); |
| for (CascadeType ct : cascades) { |
| if (ct == CascadeType.ALL) { |
| res.add(HbCascadeType.ALL); |
| } else if (ct == CascadeType.MERGE) { |
| res.add(HbCascadeType.MERGE); |
| } else if (ct == CascadeType.PERSIST) { |
| res.add(HbCascadeType.PERSIST); |
| res.add(HbCascadeType.SAVE_UPDATE); |
| res.add(HbCascadeType.LOCK); |
| } else if (ct == CascadeType.REFRESH) { |
| res.add(HbCascadeType.REFRESH); |
| } else if (ct == CascadeType.REMOVE) { |
| res.add(HbCascadeType.REMOVE); |
| } else if (ct == CascadeType.NONE) { |
| return new ArrayList<HbCascadeType>(); |
| } |
| } |
| return res; |
| } |
| |
| protected void mapMapsIdColumns() { |
| |
| } |
| |
| /** Adds a manytoone tag to the current element of the hbmcontext */ |
| protected Element addManyToOne(Element currentParent, PAnnotatedEReference aReference, |
| String referedTo, boolean isPartOfKey) { |
| final String assocName = getHbmContext().getPropertyName(aReference.getModelEReference()); |
| log.debug("addManyToOne " + assocName + "/" + referedTo); |
| |
| final String tagName; |
| if (((HbAnnotatedEReference) aReference).getHbType() != null) { |
| tagName = "property"; |
| final Element element = currentParent.addElement(tagName).addAttribute("name", assocName); |
| final Type hbType = ((HbAnnotatedEReference) aReference).getHbType(); |
| final List<Parameter> params = hbType.getParameters(); |
| if (params == null || params.isEmpty()) { |
| element.addAttribute("type", hbType.getType()); |
| } else { |
| final Element typeElement = element.addElement("type").addAttribute("name", |
| hbType.getType()); |
| for (Parameter param : params) { |
| typeElement.addElement("param").addAttribute("name", param.getName()) |
| .addText(param.getValue()); |
| } |
| } |
| return element; |
| } else if (isPartOfKey) { |
| tagName = "key-many-to-one"; |
| } else { |
| tagName = "many-to-one"; |
| } |
| |
| final EClass referedToEClass = aReference.getModelEReference().getEReferenceType(); |
| final PAnnotatedEClass referedToAClass = aReference.getPaModel().getPAnnotated(referedToEClass); |
| final Element element; |
| if (referedToAClass.isOnlyMapAsEntity() || !getHbmContext().forceUseOfInstance(referedToAClass)) { |
| element = currentParent.addElement(tagName).addAttribute("name", assocName) |
| .addAttribute("entity-name", referedTo); |
| } else { |
| element = currentParent.addElement(tagName).addAttribute("name", assocName) |
| .addAttribute("class", getHbmContext().getInstanceClassName(referedToEClass)); |
| } |
| |
| if (aReference instanceof HbAnnotatedEReference) { |
| final HbAnnotatedEReference hae = (HbAnnotatedEReference) aReference; |
| if (hae.getHbFetch() != null) { |
| element.addAttribute("fetch", hae.getHbFetch().getValue().getName().toLowerCase()); |
| } |
| } |
| |
| return element; |
| } |
| |
| /** |
| * Adds joincolumns to the associationElement, sets the insert and update attributes of the |
| * associationElement on the basis of the insertable/updatable attributes of the joinColumns. Note |
| * that the joinColumns list can be empty. forcenullable is set to true when a feature map entry |
| * is being processed. |
| */ |
| protected void addJoinColumns(PAnnotatedEReference per, Element associationElement, |
| List<JoinColumn> joinColumns, boolean forceNullable) { |
| |
| if (per.getMapsId() != null) { |
| if (mapMapsIdColumns(per, associationElement)) { |
| return; |
| } |
| } |
| |
| log.debug("addJoinColumns " + associationElement.getName() + "/ no of joincolumns" |
| + joinColumns.size()); |
| |
| // assumption is that if one column is not insertable then the |
| // association is |
| // not insertable, same for updatable |
| boolean insertable = true; |
| boolean updatable = true; |
| |
| for (JoinColumn joinColumn : joinColumns) { |
| log.debug("JoinColumn " + joinColumn.getName()); |
| |
| Element columnElement = associationElement |
| .addElement("column") |
| .addAttribute( |
| "not-null", |
| (joinColumn.isNullable() && joinColumn.isSetNullable()) || forceNullable ? "false" |
| : "true").addAttribute("unique", joinColumn.isUnique() ? "true" : "false"); |
| if (joinColumn.getName() != null) { |
| |
| columnElement.addAttribute("name", getHbmContext().trunc(joinColumn, joinColumn.getName())); |
| final String uc = getHbmContext().getUniqueConstraintKey(joinColumn.getName()); |
| if (uc != null) { |
| columnElement.addAttribute("unique-key", uc); |
| } |
| } |
| |
| final Index index = ((HbAnnotatedETypeElement) per).getHbIndex(); |
| if (index != null) { |
| columnElement.addAttribute("index", index.getName()); |
| } |
| |
| // keep track if all joinColumns are insertable/updatable for in |
| // that case the |
| // associationElement is also insertable/updatable or not |
| insertable &= joinColumn.isInsertable(); |
| updatable &= joinColumn.isUpdatable(); |
| |
| // disabled this because not-null is specified as optional on the |
| // many-to-one tag |
| // also unique is more difficult |
| // associationElement.addAttribute("not-null", |
| // !joinColumn.isNullable() ? "true" : "false"); |
| // associationElement.addAttribute("unique", joinColumn.isUnique() ? |
| // "true" : "false"); |
| // if |
| // (joinColumn.eIsSet(PannotationPackage.eINSTANCE.getJoinColumn_ReferencedColumnName())) |
| // TODO is this foreign key ? |
| // MT: see the property-ref in hibernate, is used when the reference |
| // is not on the primary key |
| // of the target table but on some other column |
| |
| // MT: TODO add check on not insertable/updatable which is strange |
| // for a joincolumn, this check |
| // is present in onetomany mapper |
| |
| // --- JJH |
| addCommentElement(per.getModelEReference(), columnElement); |
| // --- JJH |
| |
| if (joinColumn.getReferencedColumnName() != null) { |
| String propName = null; |
| final String colName = joinColumn.getReferencedColumnName(); |
| for (PAnnotatedEStructuralFeature pef : per.getPaEClass().getPaEStructuralFeatures()) { |
| if (pef instanceof PAnnotatedEReference) { |
| final PAnnotatedEReference paEreference = (PAnnotatedEReference) pef; |
| // set a default, by looking for the efeature name |
| if (pef.getModelElement().getName().equals(colName) && propName == null) { |
| propName = getHbmContext().getPropertyName(paEreference.getModelEReference()); |
| } |
| // use the columnname, that's according to the standard |
| if (paEreference.getColumn() != null |
| && paEreference.getColumn().getName().equals(colName)) { |
| propName = getHbmContext().getPropertyName(paEreference.getModelEReference()); |
| } |
| } |
| } |
| if (propName != null) { |
| associationElement.addAttribute("property-ref", propName); |
| } else { |
| log.warn("No property found for the referencedColumnName " + colName |
| + " and EReference " + per.getModelElement().getName()); |
| } |
| } |
| |
| if (joinColumn.getColumnDefinition() != null) { |
| columnElement.addAttribute("sql-type", joinColumn.getColumnDefinition()); |
| } |
| |
| } |
| // ugly but effective |
| if (associationElement.getName().compareTo("map-key-many-to-many") != 0 |
| && associationElement.getName().compareTo("join") != 0 |
| && associationElement.getName().compareTo("key-many-to-one") != 0) { |
| associationElement.addAttribute("insert", Boolean.toString(insertable)); |
| associationElement.addAttribute("update", Boolean.toString(updatable)); |
| } |
| |
| } |
| |
| /** |
| * Creates cascades for onetoone/manytoone, they differ from many relations because no |
| * delete-orphan is supported. |
| * |
| * @param associationElement |
| * : the element to which the cascades are added. |
| * @param cascade |
| * : list of cascade annotation types |
| */ |
| protected void addCascadesForSingle(Element associationElement, List<HbCascadeType> cascades) { |
| addCascades(associationElement, cascades, false); |
| } |
| |
| /** |
| * Adds a foreign key attribute to the collection element, if the aFeature has a foreign key |
| */ |
| protected void addForeignKeyAttribute(Element manyElement, PAnnotatedEStructuralFeature aFeature) { |
| if (aFeature.getForeignKey() != null) { |
| if (manyElement.getName().equals("key")) { |
| manyElement.addAttribute("foreign-key", getHbmContext().getSqlNameStrategy() |
| .adaptForeignKeyNameForKey(aFeature, aFeature.getForeignKey().getName())); |
| } else { |
| manyElement.addAttribute("foreign-key", aFeature.getForeignKey().getName()); |
| } |
| } |
| } |
| |
| /** |
| * Creates cascades for onetomany, it differs from single relations because delete-orphan is |
| * supported when cascade=all |
| * |
| * @param associationElement |
| * : the element to which the cascades are added. |
| * @param cascade |
| * : list of cascade annotation types |
| */ |
| protected void addCascadesForMany(Element associationElement, List<HbCascadeType> cascades) { |
| addCascades(associationElement, cascades, true); |
| } |
| |
| /** |
| * Sets the lazy attribute of the associationElement based on the fetchtype. |
| */ |
| protected void addFetchType(Element associationElement, FetchType fetch) { |
| if (fetch == null) { |
| return; |
| } |
| // TODO: when proxies are supported the below should be changed! |
| if (FetchType.EXTRA.equals(fetch)) { |
| associationElement.addAttribute("lazy", "extra"); |
| } else { |
| associationElement.addAttribute("lazy", FetchType.LAZY.equals(fetch) ? "true" : "false"); |
| } |
| } |
| |
| protected void addLazyProxy(Element element, FetchType fetch, PAnnotatedEReference paReference) { |
| final HbAnnotatedEClass haClass = (HbAnnotatedEClass) paReference.getAReferenceType(); |
| |
| boolean lazyFetch = fetch == null || fetch == FetchType.LAZY; |
| boolean doProxy = lazyFetch && (haClass.getHbProxy() != null && haClass.getHbProxy().isLazy()); |
| if (doProxy && lazyFetch) { |
| element.addAttribute("lazy", "proxy"); |
| } else { |
| element.addAttribute("lazy", "false"); |
| } |
| } |
| |
| /** |
| * Adds a listindex element with the column name set to the give collection element. |
| */ |
| protected void addListIndex(Element collElement, PAnnotatedEStructuralFeature aFeature) { |
| // TODO use column name generator |
| String name = getIndexColumnName(aFeature); |
| |
| log.debug("Add list index " + name + " to " + aFeature.getModelEStructuralFeature().getName()); |
| |
| if (aFeature.getOrderColumn() != null && aFeature.getOrderColumn().getName() != null) { |
| collElement.addElement("list-index").addAttribute("column", |
| getHbmContext().trunc(aFeature.getOrderColumn(), name)); |
| } else { |
| collElement.addElement("list-index").addAttribute("column", |
| getHbmContext().trunc(aFeature.getListIndexColumn(), name)); |
| } |
| } |
| |
| protected String getIndexColumnName(PAnnotatedEStructuralFeature aFeature) { |
| final SQLNameStrategy sqlNameStrategy = getHbmContext().getExtensionManager().getExtension( |
| SQLNameStrategy.class); |
| if (aFeature.getOrderColumn() != null && aFeature.getOrderColumn().getName() != null) { |
| return aFeature.getOrderColumn().getName(); |
| } |
| if (aFeature.getListIndexColumn() != null) { |
| return aFeature.getListIndexColumn().getName(); |
| } |
| if (sqlNameStrategy instanceof ClassicSQLNameStrategy) { |
| return ((ClassicSQLNameStrategy) sqlNameStrategy).getIndexColumnName(aFeature); |
| } else { |
| return getHbmContext().getPersistenceOptions().getSQLColumnNamePrefix() |
| + (aFeature.getPaEClass().getModelEClass().getName() + "_" |
| + aFeature.getModelEStructuralFeature().getName() + "_IDX").toUpperCase(); |
| } |
| } |
| |
| /** |
| * Adds a map-key element with the column name set to the give selected column element. |
| */ |
| // bugzilla 238515 |
| // protected void addMapKey(Element collElement, |
| // PAnnotatedEStructuralFeature aFeature, MapKey |
| // mapKey) { |
| // |
| // log.debug("Add map key " + mapKey.getName() + " to " + |
| // aFeature.getModelEStructuralFeature().getName()); |
| // |
| // // now, we add the column type. this is a required field |
| // final EStructuralFeature keyFeature = |
| // ((EReference) |
| // aFeature.getModelElement()).getEReferenceType().getEStructuralFeature("key"); |
| // if (keyFeature instanceof EReference) { |
| // final PAnnotatedEClass referedAClass = |
| // aFeature.getPaModel().getPAnnotated(((EReference) |
| // keyFeature).getEReferenceType()); |
| // if (referedAClass.isOnlyMapAsEntity() || |
| // !getHbmContext().forceUseOfInstance(referedAClass)) { |
| // final String entityName = hbmContext.getEntityName(((EReference) |
| // keyFeature).getEReferenceType()); |
| // collElement.addElement("map-key-many-to-many").addAttribute("entity-name", |
| // entityName); |
| // } else { |
| // collElement.addElement("map-key-many-to-many").addAttribute("class", |
| // getHbmContext().getInstanceClassName(referedAClass.getModelEClass())); |
| // } |
| // } else { |
| // final PAnnotatedEAttribute paAttribute = |
| // (PAnnotatedEAttribute) aFeature.getPaModel().getPAnnotated(keyFeature); |
| // final Element mapKeyElement = |
| // collElement.addElement("map-key").addAttribute("column", |
| // getHbmContext().trunc(mapKey.getName())); |
| // setType(paAttribute, mapKeyElement); |
| // } |
| // // "type", attr.getEType().getInstanceClassName()); |
| // } |
| /** |
| * Add a mapkey taking into account if the key is an entity or a simple type |
| */ |
| protected void addMapKey(Element collElement, PAnnotatedEReference aref) { |
| final EReference eref = aref.getModelEReference(); |
| final HbAnnotatedEReference hbRef = (HbAnnotatedEReference) aref; |
| final EStructuralFeature keyFeature = eref.getEReferenceType().getEStructuralFeature("key"); |
| |
| final PAnnotatedEStructuralFeature paKeyFeature = aref.getPaModel().getPAnnotated(keyFeature); |
| PAnnotatedEAttribute paAttribute = null; |
| if (paKeyFeature instanceof PAnnotatedEAttribute) { |
| |
| paAttribute = (PAnnotatedEAttribute) paKeyFeature; |
| |
| // put different things that are defined at reference level to the |
| // key |
| if (paAttribute.getEnumerated() == null && aref.getMapKeyEnumerated() != null) { |
| paAttribute.setEnumerated(EcoreUtil.copy(aref.getMapKeyEnumerated())); |
| } |
| if (paAttribute.getTemporal() == null && aref.getMapKeyTemporal() != null) { |
| paAttribute.setTemporal(EcoreUtil.copy(aref.getMapKeyTemporal())); |
| } |
| } |
| |
| if (hbRef.getHbMapKey() != null && hbRef.getMapKey() != null) { |
| log.warn("The EReference " |
| + aref.getModelElement().getName() |
| + " has both a javax.persistence.MapKey as well as a hibernate MapKey annotation this is not correct, only one of the two should be used."); |
| } |
| |
| if (keyFeature == null) { |
| throw new IllegalArgumentException("The EFeature " + eref.getName() + " of EClass " |
| + eref.getEContainingClass().getName() + " does not have a keyfeature. " |
| + "Are you sure that this feature is an EMap"); |
| } |
| |
| if (hbRef.getHbMapKey() != null) { |
| final org.eclipse.emf.teneo.hibernate.hbannotation.HbMapKey mapKey = hbRef.getHbMapKey(); |
| final Element mapKeyElement = collElement.addElement("map-key"); |
| if (mapKey.getColumns() != null && mapKey.getColumns().size() > 0) { |
| addColumnsAndFormula(mapKeyElement, aref, mapKey.getColumns(), false, false, false, false); |
| } else if (hbRef.getMapKeyColumn() != null) { |
| final List<Column> mkColumns = new ArrayList<Column>(); |
| mkColumns.add(hbRef.getMapKeyColumn()); |
| addColumnsAndFormula(mapKeyElement, aref, mkColumns, false, false, false, false); |
| } |
| setType(paAttribute, mapKeyElement); |
| } else if (hbRef.getMapKey() != null) { |
| final MapKey mapKey = hbRef.getMapKey(); |
| final Element mapKeyElement = collElement.addElement("map-key"); |
| if (hbRef.getMapKeyColumn() != null) { |
| final List<Column> mkColumns = new ArrayList<Column>(); |
| mkColumns.add(hbRef.getMapKeyColumn()); |
| addColumnsAndFormula(mapKeyElement, aref, mkColumns, false, false, false, false); |
| } else { |
| mapKeyElement.addAttribute("column", |
| getHbmContext().trunc(hbRef.getMapKey(), mapKey.getName())); |
| } |
| |
| setType(paAttribute, mapKeyElement); |
| } else if (hbRef.getMapKeyManyToMany() != null) { |
| final MapKeyManyToMany mkm = hbRef.getMapKeyManyToMany(); |
| final PAnnotatedEClass referedAClass = aref.getPaModel().getPAnnotated( |
| ((EReference) keyFeature).getEReferenceType()); |
| final Element mkmElement = collElement.addElement("map-key-many-to-many"); |
| if (referedAClass.isOnlyMapAsEntity() || !getHbmContext().forceUseOfInstance(referedAClass)) { |
| final String entityName = mkm.getTargetEntity() != null ? mkm.getTargetEntity() |
| : hbmContext.getEntityName(((EReference) keyFeature).getEReferenceType()); |
| mkmElement.addAttribute("entity-name", entityName); |
| } else { |
| mkmElement.addAttribute("class", |
| getHbmContext().getInstanceClassName(referedAClass.getModelEClass())); |
| } |
| if (mkm.getJoinColumns() != null && mkm.getJoinColumns().size() > 0) { |
| addJoinColumns(hbRef, mkmElement, mkm.getJoinColumns(), false); |
| } else if (hbRef.getMapKeyJoinColumns().size() > 0) { |
| final List<JoinColumn> jcs = new ArrayList<JoinColumn>(); |
| jcs.addAll(hbRef.getMapKeyJoinColumns()); |
| addJoinColumns(hbRef, mkmElement, jcs, false); |
| } |
| } else if (keyFeature instanceof EReference) { |
| final PAnnotatedEClass referedAClass = aref.getPaModel().getPAnnotated( |
| ((EReference) keyFeature).getEReferenceType()); |
| if (referedAClass.isOnlyMapAsEntity() || !getHbmContext().forceUseOfInstance(referedAClass)) { |
| final String entityName = hbmContext.getEntityName(((EReference) keyFeature) |
| .getEReferenceType()); |
| collElement.addElement("map-key-many-to-many").addAttribute("entity-name", entityName); |
| } else { |
| collElement.addElement("map-key-many-to-many").addAttribute("class", |
| getHbmContext().getInstanceClassName(referedAClass.getModelEClass())); |
| } |
| Element mkmElement = collElement.element("map-key-many-to-many"); |
| if (hbRef.getMapKeyJoinColumns().size() > 0) { |
| final List<JoinColumn> jcs = new ArrayList<JoinColumn>(); |
| jcs.addAll(hbRef.getMapKeyJoinColumns()); |
| addJoinColumns(hbRef, mkmElement, jcs, false); |
| } |
| } else { |
| // final String type = |
| // hbType(aref.getPaModel().getPAnnotated((EAttribute) feature)); |
| final Element mapKeyElement = collElement.addElement("map-key"); // .addAttribute("type", |
| // type); |
| setType(paAttribute, mapKeyElement); |
| if (hbRef.getMapKeyColumn() != null) { |
| final List<Column> mkColumns = new ArrayList<Column>(); |
| mkColumns.add(hbRef.getMapKeyColumn()); |
| addColumnsAndFormula(mapKeyElement, aref, mkColumns, false, false, false, false); |
| } |
| } |
| } |
| |
| /** |
| * @return a newly added hibernate for given collection |
| * @deprecated use addCollectionElement(PAnnotatedEStructuralFeature) instead. protected Element |
| * addCollectionElement(String name, boolean isIndexed) { return |
| * getHbmContext().getCurrent().addElement(isIndexed ? "list" : |
| * "bag").addAttribute("name", name); // .addAttribute("access", |
| * "org.eclipse.emf.teneo.hibernate.mapping.elist.EListPropertyAccessor" ); } |
| */ |
| |
| /** |
| * Creates a Hibernate collection element: |
| * <ul> |
| * <li>"<list>" if the collection is indexed. |
| * <li>"<bag>" if the collection is not indexed. |
| * <li>"<idbag>" if the collection is not indexed and has an IdBag annotation. |
| * </ul> |
| * |
| * @param hbFeature |
| * The structural feature for which to create collection. |
| * @return The collection element. |
| */ |
| protected Element addCollectionElement(PAnnotatedEStructuralFeature paFeature) { |
| final Element collectionElement; |
| HbAnnotatedETypeElement hbFeature = (HbAnnotatedETypeElement) paFeature; |
| final PAnnotatedEReference paReference = paFeature instanceof PAnnotatedEReference ? (PAnnotatedEReference) paFeature |
| : null; |
| final IdBag idBag = hbFeature.getHbIdBag(); |
| |
| final EStructuralFeature estruct = paFeature.getModelEStructuralFeature(); |
| final boolean isArray = estruct instanceof EAttribute |
| && estruct.getEType().getInstanceClass() != null |
| && estruct.getEType().getInstanceClass().isArray(); |
| final boolean isMap = StoreUtil.isMap(estruct) && getHbmContext().isMapEMapAsTrueMap(); |
| |
| // disabled following check because it also failed for many eattribute |
| // which even with a onetomany |
| // do not create a onetomany tag |
| // if (paFeature.getOneToMany() != null && paFeature.getJoinTable() == |
| // null && idBag != null) { |
| // throw new MappingException("Cannot use one-to-many attribute mapping |
| // without jointable in combination with |
| // IdBag."); |
| // } |
| final boolean hasOrderBy = paFeature instanceof PAnnotatedEReference |
| && (paReference.getOrderBy() != null && paReference.getOrderColumn() == null); |
| final boolean hasWhereClause = paFeature instanceof PAnnotatedEReference |
| && ((HbAnnotatedEReference) paFeature).getHbWhere() != null; |
| |
| if (isArray) { // array type |
| collectionElement = getHbmContext().getCurrent().addElement("array"); |
| } else if (isMap) { |
| collectionElement = getHbmContext().getCurrent().addElement("map"); |
| } else if (idBag != null) { |
| collectionElement = getHbmContext().getCurrent().addElement("idbag"); |
| } else if (getHbmContext().isGeneratedByEMF() && hbFeature.getOneToMany() != null |
| && hbFeature.getOneToMany().isList()) { |
| if (hasOrderBy && hbFeature.getOneToMany().isIndexed()) { |
| log.warn("One to many ereference has indexed=true and has orderby set. Ignoring indexed and using orderby, assuming set " |
| + hbFeature); |
| } |
| |
| if (hasOrderBy || !hbFeature.getOneToMany().isIndexed()) { |
| collectionElement = getHbmContext().getCurrent().addElement("bag"); |
| } else { |
| collectionElement = getHbmContext().getCurrent().addElement("list"); |
| } |
| } else if (!getHbmContext().isGeneratedByEMF() && hbFeature.getOneToMany() != null) { |
| if (hasOrderBy && hbFeature.getOneToMany().isIndexed()) { |
| log.warn("One to many ereference has indexed=true and has orderby set. " |
| + "Ignoring indexed and using orderby, assuming set " + hbFeature); |
| } |
| |
| if (!hbFeature.getOneToMany().isList() || hasOrderBy) { |
| collectionElement = getHbmContext().getCurrent().addElement("set"); |
| } else if (hbFeature.getOneToMany().isList() && !hbFeature.getOneToMany().isIndexed()) { |
| collectionElement = getHbmContext().getCurrent().addElement("bag"); |
| } else { |
| collectionElement = getHbmContext().getCurrent().addElement("list"); |
| } |
| } else if (hbFeature instanceof PAnnotatedEReference |
| && ((PAnnotatedEReference) hbFeature).getManyToMany() != null |
| && ((PAnnotatedEReference) hbFeature).getManyToMany().isList()) { |
| collectionElement = getHbmContext().getCurrent().addElement("list"); |
| } else { |
| collectionElement = getHbmContext().getCurrent().addElement("bag"); |
| } |
| |
| collectionElement.addAttribute("name", |
| getHbmContext().getPropertyName(hbFeature.getModelEStructuralFeature())); |
| if (idBag != null) { |
| final String generator = (idBag.getGenerator() == null ? "increment" : idBag.getGenerator()); |
| final String type = (idBag.getType() == null ? "long" : idBag.getType()); |
| // if (false && idBag.getTable() != null) { |
| // collectionElement.addAttribute("table", idBag.getTable()); |
| // } |
| final Element collectionIdElement = collectionElement.addElement("collection-id"); |
| collectionIdElement.addAttribute("column", hbmContext.getIdbagIDColumnName()); |
| collectionIdElement.addAttribute("type", type); |
| |
| collectionIdElement.addElement("generator").addAttribute("class", generator); |
| } |
| |
| if (hbFeature.getHbFetch() != null) { |
| collectionElement.addAttribute("fetch", hbFeature.getHbFetch().getValue().getName() |
| .toLowerCase()); |
| } |
| |
| if (hasOrderBy) { |
| final PAnnotatedEClass aClass = paReference.getAReferenceType(); |
| final String name; |
| final String orderByValue = paReference.getOrderBy().getValue(); |
| |
| if (orderByValue != null && orderByValue.contains("(")) { |
| // a sql function, just copy it completely |
| name = orderByValue; |
| } else { |
| name = getColumnNameForOrderBy(aClass, paReference.getOrderBy().getValue()); |
| } |
| collectionElement.addAttribute("order-by", name); |
| } |
| |
| if (hasWhereClause) { |
| collectionElement.addAttribute("where", ((HbAnnotatedEReference) paFeature).getHbWhere() |
| .getClause()); |
| } |
| |
| final boolean hasBatchSize = paFeature instanceof HbAnnotatedETypeElement |
| && ((HbAnnotatedETypeElement) paFeature).getBatchSize() != null; |
| |
| if (hasBatchSize) { |
| collectionElement.addAttribute("batch-size", "" |
| + ((HbAnnotatedETypeElement) paFeature).getBatchSize().getSize()); |
| } |
| |
| return collectionElement; |
| } |
| |
| // returns the column name of a certain feature in the target entity |
| protected String getColumnNameForOrderBy(PAnnotatedEClass aClass, String orderBy) { |
| // handle the case of multiple features separated by commas |
| final StringBuilder sb = new StringBuilder(); |
| String[] orderBys = null; |
| if (orderBy != null) { |
| orderBys = orderBy.split(","); |
| } else { |
| // empty orderBy, get an efeature with the id annotation and use |
| // that |
| for (PAnnotatedEStructuralFeature aFeature : getAllFeatures(aClass)) { |
| if (aFeature instanceof PAnnotatedEAttribute) { |
| final PAnnotatedEAttribute aAttribute = (PAnnotatedEAttribute) aFeature; |
| if (aAttribute.getId() != null) { |
| orderBys = new String[1]; |
| orderBys[0] = aAttribute.getModelEAttribute().getName(); |
| break; |
| } |
| } |
| } |
| if (orderBys == null) { |
| throw new MappingException( |
| "Orderby column can not be determined for association with elements of aClass " |
| + aClass); |
| } |
| } |
| for (String ob : orderBys) { |
| // handle direction asc/desc |
| ob = ob.trim(); |
| String direction = " asc"; |
| if (ob.indexOf(" ") != -1) { |
| final int index = ob.lastIndexOf(" "); |
| direction = ob.substring(index); |
| ob = ob.substring(0, index).trim(); |
| if (ob.trim().startsWith(getHbmContext().getEscapeCharacter())) { |
| ob = ob.trim().substring(getHbmContext().getEscapeCharacter().length()); |
| } |
| if (ob.trim().endsWith(getHbmContext().getEscapeCharacter())) { |
| ob = ob.trim().substring(0, |
| ob.trim().length() - getHbmContext().getEscapeCharacter().length()); |
| } |
| } |
| boolean found = false; |
| for (PAnnotatedEStructuralFeature aFeature : getAllFeatures(aClass)) { |
| if (aFeature.getModelElement().getName().compareTo(ob) == 0) { |
| if (aFeature instanceof PAnnotatedEReference) { |
| throw new MappingException("Feature " + ob |
| + " is an ereference, onle eattribute is supported"); |
| } |
| found = true; |
| final PAnnotatedEAttribute attr = (PAnnotatedEAttribute) aFeature; |
| final List<Column> cs = getColumns(attr); |
| if (cs.isEmpty()) { |
| if (sb.length() > 0) { |
| sb.append(","); |
| } |
| sb.append(escapeName(getColumnName(attr)) + direction); |
| } else { |
| for (Column c : cs) { |
| if (sb.length() > 0) { |
| sb.append(","); |
| } |
| sb.append(escapeName(c.getName()) + direction); |
| } |
| } |
| } |
| } |
| if (!found) { |
| throw new MappingException("Feature " + ob + " not found in eclass " |
| + aClass.getModelEClass().getName()); |
| } |
| } |
| return sb.toString(); |
| } |
| |
| private String escapeName(String name) { |
| // assume it also ends with it... |
| if (name.startsWith(getHbmContext().getEscapeCharacter())) { |
| return name; |
| } |
| return getHbmContext().getEscapeCharacter() + name + getHbmContext().getEscapeCharacter(); |
| } |
| |
| /** |
| * Add Element element in given collection element. |
| */ |
| protected Element addElementElement(Element collElement, PAnnotatedEStructuralFeature paFeature, |
| List<Column> columns, String targetEntity) { |
| final Element elElement; |
| // if (targetEntity == null || paAttribute.getEnumerated() != null || |
| // StoreUtil.isQName(paAttribute.getModelEAttribute())) { |
| // MT: the target type name is ignored for a many element, it is always |
| // recomputed |
| elElement = collElement.addElement("element"); |
| // } else { // in this case the defaultannotator has set the |
| // targetentity! |
| // elElement = collElement.addElement("element").addAttribute("type", |
| // targetEntity); |
| // } |
| if (columns != null && columns.size() > 0) { |
| addColumnsAndFormula(elElement, paFeature, columns, getHbmContext() |
| .isCurrentElementFeatureMap(), true); |
| } |
| setType(paFeature, elElement); |
| return elElement; |
| } |
| |
| protected boolean mapMapsIdColumns(PAnnotatedEReference per, Element parentElement) { |
| // find the embedded id type and its features |
| final PAnnotatedEClass aClass = per.getPaEClass(); |
| final String name = per.getMapsId().getValue(); |
| PAnnotatedEReference embeddedERef = null; |
| for (PAnnotatedEStructuralFeature pef : aClass.getPaEStructuralFeatures()) { |
| if (pef instanceof PAnnotatedEReference |
| && ((PAnnotatedEReference) pef).getEmbeddedId() != null) { |
| embeddedERef = (PAnnotatedEReference) pef; |
| } |
| } |
| if (embeddedERef == null) { |
| return false; |
| } |
| final PAnnotatedEClass embeddedIdAClass = embeddedERef.getAReferenceType(); |
| for (PAnnotatedEStructuralFeature aFeature : embeddedIdAClass.getPaEStructuralFeatures()) { |
| // if only map a specific name then ignore all efeatures which are |
| // not that name |
| if (name != null && !aFeature.getModelEStructuralFeature().getName().equals(name)) { |
| continue; |
| } |
| |
| if (aFeature instanceof PAnnotatedEAttribute) { |
| PAnnotatedEAttribute aAttribute = (PAnnotatedEAttribute) aFeature; |
| addColumnsAndFormula(parentElement, aAttribute, getColumns(aAttribute), getHbmContext() |
| .isCurrentElementFeatureMap(), false); |
| } else if (aFeature instanceof PAnnotatedEReference |
| && !((PAnnotatedEReference) aFeature).getModelEReference().isMany()) { |
| final PAnnotatedEReference embeddedIdReferenceProperty = (PAnnotatedEReference) aFeature; |
| final List<JoinColumn> jcs = getJoinColumns(embeddedIdReferenceProperty); |
| final ManyToOne mto = embeddedIdReferenceProperty.getManyToOne(); |
| |
| addJoinColumns(embeddedIdReferenceProperty, parentElement, jcs, getHbmContext() |
| .isDoForceOptional(embeddedIdReferenceProperty) |
| || mto.isOptional() |
| || getHbmContext().isCurrentElementFeatureMap()); |
| } |
| } |
| |
| // now set insertable/updatable to false |
| parentElement.addAttribute("insert", "false"); |
| parentElement.addAttribute("update", "false"); |
| return true; |
| } |
| |
| /** |
| * Adds columns to a key element. Also sets update on the key element based on the values in the |
| * columns. |
| */ |
| protected void addKeyColumns(HbAnnotatedETypeElement per, Element keyElement, |
| List<JoinColumn> joinColumns) { |
| if (per instanceof HbAnnotatedEReference) { |
| final HbAnnotatedEReference hbRef = (HbAnnotatedEReference) per; |
| if (hbRef.getMapsId() != null) { |
| if (mapMapsIdColumns(hbRef, keyElement)) { |
| return; |
| } |
| } |
| } |
| |
| log.debug("Adding key columns"); |
| boolean setUpdatable = false; |
| boolean isUpdatable = false; |
| for (JoinColumn joinColumn : joinColumns) { |
| log.debug("Column " + joinColumn.getName()); |
| |
| if (!setUpdatable && keyElement.getName().compareTo("key") == 0) { |
| isUpdatable = joinColumn.isUpdatable(); |
| keyElement.addAttribute("update", isUpdatable ? "true" : "false"); |
| setUpdatable = true; |
| } |
| |
| // these checks are disabled because they do not apply in case the |
| // join column |
| // is added to a join table |
| if (!joinColumn.isInsertable()) { |
| log.error("Unsupported non insertable join column in " + joinColumn); |
| throw new MappingException("Unsupported non insertable join column", joinColumn); |
| } |
| if (setUpdatable && joinColumn.isUpdatable() != isUpdatable) { |
| log.error("Unsupported join column updatable in " + joinColumn); |
| throw new MappingException("Unsupported join column updatable", joinColumn); |
| } |
| |
| final Element ce = keyElement.addElement("column") |
| .addAttribute("name", getHbmContext().trunc(joinColumn, joinColumn.getName())) |
| .addAttribute("unique", joinColumn.isUnique() ? "true" : "false"); |
| |
| // --- JJH, adapted by Martin Taal |
| addCommentElement(per.getModelElement(), ce); |
| // --- JJH |
| |
| if (per != null) { // is null in case of jointables |
| final Index index = (per).getHbIndex(); |
| if (index != null) { |
| ce.addAttribute("index", index.getName()); |
| } |
| } |
| |
| if (joinColumn.getReferencedColumnName() != null) { |
| String propName = null; |
| final String colName = joinColumn.getReferencedColumnName(); |
| for (PAnnotatedEStructuralFeature pef : per.getPaEClass().getPaEStructuralFeatures()) { |
| // set a default, by looking for the efeature name |
| if (pef.getModelElement().getName().equals(colName) && propName == null) { |
| propName = getHbmContext().getPropertyName(pef.getModelEStructuralFeature()); |
| } |
| // use the columnname, that's according to the standard |
| if (pef.getColumn() != null && pef.getColumn().getName().equals(colName)) { |
| propName = getHbmContext().getPropertyName(pef.getModelEStructuralFeature()); |
| } |
| } |
| if (propName != null) { |
| keyElement.addAttribute("property-ref", propName); |
| } else { |
| log.warn("No property found for the referencedColumnName " + colName |
| + " and EReference " + per.getModelElement().getName()); |
| } |
| } |
| |
| // bz247939, after disabling these lines it all seemed to work fine |
| // if (joinColumn.getTable() != null) { |
| // log.error("Unsupported secondary table in " + joinColumn); |
| // throw new MappingException("Unsupported secondary table", |
| // joinColumn); |
| // } |
| |
| if (joinColumn.getColumnDefinition() != null) { |
| log.error("Unsupported column definition in " + joinColumn); |
| throw new MappingException("Unsupported column definition", joinColumn); |
| } |
| } |
| // TODO jc.getReferencedColumnName(); |
| } |
| |
| /** |
| * Adds a jointable and possible joincolumns to the passed key element. |
| * |
| * @param collElement |
| * @param joinTable |
| */ |
| protected void addJoinTable(HbAnnotatedETypeElement hbAnnotatedElement, Element collElement, |
| Element keyElement, JoinTable joinTable) { |
| if (joinTable == null) { |
| log.debug("No joinTable"); |
| return; |
| } |
| if (joinTable.getCatalog() != null) { |
| collElement.addAttribute("catalog", getHbmContext().trunc(joinTable, joinTable.getCatalog())); |
| } |
| if (joinTable.getSchema() != null) { |
| collElement.addAttribute("schema", getHbmContext().trunc(joinTable, joinTable.getSchema())); |
| } |
| if (joinTable.getName() != null) { |
| collElement.addAttribute("table", getHbmContext().trunc(joinTable, joinTable.getName())); |
| } |
| if (joinTable.getUniqueConstraints().size() > 0) { |
| log.error("Unsupported unique constraints in " + joinTable); |
| throw new MappingException("Unsupported unique constraints", joinTable); |
| } |
| addKeyColumns(hbAnnotatedElement, keyElement, joinTable.getJoinColumns() |
| /* |
| * == null ? new ArrayList ( ) : ( List ) joinTable . getJoinColumns ( ) . getValue ( ) |
| */); |
| } |
| |
| protected JoinTable getJoinTable(PAnnotatedEReference paReference) { |
| final AssociationOverride override = getHbmContext().getAssociationOverrides(paReference); |
| if (override != null && override.getJoinTable() != null) { |
| return override.getJoinTable(); |
| } |
| final boolean isMap = StoreUtil.isMap(paReference.getModelEStructuralFeature()) |
| && getHbmContext().isMapEMapAsTrueMap(); |
| |
| if ((paReference.getEmbedded() != null || isMap || paReference.getElementCollection() != null) |
| && paReference.getCollectionTable() != null) { |
| return paReference.getCollectionTable(); |
| } |
| |
| return paReference.getJoinTable(); |
| } |
| } |