Search in sources :

Example 1 with MappedSuperclass

use of org.apache.openejb.jee.jpa.MappedSuperclass in project tomee by apache.

the class CmpJpaConversion method processEntityBean.

/**
 * Generate the CMP mapping data for an individual
 * EntityBean.
 *
 * @param ejbModule      The module containing the bean.
 * @param entityMappings The accumulated set of entity mappings.
 * @param bean           The been we're generating the mapping for.
 */
private void processEntityBean(final EjbModule ejbModule, final EntityMappings entityMappings, final EntityBean bean) {
    // try to add a new persistence-context-ref for cmp
    if (!addPersistenceContextRef(bean)) {
        // which means it has a mapping, so skip this bean
        return;
    }
    // get the real bean class
    final Class ejbClass = loadClass(ejbModule.getClassLoader(), bean.getEjbClass());
    // and generate a name for the subclass that will be generated and handed to the JPA
    // engine as the managed class.
    final String jpaEntityClassName = CmpUtil.getCmpImplClassName(bean.getAbstractSchemaName(), ejbClass.getName());
    // We don't use this mapping directly, instead we pull entries from it
    // the reason being is that we intend to support mappings that aren't
    // exactly correct.  i.e. users should be able to write mappings completely
    // ignorant of the fact that we subclass.  The fact that we subclass means
    // these user supplied mappings might need to be adjusted as the jpa orm.xml
    // file is extremely subclass/supperclass aware and mappings specified in it
    // need to be spot on.
    final EntityMappings userMappings = getUserEntityMappings(ejbModule);
    // chain of the bean looking for any that have user defined mappings.
    for (Class clazz = ejbClass; clazz != null; clazz = clazz.getSuperclass()) {
        final MappedSuperclass mappedSuperclass = removeMappedSuperclass(userMappings, clazz.getName());
        // that the mapping is correct.  Copy it from their mappings to ours
        if (mappedSuperclass != null) {
            entityMappings.getMappedSuperclass().add(mappedSuperclass);
        }
    }
    // Look for an existing mapping using the openejb generated subclass name
    Entity entity = removeEntity(userMappings, jpaEntityClassName);
    // because we are going to ignore all other xml metadata.
    if (entity != null) {
        // XmlMetadataComplete is an OpenEJB specific flag that
        // tells all other legacy descriptor converters to keep
        // their hands off.
        entity.setXmlMetadataComplete(true);
        entityMappings.getEntity().add(entity);
        return;
    }
    if (entity == null) {
        entity = new Entity(jpaEntityClassName);
    }
    // have to check for null everywhere.
    if (entity.getAttributes() == null) {
        entity.setAttributes(new Attributes());
    }
    // add the entity
    entityMappings.getEntity().add(entity);
    // OVERWRITE: description: contains the name of the entity bean
    entity.setDescription(ejbModule.getModuleId() + "#" + bean.getEjbName());
    // PRESERVE has queries: name: the name of the entity in queries
    final String entityName = bean.getAbstractSchemaName();
    entity.setName(entityName);
    entity.setEjbName(bean.getEjbName());
    final ClassLoader classLoader = ejbModule.getClassLoader();
    final Collection<MappedSuperclass> mappedSuperclasses;
    if (bean.getCmpVersion() == CmpVersion.CMP2) {
        // perform the 2.x class mapping.  This really just identifies the primary key and
        // other cmp fields that will be generated for the concrete class and identify them
        // to JPA.
        mappedSuperclasses = mapClass2x(entity, bean, classLoader);
    } else {
        // map the cmp class, but if we are using a mapped super class,
        // generate attribute-override instead of id and basic
        mappedSuperclasses = mapClass1x(bean.getEjbClass(), entity, bean, classLoader);
    }
    // configuration. f
    if (mappedSuperclasses != null) {
        // that will get passed to the JPA engine.
        for (final MappedSuperclass mappedSuperclass : mappedSuperclasses) {
            entityMappings.getMappedSuperclass().add(mappedSuperclass);
        }
    }
    // process queries
    for (final Query query : bean.getQuery()) {
        final NamedQuery namedQuery = new NamedQuery();
        final QueryMethod queryMethod = query.getQueryMethod();
        // todo deployment id could change in one of the later conversions... use entity name instead, but we need to save it off
        final StringBuilder name = new StringBuilder();
        name.append(entityName).append(".").append(queryMethod.getMethodName());
        if (queryMethod.getMethodParams() != null && !queryMethod.getMethodParams().getMethodParam().isEmpty()) {
            name.append('(');
            boolean first = true;
            for (final String methodParam : queryMethod.getMethodParams().getMethodParam()) {
                if (!first) {
                    name.append(",");
                }
                name.append(methodParam);
                first = false;
            }
            name.append(')');
        }
        namedQuery.setName(name.toString());
        namedQuery.setQuery(query.getEjbQl());
        entity.getNamedQuery().add(namedQuery);
    }
    // todo: there should be a common interface between ejb query object and openejb query object
    final OpenejbJar openejbJar = ejbModule.getOpenejbJar();
    final EjbDeployment ejbDeployment = openejbJar.getDeploymentsByEjbName().get(bean.getEjbName());
    if (ejbDeployment != null) {
        for (final org.apache.openejb.jee.oejb3.Query query : ejbDeployment.getQuery()) {
            final NamedQuery namedQuery = new NamedQuery();
            final org.apache.openejb.jee.oejb3.QueryMethod queryMethod = query.getQueryMethod();
            // todo deployment id could change in one of the later conversions... use entity name instead, but we need to save it off
            final StringBuilder name = new StringBuilder();
            name.append(entityName).append(".").append(queryMethod.getMethodName());
            if (queryMethod.getMethodParams() != null && !queryMethod.getMethodParams().getMethodParam().isEmpty()) {
                name.append('(');
                boolean first = true;
                for (final String methodParam : queryMethod.getMethodParams().getMethodParam()) {
                    if (!first) {
                        name.append(",");
                    }
                    name.append(methodParam);
                    first = false;
                }
                name.append(')');
            }
            namedQuery.setName(name.toString());
            namedQuery.setQuery(query.getObjectQl());
            entity.getNamedQuery().add(namedQuery);
        }
    }
}
Also used : Entity(org.apache.openejb.jee.jpa.Entity) Query(org.apache.openejb.jee.Query) NamedQuery(org.apache.openejb.jee.jpa.NamedQuery) QueryMethod(org.apache.openejb.jee.QueryMethod) Attributes(org.apache.openejb.jee.jpa.Attributes) OpenejbJar(org.apache.openejb.jee.oejb3.OpenejbJar) MappedSuperclass(org.apache.openejb.jee.jpa.MappedSuperclass) EntityMappings(org.apache.openejb.jee.jpa.EntityMappings) IdClass(org.apache.openejb.jee.jpa.IdClass) EjbDeployment(org.apache.openejb.jee.oejb3.EjbDeployment) NamedQuery(org.apache.openejb.jee.jpa.NamedQuery)

