All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.javaclub.jorm.jdbc.ClassMetadata Maven / Gradle / Ivy

There is a newer version: 1.1.3
Show newest version
/*
 * @(#)ClassMetadata.java	2011-7-20
 *
 * Copyright (c) 2011. All Rights Reserved.
 *
 */

package com.github.javaclub.jorm.jdbc;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import com.github.javaclub.jorm.JormException;
import com.github.javaclub.jorm.annotation.Basic;
import com.github.javaclub.jorm.annotation.Entity;
import com.github.javaclub.jorm.annotation.Id;
import com.github.javaclub.jorm.annotation.ManyToMany;
import com.github.javaclub.jorm.annotation.ManyToOne;
import com.github.javaclub.jorm.annotation.NoColumn;
import com.github.javaclub.jorm.annotation.OneToMany;
import com.github.javaclub.jorm.annotation.OneToOne;
import com.github.javaclub.jorm.annotation.PK;
import com.github.javaclub.jorm.annotation.constant.FetchType;
import com.github.javaclub.jorm.annotation.constant.GenerationType;
import com.github.javaclub.jorm.common.Annotations;
import com.github.javaclub.jorm.common.MethodType;
import com.github.javaclub.jorm.common.Reflections;
import com.github.javaclub.jorm.common.Strings;
import com.github.javaclub.jorm.jdbc.process.DummyFieldProcessor;
import com.github.javaclub.jorm.jdbc.process.FieldProcessor;
import com.github.javaclub.jorm.jdbc.sql.AnnotationModelHelper;
import com.github.javaclub.jorm.jdbc.sql.SqlParams;

/**
 * ClassMetadata
 *
 * @author Gerald Chen
 * @version $Id: ClassMetadata.java 2011-7-20 上午11:39:56 Exp $
 */
public class ClassMetadata {
	
	private static ConcurrentMap, ClassMetadata> metadatas = new ConcurrentHashMap, ClassMetadata>();

	public Class clazz;
	public String tableName;
	public boolean hasLazyField = false;
	public boolean isLazyEntity = false;
	
	public Field identifierField;
	public Method getIdentifierMethod;
	public Method setIdentifierMethod;
	
	public List definedPkFields = new ArrayList();
	public List definedIdFields = new ArrayList();
	public List generatedFields = new ArrayList();
	public List insertFields = new ArrayList();
	public List updateFields = new ArrayList();
	public List lazyFields = new ArrayList();
	public List allFields = new ArrayList();
	
	public List OneToOneFields = new ArrayList();
	public List OneToManyFields = new ArrayList();
	public List ManyToOneFields = new ArrayList();
	public List ManyToManyFields = new ArrayList();
	
	public String insert;
	
	private Map fieldColumns = new HashMap();
	
	private ClassMetadata(Class clazz) {
		//System.out.println("ClassMetadata constructor [" + clazz.getName() +  "] initialized ...");
		this.clazz = clazz;
		String pkFieldNamesJoin = processClassAnnotation(clazz);
		processFieldAnnotation(clazz, pkFieldNamesJoin);
	}

	public boolean isIdField(Field field) {
		return field.getAnnotation(Id.class) != null;
	}
	
	public boolean isEntityLazy() {
		return hasLazyField || isLazyEntity;
	}
	
	public boolean isJustFieldLazy() {
		return hasLazyField && (!isLazyEntity);
	}
	
	public boolean isIdentityfierAutoGenerated() {
		if(null == this.identifierField) {
			return false;
		}
		Id ann = this.identifierField.getAnnotation(Id.class);
		return (null != ann && GenerationType.isAutoGenerated(ann.value()));
	}
	
	public boolean isIdentityfierNeedAssigned() {
		if(null == this.identifierField) {
			return false;
		}
		Id ann = this.identifierField.getAnnotation(Id.class);
		return (null != ann && GenerationType.ASSIGNED.endsWith(ann.value()));
	}
	
	public boolean hasAssociated() {
		return (OneToOneFields.size() > 0 || OneToManyFields.size() > 0 
				|| ManyToManyFields.size() > 0 || ManyToOneFields.size() > 0);
	}
	
