/* * 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.cfg; import com.caucho.amber.field.ManyToManyField; import com.caucho.amber.manager.AmberPersistenceUnit; import com.caucho.amber.table.AmberTable; import com.caucho.amber.table.ForeignColumn; import com.caucho.amber.table.LinkColumns; import com.caucho.amber.type.EntityType; import com.caucho.bytecode.JType; import com.caucho.bytecode.JTypeWrapper; import com.caucho.config.ConfigException; import com.caucho.util.L10N; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashMap; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; /** * The <many-to-many> tag in orm.xml */ class ManyToManyConfig extends AbstractRelationConfig { private static final L10N L = new L10N(ManyToManyConfig.class); private BaseConfigIntrospector _introspector; private EntityType _sourceType; private AccessibleObject _field; private String _fieldName; private Class _fieldType; // attributes private String _mappedBy; // elements private MapKeyConfig _mapKey; private String _orderBy; ManyToManyConfig(BaseConfigIntrospector introspector, EntityType sourceType, AccessibleObject field, String fieldName, Class fieldType) { _introspector = introspector; _sourceType = sourceType; _field = field; _fieldName = fieldName; _fieldType = fieldType; setName(_fieldName); introspect(); } public String getMappedBy() { return _mappedBy; } public void setMappedBy(String mappedBy) { _mappedBy = mappedBy; } public MapKeyConfig getMapKey() { return _mapKey; } public void setMapKey(MapKeyConfig mapKey) { _mapKey = mapKey; } public String getOrderBy() { return _orderBy; } public void setOrderBy(String orderBy) { _orderBy = orderBy; } public boolean isOwningSide() { return "".equals(_mappedBy); } private void introspect() { introspectTypes(); ManyToMany manyToMany = _field.getAnnotation(ManyToMany.class); if (manyToMany != null) introspectManyToMany(manyToMany); JoinTable joinTableAnn = _field.getAnnotation(JoinTable.class); if (joinTableAnn != null) setJoinTable(new JoinTableConfig(joinTableAnn)); } private void introspectTypes() { Type retType; if (_field instanceof Field) retType = ((Field) _field).getGenericType(); else retType = ((Method) _field).getGenericReturnType(); ClassLoader loader = _sourceType.getPersistenceUnit().getTempClassLoader(); JType type = JTypeWrapper.create(retType, loader); JType []typeArgs = type.getActualTypeArguments(); if (typeArgs.length > 0) { setTargetEntity(typeArgs[0].getRawType().getJavaClass()); } } private void introspectManyToMany(ManyToMany manyToMany) { Class targetClass = manyToMany.targetEntity(); if (! void.class.equals(targetClass)) setTargetEntity(targetClass); setCascadeTypes(manyToMany.cascade()); setFetch(manyToMany.fetch()); _mappedBy = manyToMany.mappedBy(); } private void introspectJoinColumns(JoinColumn []joinColumns) { for (JoinColumn joinColumn : joinColumns) { // addJoinColumn(new JoinColumnConfig(joinColumn)); } } @Override public EntityType getRelatedType() { return _sourceType; } @Override public void complete() { addManyToMany(_sourceType, _field, _fieldName, _fieldType); } void addManyToMany(EntityType sourceType, AccessibleObject field, String fieldName, Class fieldType) throws ConfigException { AmberPersistenceUnit persistenceUnit = _sourceType.getPersistenceUnit(); Class targetClass = getTargetEntity(); if (targetClass == null || void.class.equals(targetClass)) throw error(field, L.l("Can't determine targetEntity for {0}. @ManyToMany properties must target @Entity beans.", _fieldName)); EntityType targetType = persistenceUnit.getEntityType(targetClass); if (targetType == null) throw error(field, L.l("targetEntity '{0}' is not an @Entity bean for {1}. The targetEntity of a @ManyToMany collection must be an @Entity bean.", targetClass.getName(), _fieldName)); if (! isOwningSide()) addDependentSide(targetType); else addOwningSide(targetType); } private void addOwningSide(EntityType targetType) { AmberPersistenceUnit persistenceUnit = _sourceType.getPersistenceUnit(); ManyToManyField manyToManyField; manyToManyField = new ManyToManyField(_sourceType, _fieldName, getCascade()); manyToManyField.setType(targetType); String sqlTable = _sourceType.getTable().getName() + "_" + targetType.getTable().getName(); JoinTableConfig joinTableConfig = getJoinTable(); AmberTable mapTable = null; ArrayList<ForeignColumn> sourceColumns = null; ArrayList<ForeignColumn> targetColumns = null; HashMap<String,JoinColumnConfig> joinColumnsConfig = null; HashMap<String,JoinColumnConfig> inverseJoinColumnsConfig = null; if (joinTableConfig != null) { String joinTableName = joinTableConfig.getName(); joinColumnsConfig = joinTableConfig.getJoinColumnMap(); inverseJoinColumnsConfig = joinTableConfig.getInverseJoinColumnMap(); if (joinColumnsConfig != null && joinColumnsConfig.size() > 0) manyToManyField.setJoinColumns(true); if (inverseJoinColumnsConfig != null && inverseJoinColumnsConfig.size() > 0) manyToManyField.setInverseJoinColumns(true); if (! joinTableName.equals("")) sqlTable = joinTableName; } mapTable = persistenceUnit.createTable(sqlTable); sourceColumns = calculateColumns(_field, _fieldName, mapTable, _sourceType.getTable().getName() + "_", _sourceType, joinColumnsConfig); targetColumns = calculateColumns(_field, _fieldName, mapTable, targetType.getTable().getName() + "_", targetType, inverseJoinColumnsConfig); manyToManyField.setAssociationTable(mapTable); manyToManyField.setTable(sqlTable); manyToManyField.setLazy(isFetchLazy()); manyToManyField.setSourceLink(new LinkColumns(mapTable, _sourceType.getTable(), sourceColumns)); manyToManyField.setTargetLink(new LinkColumns(mapTable, targetType.getTable(), targetColumns)); _sourceType.addField(manyToManyField); } private void addMapKey() { MapKeyConfig mapKeyConfig = _mapKey; String key = mapKeyConfig.getName(); String getter = "get" + Character.toUpperCase(key.charAt(0)) + key.substring(1); /* Method method = targetType.getGetter(getter); if (method == null) { throw error(_field, L.l("targetEntity '{0}' has no getter for field named '{1}'. Either the @MapKey name or the @ManyToMany targetEntity is incorrect.", targetName, key)); } // manyToManyField.setMapKey(key); */ } private void addDependentSide(EntityType targetType) { ManyToManyField sourceField = (ManyToManyField) targetType.getField(getMappedBy()); if (sourceField == null) throw error(_field, L.l("Unable to find the associated field in '{0}' for a @ManyToMany relationship from '{1}'", targetType.getName(), _fieldName)); ManyToManyField manyToManyField; manyToManyField = new ManyToManyField(_sourceType, _fieldName, sourceField, getCascade()); manyToManyField.setType(targetType); _sourceType.addField(manyToManyField); // jpa/0i5- // Update column names for bidirectional many-to-many if (! sourceField.hasJoinColumns()) { LinkColumns sourceLink = sourceField.getSourceLink(); ArrayList<ForeignColumn> columns = sourceLink.getColumns(); for (ForeignColumn column : columns) { String columnName = column.getName(); columnName = columnName.substring(columnName.indexOf('_')); columnName = toSqlName(manyToManyField.getName()) + columnName; column.setName(columnName); } } if (! sourceField.hasInverseJoinColumns()) { LinkColumns targetLink = sourceField.getTargetLink(); ArrayList<ForeignColumn> columns = targetLink.getColumns(); for (ForeignColumn column : columns) { String columnName = column.getName(); columnName = columnName.substring(columnName.indexOf('_')); columnName = toSqlName(sourceField.getName()) + columnName; column.setName(columnName); } } return; } }