Example 2 with MappedSuperclass

use of org.apache.openejb.jee.jpa.MappedSuperclass in project tomee by apache.

the class CmpJpaConversion method mapClass2x.

/**
 * Generate the JPA mapping for a CMP 2.x bean.  Since
 * the field accessors are all defined as abstract methods
 * and the fields will not be defined in the implementation
 * class, we don't need to deal with mapped superclasses.
 * All of the fields and concrete methods will be
 * implemented by the generated subclass, so from
 * a JPA standpoint, there are no mapped superclasses
 * required.
 *
 * @param mapping     The mapping information we're updating.
 * @param bean        The entity bean meta data
 * @param classLoader The classloader for resolving class references and
 *                    primary key classes.
 */
private Collection<MappedSuperclass> mapClass2x(final Mapping mapping, final EntityBean bean, final ClassLoader classLoader) {
    final Set<String> allFields = new TreeSet<>();
    // get an acculated set of the CMP fields.
    for (final CmpField cmpField : bean.getCmpField()) {
        allFields.add(cmpField.getFieldName());
    }
    Class<?> beanClass = null;
    try {
        beanClass = classLoader.loadClass(bean.getEjbClass());
    } catch (final ClassNotFoundException e) {
        // if it does fail, just return null from here
        return null;
    }
    // build a map from the field name to the super class that contains that field.
    // If this is a strictly CMP 2.x class, this is generally an empty map.  However,
    // we support some migration steps toward EJB3, so this can be defined completely
    // or partially as a POJO with concrete fields and accessors.  This allows us to
    // locate and generate the mappings
    final Map<String, MappedSuperclass> superclassByField = mapFields(beanClass, allFields);
    for (final Method method : beanClass.getMethods()) {
        if (!Modifier.isAbstract(method.getModifiers())) {
            continue;
        }
        if (method.getParameterTypes().length != 0) {
            continue;
        }
        if (method.getReturnType().equals(Void.TYPE)) {
            continue;
        }
        // Skip relationships: anything of type EJBLocalObject or Collection
        if (EJBLocalObject.class.isAssignableFrom(method.getReturnType())) {
            continue;
        }
        if (Collection.class.isAssignableFrom(method.getReturnType())) {
            continue;
        }
        if (Map.class.isAssignableFrom(method.getReturnType())) {
            continue;
        }
        String name = method.getName();
        if (name.startsWith("get")) {
            name = name.substring("get".length(), name.length());
        } else if (name.startsWith("is")) {
            // boolean.
            if (method.getReturnType() == Boolean.TYPE) {
                name = name.substring("is".length(), name.length());
            } else {
                // not an acceptable "is" method.
                continue;
            }
        } else {
            continue;
        }
        // the property needs the first character lowercased.  Generally,
        // we'll have this field already in our list, but it might have been
        // omitted from the meta data.
        name = Strings.lcfirst(name);
        if (!allFields.contains(name)) {
            allFields.add(name);
            bean.addCmpField(name);
        }
    }
    // 
    // id: the primary key
    // 
    final Set<String> primaryKeyFields = new HashSet<>();
    if (bean.getPrimkeyField() != null) {
        final String fieldName = bean.getPrimkeyField();
        final MappedSuperclass superclass = superclassByField.get(fieldName);
        // this need not be here...for CMP 2.x, these are generally autogenerated fields.
        if (superclass != null) {
            // ok, add this field to the superclass mapping
            superclass.addField(new Id(fieldName));
            // the direct mapping is an over ride
            mapping.addField(new AttributeOverride(fieldName));
        } else {
            // this is a normal generated field, it will be in the main class mapping.
            mapping.addField(new Id(fieldName));
        }
        primaryKeyFields.add(fieldName);
    } else if ("java.lang.Object".equals(bean.getPrimKeyClass())) {
        // the automatically generated keys use a special property name
        // and will always be in the generated superclass.
        final String fieldName = "OpenEJB_pk";
        final Id field = new Id(fieldName);
        field.setGeneratedValue(new GeneratedValue(GenerationType.AUTO));
        mapping.addField(field);
        primaryKeyFields.add(fieldName);
    } else if (bean.getPrimKeyClass() != null) {
        Class<?> pkClass = null;
        try {
            pkClass = classLoader.loadClass(bean.getPrimKeyClass());
            MappedSuperclass idclass = null;
            // to make sure everything maps correctly.
            for (final Field pkField : pkClass.getFields()) {
                final String pkFieldName = pkField.getName();
                final int modifiers = pkField.getModifiers();
                if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers) && allFields.contains(pkFieldName)) {
                    // see if the bean field is concretely defined in one of the superclasses
                    final MappedSuperclass superclass = superclassByField.get(pkFieldName);
                    if (superclass != null) {
                        // ok, we have an override that needs to be specified at the main class level.
                        superclass.addField(new Id(pkFieldName));
                        mapping.addField(new AttributeOverride(pkFieldName));
                        idclass = resolveIdClass(idclass, superclass, beanClass);
                    } else {
                        // this field will be autogenerated
                        mapping.addField(new Id(pkFieldName));
                    }
                    primaryKeyFields.add(pkFieldName);
                }
            }
            // if we've located an ID class, set it as such
            if (idclass != null) {
                idclass.setIdClass(new IdClass(bean.getPrimKeyClass()));
            } else {
                // do this for the toplevel mapping
                mapping.setIdClass(new IdClass(bean.getPrimKeyClass()));
            }
        } catch (final ClassNotFoundException e) {
            throw (IllegalStateException) new IllegalStateException("Could not find entity primary key class " + bean.getPrimKeyClass()).initCause(e);
        }
    }
    // 
    for (final CmpField cmpField : bean.getCmpField()) {
        // only add entries for cmp fields that are not part of the primary key
        if (!primaryKeyFields.contains(cmpField.getFieldName())) {
            final String fieldName = cmpField.getFieldName();
            // this will be here if we've already processed this
            final MappedSuperclass superclass = superclassByField.get(fieldName);
            // we need to provide a mapping for this.
            if (superclass != null) {
                // we need to mark this as being in one of the superclasses
                superclass.addField(new Basic(fieldName));
                mapping.addField(new AttributeOverride(fieldName));
            } else {
                // directly generated.
                mapping.addField(new Basic(fieldName));
            }
        }
    }
    // the field mappings
    return new HashSet<>(superclassByField.values());
}
Also used : Basic(org.apache.openejb.jee.jpa.Basic) Method(java.lang.reflect.Method) QueryMethod(org.apache.openejb.jee.QueryMethod) AttributeOverride(org.apache.openejb.jee.jpa.AttributeOverride) GeneratedValue(org.apache.openejb.jee.jpa.GeneratedValue) RelationField(org.apache.openejb.jee.jpa.RelationField) CmpField(org.apache.openejb.jee.CmpField) Field(java.lang.reflect.Field) IdClass(org.apache.openejb.jee.jpa.IdClass) CmpField(org.apache.openejb.jee.CmpField) TreeSet(java.util.TreeSet) MappedSuperclass(org.apache.openejb.jee.jpa.MappedSuperclass) Id(org.apache.openejb.jee.jpa.Id) HashSet(java.util.HashSet)