	/**
	 * Tests the identifier field's generation strategy is GenerationType.FOREIGN.
	 *
	 * @return true if the identifier field's generation strategy is GenerationType.FOREIGN, 
     * 			otherwise false.
	 */
	public final boolean isForeignStrategy() {
		Id idAnnotation = this.identifierField.getAnnotation(Id.class);
		return Strings.equals(idAnnotation.value(), GenerationType.FOREIGN);
	}
	
	public String identityStrategy() {
		Id id = identifierField.getAnnotation(Id.class);
		if(null != id) {
			return id.value();
		}
		return null;
	}

	
	/**
	 * Database column's name value
	 *
	 * @param fieldName a entity's property name
	 * @return column name
	 */ 
	public String column(String fieldName) {
		return fieldColumns.get(fieldName);
	}
	
	/**
	 * Find the matched field, class type of which is matched the the specified class type [fieldType] in a fields list.
	 *
	 * @param list fields list
	 * @param fieldType the specified class type
	 * @return
	 */
	public Field matchedField(List list, Class fieldType) {
		for (Field field : list) {
			if(fieldType == field.getType()) {
				return field;
			}
		}
		return null;
	}
	
	/**
	 * Tests if the specified field is lazy
	 *
	 * @param field the specified field
	 * @return true if the specified field is lazy, 
	 * 			otherwise false.
	 */
	public static boolean isLazyField(Field field) {
		if(field.getAnnotation(Basic.class) != null) {
			if(field.getAnnotation(Basic.class).fetch() == FetchType.LAZY) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Useful for those Entities that only support one annotation of Id is defined.
	 * 
	 * @param clazz entity class type
	 * @return the identifier field
	 */
	public static Field getIdField(Class clazz) {
		List keys = ClassMetadata.getClassMetadata(clazz).definedIdFields;
		if(keys.isEmpty())
			throw new JormException("No valid @Id defined in class " + clazz.getName());
		if(keys.size() > 1)
			throw new JormException("Multiple @Id defined in class " + clazz.getName());
		return keys.get(0);
	}
	
	/**
	 * Tests if the specified field has {@link FieldProcessor}
	 *
	 * @param field the specified field
	 * @return true if the specified field has {@link FieldProcessor}, 
	 * 			otherwise false.
	 */
	public static boolean hasProcessor(Field field) {
		if(field.getAnnotation(Basic.class) != null) {
			Class processor = field.getAnnotation(Basic.class).processor();
			if(null != processor && processor != DummyFieldProcessor.class) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Gets entity's ClassMetadata by entity class type
	 *
	 * @param clazz entity class type
	 * @return the entity's ClassMetadata
	 */
	public static ClassMetadata getClassMetadata(Class clazz) {
		ClassMetadata metadata = metadatas.get(clazz);
		if(null == metadata) {
			metadata = new ClassMetadata(clazz);
			metadatas.put(clazz, metadata);
		}
		return metadata;
	}
	
	/**
	 * Gets mapping table name for @ManyToMany
	 *
	 * @param obj   the target object
	 * @param mtmField a many-to-many relational field
	 * @return the many-to-many relation mapping table name
	 */
	public static String getMappingTablename(Object obj, Field mtmField) {
		String r = mtmField.getAnnotation(ManyToMany.class).table();
		if(Strings.isEmpty(r)) {
			String part1 = Strings.lowerCase(obj.getClass().getSimpleName());
			String part2 = Strings.lowerCase(mtmField.getAnnotation(ManyToMany.class).type().getSimpleName());
			String union = null;
			if(part1.compareTo(part2) < 0) {
				union = part1 + "_" + part2;
			} else {
				union = part2 + "_" + part1;
			}
			r = "t_mtm_r_" + union;
		}
		return r;
	}
	
	// ----------------- private method -----------------
	
	/**
	 * Process the class's annotation
	 *
	 * @param clazz domain class
	 * @return pk field names join string splited by ','
	 */
	private String processClassAnnotation(Class clazz) {
		tableName = AnnotationModelHelper.getTableName(clazz);
		Entity entity = Annotations.findAnnotation(clazz, Entity.class);
		if(null != entity && entity.lazy()) {
			isLazyEntity = true;
		}
		PK pk = Annotations.findAnnotation(clazz, PK.class);
		String pkFieldNamesJoin = "";
		if(pk != null) {
			String[] pkFieldNames = pk.value();
			for (int i = 0; i < pkFieldNames.length; i++) {
				if(i > 0) {
					pkFieldNamesJoin = pkFieldNamesJoin + ",";
				}
				pkFieldNamesJoin = pkFieldNamesJoin + pkFieldNames[i];
				definedPkFields.add(Reflections.getField(clazz, pkFieldNames[i]));
			}
		}
		return pkFieldNamesJoin;
	}
	
	/**
	 * Process the class field's annotation
	 *
	 * @param clazz domain class
	 * @param pkjoin pk field names join string splited by ','
	 */
	private void processFieldAnnotation(Class clazz, String pkjoin) {
		Class theClass = clazz;
		while (null != theClass && !(theClass == Object.class)) {
			Field[] fs = theClass.getDeclaredFields();
			for (int i = 0; i < fs.length; i++) {
				if (isIgnoredField(fs[i])) continue;
				Id idAnn = fs[i].getAnnotation(Id.class);
				if(idAnn != null) {
					if(GenerationType.isAutoGenerated(idAnn.value())) {
						generatedFields.add(fs[i]);
					} else {
						insertFields.add(fs[i]);
					}
					definedIdFields.add(fs[i]);
					processIdentifierField(fs[i]);
				} else {
					if(fs[i].getAnnotation(Basic.class) != null) {
						if(fs[i].getAnnotation(Basic.class).fetch() == FetchType.LAZY) {
							hasLazyField = true;
							lazyFields.add(fs[i]);
						}
					}
					if(Annotations.hasAnnotation(fs[i], OneToOne.class)) {
						OneToOneFields.add(fs[i]);
					} else if(Annotations.hasAnnotation(fs[i], OneToMany.class)) {
						OneToManyFields.add(fs[i]);
					} else if(Annotations.hasAnnotation(fs[i], ManyToMany.class)) {
						ManyToManyFields.add(fs[i]);
					} else if(Annotations.hasAnnotation(fs[i], ManyToOne.class)) {
						ManyToOneFields.add(fs[i]);
					} else {
						insertFields.add(fs[i]);
					}
					
					if (pkjoin.indexOf(fs[i].getName()) < 0
							|| pkjoin.indexOf(fs[i].getName()) > pkjoin.length()) {
						updateFields.add(fs[i]);
					}
				}
				allFields.add(fs[i]);
				fieldColumns.put(fs[i].getName(), AnnotationModelHelper.getColumName(fs[i]));
			}
			theClass = theClass.getSuperclass();
		}
	}
	
	/**
	 * Tests if the specified field is needed to be managed by hand and {@link SqlParams}
	 *
	 * @param field the specified Field
	 * @return true if the specified field is needed to be managed by hand and {@link SqlParams}, 
	 * 			otherwise false.
	 */
	public static boolean isInsertField(Field field) {
		if(field.getAnnotation(NoColumn.class) != null) {
			return false;
		}
		Id id = field.getAnnotation(Id.class);
		if(id != null && GenerationType.isAutoGenerated(id.value())) {
			return false;
		}
		if(Annotations.hasAnnotation(field, OneToOne.class) || 
		   Annotations.hasAnnotation(field, OneToMany.class) || 
		   Annotations.hasAnnotation(field, ManyToMany.class) || 
		   Annotations.hasAnnotation(field, ManyToOne.class)) {
			return false;
		}
		return true;
	}
	
	private void processIdentifierField(Field field) {
		this.identifierField = field;
		this.getIdentifierMethod = Reflections.getMethod(this.clazz, field, MethodType.GET);
		this.setIdentifierMethod = Reflections.getMethod(this.clazz, field, MethodType.SET);
	}

	private static boolean isIgnoredField(Field f) {
		if(null == f) return true;
		if(f.getAnnotation(NoColumn.class) != null)
			return true;
		if (Modifier.isStatic(f.getModifiers()))
			return true;
		if (Modifier.isFinal(f.getModifiers()))
			return true;
		if(Modifier.isTransient(f.getModifiers()))
			return true;
		if (f.getName().startsWith("this$"))
			return true;
		return false;
	}

	public static void main(String[] args) {
		
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy