Search in sources :

Example 1 with IdClass

use of org.apache.openejb.jee.jpa.IdClass 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 2 with IdClass

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

the class CmpJpaConversion method mapClass1x.

/**
 * Create the class mapping for a CMP 1.x entity bean.
 * Since the fields for 1.x persistence are defined
 * in the objects directly, we need to create superclass
 * mappings for each of the defined fields to identify
 * which classes implement each of the managed fields.
 *
 * @param ejbClassName The name of the class we're processing.
 * @param mapping      The mappings we're going to generate.
 * @param bean         The bean metadata for the ejb.
 * @param classLoader  The classloader used to load the bean class for
 *                     inspection.
 * @return The set of mapped superclasses used in this
 * bean mapping.
 */
private Collection<MappedSuperclass> mapClass1x(final String ejbClassName, final Mapping mapping, final EntityBean bean, final ClassLoader classLoader) {
    final Class ejbClass = loadClass(classLoader, ejbClassName);
    // build a set of all field names
    final Set<String> allFields = new TreeSet<>();
    for (final CmpField cmpField : bean.getCmpField()) {
        allFields.add(cmpField.getFieldName());
    }
    // build a map from the field name to the super class that contains that field
    final Map<String, MappedSuperclass> superclassByField = mapFields(ejbClass, allFields);
    // 
    // 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);
        if (superclass == null) {
            throw new IllegalStateException("Primary key field " + fieldName + " is not defined in class " + ejbClassName + " or any super classes");
        }
        superclass.addField(new Id(fieldName));
        mapping.addField(new AttributeOverride(fieldName));
        primaryKeyFields.add(fieldName);
    } else if ("java.lang.Object".equals(bean.getPrimKeyClass())) {
        // a primary field type of Object is an automatically generated
        // pk field.  Mark it as such and add it to the mapping.
        final String fieldName = "OpenEJB_pk";
        final Id field = new Id(fieldName);
        field.setGeneratedValue(new GeneratedValue(GenerationType.AUTO));
        mapping.addField(field);
    } else if (bean.getPrimKeyClass() != null) {
        // we have a primary key class.  We need to define the mappings between the key class fields
        // and the bean's managed fields.
        Class<?> pkClass = null;
        try {
            pkClass = classLoader.loadClass(bean.getPrimKeyClass());
            MappedSuperclass superclass = null;
            MappedSuperclass idclass = null;
            for (final Field pkField : pkClass.getFields()) {
                final String fieldName = pkField.getName();
                final int modifiers = pkField.getModifiers();
                // AND must also exist in the class hierarchy (not enforced by mapFields());
                if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers) && allFields.contains(fieldName)) {
                    superclass = superclassByField.get(fieldName);
                    if (superclass == null) {
                        throw new IllegalStateException("Primary key field " + fieldName + " is not defined in class " + ejbClassName + " or any super classes");
                    }
                    // these are defined ast ID fields because they are part of the primary key
                    superclass.addField(new Id(fieldName));
                    mapping.addField(new AttributeOverride(fieldName));
                    primaryKeyFields.add(fieldName);
                    idclass = resolveIdClass(idclass, superclass, ejbClass);
                }
            }
            // if we've located an ID class, set it as such
            if (idclass != null) {
                idclass.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()) {
        final String fieldName = cmpField.getFieldName();
        // all of the primary key fields have been processed, so only handle whatever is left over
        if (!primaryKeyFields.contains(fieldName)) {
            final MappedSuperclass superclass = superclassByField.get(fieldName);
            if (superclass == null) {
                throw new IllegalStateException("CMP field " + fieldName + " is not defined in class " + ejbClassName + " or any super classes");
            }
            superclass.addField(new Basic(fieldName));
            mapping.addField(new AttributeOverride(fieldName));
        }
    }
    // the field mappings
    return new HashSet<>(superclassByField.values());
}
Also used : Basic(org.apache.openejb.jee.jpa.Basic) 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) IdClass(org.apache.openejb.jee.jpa.IdClass) Id(org.apache.openejb.jee.jpa.Id) HashSet(java.util.HashSet)

Example 3 with IdClass

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

the class CmpJpaConversion method resolveIdClass.

/**
 * Handle the potential situation where the fields
 * of a complex primary key are defined at different
 * levels of the class hierarchy.  We want to define
 * the idClass as the most derived class (i.e., the one
 * that will contain ALL of the defined fields).
 *
 * In practice, most ejbs will define all of the
 * primary key fields at the same subclass level, so
 * this should return quickly.
 *
 * @param idclass  The currently defined id class (will be null if
 *                 this is the first call).
 * @param current  The current superclass being processed.
 * @param ejbClass The ejbClass we're creating the mapping for.
 * @return Either idClass or current, depending on which is
 * the most derived of the classes.
 */
private MappedSuperclass resolveIdClass(final MappedSuperclass idclass, final MappedSuperclass current, final Class ejbClass) {
    // None identified yet?  Just use the one we just found
    if (idclass == null) {
        return current;
    }
    final String idClassName = idclass.getClazz();
    final String currentClassName = current.getClazz();
    // defined at the same level (common).  Just keep the same id class
    if (idClassName.equals(currentClassName)) {
        return idclass;
    }
    // the most derived
    for (Class clazz = ejbClass; clazz != null; clazz = clazz.getSuperclass()) {
        final String name = clazz.getName();
        // if we find the current one first, return it
        if (name.equals(currentClassName)) {
            return current;
        } else if (name.equals(idClassName)) {
            // keeping the same highest level
            return idclass;
        }
    }
    // this should never happen, but keep the same one if we ever reach here
    return idclass;
}
Also used : IdClass(org.apache.openejb.jee.jpa.IdClass)

Aggregations

IdClass (org.apache.openejb.jee.jpa.IdClass)3 Field (java.lang.reflect.Field)2 HashSet (java.util.HashSet)2 TreeSet (java.util.TreeSet)2 CmpField (org.apache.openejb.jee.CmpField)2 AttributeOverride (org.apache.openejb.jee.jpa.AttributeOverride)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 MappedSuperclass (org.apache.openejb.jee.jpa.MappedSuperclass)2 RelationField (org.apache.openejb.jee.jpa.RelationField)2 Method (java.lang.reflect.Method)1 QueryMethod (org.apache.openejb.jee.QueryMethod)1