Example 3 with MappedSuperclass

use of org.apache.openejb.jee.jpa.MappedSuperclass in project tomee by apache.

the class CmpJpaConversion method mapFields.

/**
 * Build a mapping between a bean's CMP fields and the
 * particular subclass in the inheritance hierarchy that
 * defines the field.
 *
 * @param clazz            The bean implementation class.
 * @param persistantFields The set of container-managed fields.
 * @return A map of fieldname-to-defining class relationships.
 */
private Map<String, MappedSuperclass> mapFields(Class clazz, Set<String> persistantFields) {
    persistantFields = new TreeSet<>(persistantFields);
    final Map<String, MappedSuperclass> fields = new TreeMap<>();
    // or we've reached the Object class.
    while (!persistantFields.isEmpty() && !clazz.equals(Object.class)) {
        // This is a single target for the relationship mapping for each
        // class in the hierarchy.
        final MappedSuperclass superclass = new MappedSuperclass(clazz.getName());
        for (final Field field : clazz.getDeclaredFields()) {
            if (!field.isSynthetic()) {
                final String fieldName = field.getName();
                // if this is one of bean's persistence fields, create the mapping
                if (persistantFields.contains(fieldName)) {
                    fields.put(fieldName, superclass);
                    persistantFields.remove(fieldName);
                } else if (!ENHANCED_FIELDS.contains(fieldName)) {
                    // these are fields we need to identify as transient for the persistence engine.
                    final Transient transientField = new Transient(fieldName);
                    superclass.addField(transientField);
                }
            }
        }
        clazz = clazz.getSuperclass();
    }
    return fields;
}
Also used : RelationField(org.apache.openejb.jee.jpa.RelationField) CmpField(org.apache.openejb.jee.CmpField) Field(java.lang.reflect.Field) MappedSuperclass(org.apache.openejb.jee.jpa.MappedSuperclass) Transient(org.apache.openejb.jee.jpa.Transient) TreeMap(java.util.TreeMap)

Example 4 with MappedSuperclass

use of org.apache.openejb.jee.jpa.MappedSuperclass in project tomee by apache.

the class CmpJpaConversion method removeMappedSuperclass.

/**
 * Check the user-defined entity mappings for a superclass
 * mapping for a given named class. If the user mappings exist,
 * remove them from the user list and return them
 * for inclusion in our generated mappings.
 *
 * @param userMappings The current user mapping set.
 * @param className    The name of the class of interest.
 * @return Returns the superclass mapping for the named class, or
 * null if the class is not in the mapping set.
 */
private MappedSuperclass removeMappedSuperclass(final EntityMappings userMappings, final String className) {
    final MappedSuperclass mappedSuperclass;
    mappedSuperclass = userMappings.getMappedSuperclassMap().get(className);
    if (mappedSuperclass != null) {
        userMappings.getMappedSuperclassMap().remove(mappedSuperclass.getKey());
    }
    return mappedSuperclass;
}
Also used : MappedSuperclass(org.apache.openejb.jee.jpa.MappedSuperclass)

Example 5 with MappedSuperclass

use of org.apache.openejb.jee.jpa.MappedSuperclass in project tomee by apache.

the class CmpJpaConversion method deploy.

public AppModule deploy(final AppModule appModule) throws OpenEJBException {
    if (!hasCmpEntities(appModule)) {
        return appModule;
    }
    // create mappings if no mappings currently exist
    EntityMappings cmpMappings = appModule.getCmpMappings();
    if (cmpMappings == null) {
        cmpMappings = new EntityMappings();
        cmpMappings.setVersion("1.0");
        appModule.setCmpMappings(cmpMappings);
    }
    final Set<String> definedMappedClasses = new HashSet<>();
    // check for an existing "cmp" persistence unit, and look at existing mappings
    final PersistenceUnit cmpPersistenceUnit = findCmpPersistenceUnit(appModule);
    if (cmpPersistenceUnit != null) {
        if (cmpPersistenceUnit.getMappingFile() != null && cmpPersistenceUnit.getMappingFile().size() > 0) {
            for (final String mappingFile : cmpPersistenceUnit.getMappingFile()) {
                final EntityMappings entityMappings = readEntityMappings(mappingFile, appModule);
                if (entityMappings != null) {
                    definedMappedClasses.addAll(entityMappings.getEntityMap().keySet());
                }
            }
        }
    }
    // app mapping data
    for (final EjbModule ejbModule : appModule.getEjbModules()) {
        final EjbJar ejbJar = ejbModule.getEjbJar();
        // scan for CMP entity beans and merge the data into the collective set
        for (final EnterpriseBean enterpriseBean : ejbJar.getEnterpriseBeans()) {
            if (isCmpEntity(enterpriseBean)) {
                processEntityBean(ejbModule, definedMappedClasses, cmpMappings, (EntityBean) enterpriseBean);
            }
        }
        // if there are relationships defined in this jar, get a list of the defined
        // entities and process the relationship maps.
        final Relationships relationships = ejbJar.getRelationships();
        if (relationships != null) {
            final Map<String, Entity> entitiesByEjbName = new TreeMap<>();
            for (final Entity entity : cmpMappings.getEntity()) {
                entitiesByEjbName.put(entity.getEjbName(), entity);
            }
            for (final EjbRelation relation : relationships.getEjbRelation()) {
                processRelationship(entitiesByEjbName, relation);
            }
        }
        // Let's warn the user about any declarations we didn't end up using
        // so there can be no misunderstandings.
        final EntityMappings userMappings = getUserEntityMappings(ejbModule);
        for (final Entity mapping : userMappings.getEntity()) {
            LOGGER.warning("openejb-cmp-orm.xml mapping ignored: module=" + ejbModule.getModuleId() + ":  <entity class=\"" + mapping.getClazz() + "\">");
        }
        for (final MappedSuperclass mapping : userMappings.getMappedSuperclass()) {
            LOGGER.warning("openejb-cmp-orm.xml mapping ignored: module=" + ejbModule.getModuleId() + ":  <mapped-superclass class=\"" + mapping.getClazz() + "\">");
        }
    }
    if (!cmpMappings.getEntity().isEmpty()) {
        final PersistenceUnit persistenceUnit = getCmpPersistenceUnit(appModule);
        final boolean generatedOrmXmlProvided = appModule.getClassLoader().getResource(GENERATED_ORM_XML) != null;
        if (!persistenceUnit.getMappingFile().contains(GENERATED_ORM_XML)) {
            // explicit check for openejb-cmp-generated-orm, as this is generated and added to <mapping-file>
            if (generatedOrmXmlProvided) {
                LOGGER.warning("App module " + appModule.getModuleId() + " provides " + GENERATED_ORM_XML + ", but does not " + "specify it using <mapping-file> in persistence.xml for the CMP persistence unit, and it may conflict " + "with the generated mapping file. Consider renaming the file and explicitly referencing it in persistence.xml");
            }
            persistenceUnit.getMappingFile().add(GENERATED_ORM_XML);
        } else {
            if (generatedOrmXmlProvided) {
                LOGGER.warning("App module " + appModule.getModuleId() + " provides " + GENERATED_ORM_XML + " and additionally " + cmpMappings.getEntity().size() + "mappings have been generated. Consider renaming the " + GENERATED_ORM_XML + " in " + "your deployment archive to avoid any conflicts.");
            }
        }
        for (final Entity entity : cmpMappings.getEntity()) {
            if (!persistenceUnit.getClazz().contains(entity.getClazz())) {
                persistenceUnit.getClazz().add(entity.getClazz());
            }
        }
    }
    // causes some of the unit tests to fail.  Not sure why.  Should be fixed.
    for (final Entity entity : appModule.getCmpMappings().getEntity()) {
        if (entity.getAttributes() != null && entity.getAttributes().isEmpty()) {
            entity.setAttributes(null);
        }
    }
    return appModule;
}
Also used : Entity(org.apache.openejb.jee.jpa.Entity) EnterpriseBean(org.apache.openejb.jee.EnterpriseBean) TreeMap(java.util.TreeMap) Relationships(org.apache.openejb.jee.Relationships) PersistenceUnit(org.apache.openejb.jee.jpa.unit.PersistenceUnit) EjbRelation(org.apache.openejb.jee.EjbRelation) MappedSuperclass(org.apache.openejb.jee.jpa.MappedSuperclass) EntityMappings(org.apache.openejb.jee.jpa.EntityMappings) HashSet(java.util.HashSet) EjbJar(org.apache.openejb.jee.EjbJar)

Aggregations

MappedSuperclass (org.apache.openejb.jee.jpa.MappedSuperclass)7 IdClass (org.apache.openejb.jee.jpa.IdClass)4 Field (java.lang.reflect.Field)3 HashSet (java.util.HashSet)3 CmpField (org.apache.openejb.jee.CmpField)3 QueryMethod (org.apache.openejb.jee.QueryMethod)3 Entity (org.apache.openejb.jee.jpa.Entity)3 EntityMappings (org.apache.openejb.jee.jpa.EntityMappings)3 RelationField (org.apache.openejb.jee.jpa.RelationField)3 TreeMap (java.util.TreeMap)2 TreeSet (java.util.TreeSet)2 Query (org.apache.openejb.jee.Query)2 AttributeOverride (org.apache.openejb.jee.jpa.AttributeOverride)2 Attributes (org.apache.openejb.jee.jpa.Attributes)2 Basic (org.apache.openejb.jee.jpa.Basic)2 GeneratedValue (org.apache.openejb.jee.jpa.GeneratedValue)2 Id (org.apache.openejb.jee.jpa.Id)2 NamedQuery (org.apache.openejb.jee.jpa.NamedQuery)2 EjbDeployment (org.apache.openejb.jee.oejb3.EjbDeployment)2 OpenejbJar (org.apache.openejb.jee.oejb3.OpenejbJar)2