public class AutoBeanConfig extends BeanConfig {

	public AutoBeanConfig(Class<? extends Object> klass, String tableName) {
		super(klass, tableName);

		// now use reflection to configure this guy...

		Map<String, Class<? extends Object>> props = FindProperties.all(klass);

		for (Map.Entry<String, Class<? extends Object>> entry : props.entrySet()) {
			
			DBType<?> columnType = DBTypes.from(entry.getValue());
			if (columnType != null) {
				this.field(entry.getKey(), columnType);
			}
		}
	}
} public class BeanConfig {

	private final Map<String, DBField> fieldList = new LinkedHashMap<String, DBField>();

	private final Map<String, DBField> pkList = new LinkedHashMap<String, DBField>();
	
	private final Map<String, Class<? extends Object>> abstractInstances = new LinkedHashMap<String, Class<? extends Object>>();
	
	private final Class<? extends Object> beanClass;

	private final String tableName;

	private DBField sequence = null;
	
	private String sequenceName = null;

	private DBField autoincrement = null;
	
	private TriggerDispatcher dispatcher = new TriggerDispatcher();

	/**
	 * Creates a configuration for a bean represented by the given class.
	 * 
	 * @param beanClass
	 *            The bean klass
	 * @param tableName
	 *            The database table where the bean properties will be stored.
	 */
	public BeanConfig(final Class<? extends Object> beanClass, final String tableName) {

		this.beanClass = beanClass;

		this.tableName = tableName;
	}

	/**
	 * Return the table name where the bean properties are stored.
	 * 
	 * @return The database table name.
	 */
	public String getTableName() {

		return tableName;
	}

	/**
	 * Return the bean class.
	 * 
	 * @return The bean class.
	 */
	public Class<? extends Object> getBeanClass() {

		return beanClass;
	}

	/**
	 * Add the sequence name *in the database* that will be used for this field.
	 * 
	 * NOTE: A field of type SEQUENCE must have been defined before or an IllegalStateException is thrown.
	 * 
	 * @param seqNameInDb the name of the sequence in the database
	 * @return this bean config
	 */
	public BeanConfig addSequenceName(String seqNameInDb) {
		
		if (sequence == null) {
			throw new IllegalStateException("There is no sequence field defined!");
		}
		
		sequenceName = seqNameInDb;
		return this;
	}
	
	/**
	 * Alias for method addSequence
	 * 
	 * @param seqNameInDb
	 * @return this bean config
	 */
	public BeanConfig seq(String seqNameInDb) {
		return addSequenceName(seqNameInDb);
	}

	/**
	 * Returns the name of the sequence in the database.
	 * 
	 * NOTE: The name returned is the name of the sequence in the database.
	 * 
	 * @return the name of the sequence in the database
	 */
	public String getSequenceName() {
		return sequenceName;
	}
	
	public BeanConfig remove(final String name) {
		
		if (name == null) {
			return remove((Object) null);
		}
		
		fieldList.remove(name);
		pkList.remove(name);
		return this;
	}
	
	public BeanConfig remove(Object propValueFromGetter) {
		return remove(PropertiesProxy.getPropertyName());
	}
	
	private BeanConfig addField(final String name, final String dbName, final DBType<? extends Object> type, final boolean isPK) {

		if (!isPK) {

			if (type instanceof SequenceType) {
				throw new IllegalStateException("A sequence type can only be a primary key!");
			}

			if (type instanceof AutoIncrementType) {
				throw new IllegalStateException("A auto-increment type can only be a primary key!");
			}
		}

		final DBField f = new DBField(name, dbName, type, isPK);

		fieldList.remove(name); // just in case we are re-adding it...

		fieldList.put(name, f);
		
		if (isPK) {

			pkList.remove(name); // just in case we are re-adding it...

			pkList.put(name, f);

			if (type instanceof SequenceType) {

				if (sequence != null) {
					throw new IllegalStateException("A bean can have only one sequence field!");
				}

				sequence = f;

			} else if (type instanceof AutoIncrementType) {

				if (autoincrement != null) {
					throw new IllegalStateException("A bean can have only one auto-increment field!");
				}

				autoincrement = f;
			}
		}

		return this;
	}
	
	public DBField getField(String name) {
		
		return fieldList.get(name);
	}

	/**
	 * Return an auto-increment field, if one was configured for this bean.
	 * 
	 * Note: A bean can have only one auto-increment field configured for this bean. Attempting to configure more than one will throw an exception.
	 * 
	 * @return the auto-increment field configured for this bean or null if it was not defined
	 */
	public DBField getAutoIncrementField() {

		return autoincrement;
	}

	/**
	 * Return a sequence field, if one was configured for this bean.
	 * 
	 * Note: A bean can have only one sequence field configured for this bean. Attempting to configure more than one will throw an exception.
	 * 
	 * @return the sequence field configured for this bean or null if it was not defined
	 */
	public DBField getSequenceField() {

		return sequence;
	}

	/**
	 * Add a database field for the given property with the given database type. It assumes that the property name is the SAME as the database column name. If they are different, use the other addField method.
	 * 
	 * @param name
	 *            The bean property name (same as the database column name)
	 * @param type
	 *            The database type
	 * @return This BeanConfig (Fluent API)
	 */
	public BeanConfig field(final String name, final DBType<? extends Object> type) {

		if (name == null) {
			return field((Object) null, type);
		}
		
		return addField(name, name.replace(".", "_"), type, false);
	}
	
	public BeanConfig field(Object propValueFromGetter, final DBType<? extends Object> type) {
		
		return field(PropertiesProxy.getPropertyName(), type);
	}

	/**
	 * Add a database field for the given property with the given database type.
	 * 
	 * @param name
	 *            The bean property name
	 * @param dbName
	 *            The name of the database column holding this property
	 * @param type
	 *            The database type
	 * @return This BeanConfig (Fluent API)
	 */
	public BeanConfig field(final String name, final String dbName, final DBType<? extends Object> type) {
		
		if (name == null) {
			return field((Object) null, dbName, type);
		}

		return addField(name, dbName, type, false);
	}
	
	public BeanConfig field(Object propValueFromGetter, String dbName, final DBType<? extends Object> type) {
		
		return field(PropertiesProxy.getPropertyName(), dbName, type);
	}

	/**
	 * Add a bean property that is the primary key in the database. The column name is the same as the property bean name. If they are different use the other pk method. All beans must have a primary key and you can call this method multiple times to support composite primary keys.
	 * 
	 * @param name
	 *            The bean property name
	 * @param type
	 *            The database type
	 * @return This BeanConfig (Fluent API)
	 */
	public BeanConfig pk(final String name, final DBType<? extends Object> type) {
		
		if (name == null) {
			return pk((Object) null, type);
		}

		return addField(name, name.replace(".", "_"), type, true);
	}
	
	public BeanConfig pk(Object propValueFromGetter, final DBType<? extends Object> type) {
		
		return pk(PropertiesProxy.getPropertyName(), type);
	}

	/**
	 * Add a property that is the primary key in the database. All beans must have a primary key and you can call this method multiple times to support composite primary keys.
	 * 
	 * @param name
	 *            The bean property name
	 * @param dbName
	 *            The name of the database column holding this property
	 * @param type
	 *            The database type
	 * @return This BeanConfig (Fluent API)
	 */
	public BeanConfig pk(final String name, final String dbName, final DBType<? extends Object> type) {
		
		if (name == null) {
			return pk((Object) null, dbName, type);
		}

		return addField(name, dbName, type, true);
	}
	
	public BeanConfig pk(Object propValueFromGetter, String dbName, final DBType<? extends Object> type) {
		
		return pk(PropertiesProxy.getPropertyName(), dbName, type);
	}

	/**
	 * Return the number of fields configured for this bean. It includes the PK.
	 * 
	 * @return The number of fields configured for this bean.
	 */
	public int getNumberOfFields() {

		return fieldList.size();
	}

	public int getNumberOfPKs() {

		return pkList.size();
	}

	@Override
	public String toString() {

		final StringBuilder sb = new StringBuilder(64);

		sb.append("BeanConfig: ").append(beanClass.getSimpleName()).append(" tableName=").append(tableName);

		return sb.toString();
	}

	/**
	 * Return all DBFields configured for this bean. It includes the PK as well.
	 * 
	 * @return An Iterator with all DBFields configured for this bean.
	 */
	public Iterator<DBField> fields() {

		return fieldList.values().iterator();
	}

	/**
	 * Check whether the primary key was defined.
	 * 
	 * @return true if a primary key was defined.
	 */
	public boolean hasPK() {

		return !pkList.isEmpty();
	}

	/**
	 * Return an iterator with the DBFields for the PK configured for this bean.
	 * 
	 * Note: A bean can have more than one property as its primary key, in that case it has a composite primary key.
	 * 
	 * @return An iterator with the DBFields for the PK.
	 */
	public Iterator<DBField> pks() {

		return pkList.values().iterator();
	}
	
	public BeanConfig trigger(TriggerListener trigger) {
		
		dispatcher.addTrigger(trigger);
		
		return this;
	}
	
	public BeanConfig remove(TriggerListener trigger) {
		
		dispatcher.removeTrigger(trigger);
		
		return this;
	}
	
	public TriggerDispatcher getDispatcher() {
		return dispatcher;
	}
	
	/**
	 * Configures a class that should be used instead of property type to create instances
	 * through {@link Class#newInstance()} method. It's useful when working with abstract objects
	 * @param abstractProperty - The property name
	 * @param clazz - The concrete class
	 * @return this
	 */
	public BeanConfig abstractInstance(String abstractProperty, Class<? extends Object> clazz) {
		
		if (abstractProperty == null) {
			return abstractInstance((Object) abstractProperty, clazz);
		}
		
		abstractInstances.put(abstractProperty, clazz);
		
		return this;
		
	}	
	
	/**
	 * Configures a class that should be used instead of property type to create instances
	 * through {@link Class#newInstance()} method. It's useful when working with abstract objects
	 * @param abstractProperty - The property name (through proxy)
	 * @param clazz - The concrete class
	 * @return this
	 */
	public <E> BeanConfig abstractInstance(E abstractProperty, Class<? extends E> clazz) {
		
		return abstractInstance(PropertiesProxy.getPropertyName(), clazz);
		
	}
	
	public Class<? extends Object> getAbstractProperty(String key) {
		
		return abstractInstances.get(key);
	}
	
}
 public class BeanException extends RuntimeException {

	private static final long serialVersionUID = -6402197033079197979L;
	
	protected final Throwable rootCause;

	public BeanException() {
		super();
		this.rootCause = null;
	}

	public BeanException(Throwable e) {
		super(getMsg(e), e);
		Throwable root = getRootCause(e);
		this.setStackTrace(root.getStackTrace());

		this.rootCause = root == this ? null : root;
	}

	public BeanException(String msg) {
		super(msg);

		this.rootCause = null;
	}

	public BeanException(String msg, Throwable e) {

		super(msg, e);
		Throwable root = getRootCause(e);
		this.setStackTrace(root.getStackTrace());

		this.rootCause = root == this ? null : root;
	}

	private static String getMsg(Throwable t) {

		Throwable root = getRootCause(t);

		String msg = root.getMessage();

		if (msg == null || msg.length() == 0) {

			msg = t.getMessage();

			if (msg == null || msg.length() == 0) {
				return root.getClass().getName();
			}
		}

		return msg;
	}

	private static Throwable getRootCause(Throwable t) {

		Throwable root = t.getCause();

		if (root == null) {
			return t;
		}

		while (root.getCause() != null) {

			root = root.getCause();
		}

		return root;

	}

	@Override
	public Throwable getCause() {

		return rootCause;
	}
}
 public class BeanManager {

	private final Map<Class<? extends Object>, BeanConfig> beans = new HashMap<Class<? extends Object>, BeanConfig>();
	
	public BeanManager(PropertiesProxy proxy) {
		
		PropertiesProxy.INSTANCE = proxy == null ? new DefaultProxy() : proxy;			
	}
	
	public BeanManager() {
		this(null);
	}

	/**
	 * Add a bean configuration.
	 * 
	 * @param bc
	 *            The bean configuration to add.
	 * @return The BeanConfig added (Fluent API)
	 */
	public BeanConfig bean(final BeanConfig bc) {

		if (beans.containsKey(bc.getBeanClass())) {
			throw new IllegalStateException("A configuration was already added for this bean ("+bc.getBeanClass()+")");
		}

		beans.put(bc.getBeanClass(), bc);

		return bc;
	}

	/**
	 * Add a bean configuration.
	 * 
	 * @param bc
	 *            The bean configuration to add.
	 */
	public void addBeanConfig(final BeanConfig bc) {
		bean(bc);
	}
	
	/**
	 * Add multiple bean configurations.
	 * 
	 * @param bcs
	 *            Varargs of bean configuration to add.
	 */
	public void addBeanConfigs(final BeanConfig... bcs) {
		for (BeanConfig bc : bcs) {
			bean(bc);
		}
	}

	/**
	 * Creates a bean configuration and add to this manager.
	 * 
	 * @param beanClass
	 *            The bean class
	 * @param tableName
	 *            The table name where the bean properties will be stored.
	 * @return The BeanConfig created (Fluent API)
	 */
	public BeanConfig bean(final Class<? extends Object> beanClass, final String tableName) {

		return bean(new BeanConfig(beanClass, tableName));
	}

	/**
	 * Get the bean configuration for the given bean class.
	 * 
	 * @param beanClass
	 *            The bean class
	 * @return The bean configuration for this bean or null if it was not defined
	 */
	public BeanConfig getBeanConfig(final Class<? extends Object> beanClass) {

		return beans.get(beanClass);
	}

	public Set<BeanConfig> getBeanConfigs() {
		Set<BeanConfig> all = new HashSet<BeanConfig>();
		all.addAll(beans.values());
		return all;
	}
} public interface BeanSession {

	public static final int UPDATE = 0, INSERT = 1;
	
	/**
	 * Get the database connection being used by this bean session.
	 * 
	 * @return the database connection
	 */
	public Connection getConnection();

	/**
	 * Load the bean from the database, injecting all its properties through reflection. Note that the bean passed MUST have its primary key set otherwise there is no way we can load it from the database.
	 * 
	 * @param bean
	 *            The bean we want to load from the DB.
	 * @return true if the bean was found in the database, false otherwise
	 */
	public boolean load(Object bean);
	
	public boolean load(Object bean, Object... properties);
	
	public boolean loadMinus(Object bean, Object... minus);

	/**
	 * Update the bean in the database. Only the bean fields that have been modified (dirty) will be updated.
	 * 
	 * It will return 1 if an update did happen or 0 if the bean could not be found in the database or if there was nothing modified in bean.
	 * 
	 * The bean MUST have its primary key set, otherwise it is impossible to update the bean in the database, and an exception will be thrown.
	 * 
	 * @param bean
	 *            The bean to be updated
	 * @param forceNull
	 * 			  Database columns that will be forced to null if the bean property is not set
	 * @return 1 if update was successful, 0 if the update did not happen or was not necessary
	 */
	public boolean update(Object bean, Object... forceNull);

	/**
	 * Same as update(bean) but here you can turn off the default dynamic update behavior and force all bean properties to be updated regardless whether they have been modified or not.
	 * 
	 * @param bean
	 * @return the number of rows that were updated
	 * @throws Exception
	 */
	public boolean updateAll(Object bean);
	
	/**
	 * Updates an object using only the differences between newBean and oldBean instances. 
	 * This method is useful in distributed environments like RMI or web services because these approaches should serialize objects to send/receive informations from client to server and vice versa 
	 * @param newBean
	 * @param oldBean
	 * @return 1 if update was successful, 0 if the update did not happen or was not necessary (when bean instances have no differences)
	 */
	public <E> boolean updateDiff(E newBean, E oldBean);

	/**
	 * Insert the bean in the database.
	 * 
	 * Depending on the type of PK, the generation of the PK can and should be taken care by the DB itself. The generated PK should be inserted in the bean by reflection, before the method returns.
	 * 
	 * The default, database-independent implementation of this method, insert all fields in the database not worrying about PK generation strategies.
	 * 
	 * @param bean
	 *            The bean to insert
	 */
	public void insert(Object bean);
	
	/**
	 * Tries to update or insert a bean using the <code>update</code> method.
	 * 
	 * @param bean The bean to update or insert
	 * @param forceNull Database columns that will be forced to null (or zero) if the bean property is not set
	 * @return	A value <b>0 (zero)</b> if operation executed an <code>update</code> and <b>1 (one)</b> if <code>insert</code> method was executed
	 * @see #saveAll(Object)
	 * @see #update(Object, Object...)
	 */
	public int save(Object bean, Object... forceNull);
	
	/**
	 * Tries to update or insert a bean object into database. The update uses the <code>updateAll</code> method.
	 * 
	 * @param bean The bean to update or insert
	 * @return	A value <b>0 (zero)</b> if operation executed an <code>update</code> and <b>1 (one)</b> if <code>insert</code> method was executed
	 * @see #updateAll(Object)
	 */
	public int saveAll(Object bean);

	/**
	 * Delete the bean from the database.
	 * 
	 * The PK of the bean MUST be set. The bean can only be deleted by its PK.
	 * 
	 * @param bean
	 * @return true if it was deleted or false if it did not exist
	 * @throws Exception
	 */
	public boolean delete(Object bean);
	
	/**
	 * Delete all data based on the properties present in the bean passed
	 * 
	 * The PK doesn't need to be set
	 * 
	 * @param bean
	 * @return The total of tuples removed from database
	 */
	public int deleteAll(final Object bean);

	/**
	 * Count the number of beans that would be returned by a loadList method.
	 * 
	 * @param bean
	 *            The bean holding the properties used by the list query.
	 * @return the number of beans found in the database
	 */
	public int countList(Object bean);

	/**
	 * Load a list of beans based on the properties present in the bean passed. For example, if you want to load all users with lastName equal to 'saoj' you would instantiate a bean and set its lastName property to 'saoj' before passing it as an argument to this method.
	 * 
	 * @param <E>
	 * @param bean
	 *            The bean holding the properties used by the list query.
	 * @return A list of beans the match the properties in the given bean. Or an empty list if nothing was found.
	 */
	public <E> List<E> loadList(E bean);

	public <E> List<E> loadList(E bean, Object... properties);

	public <E> List<E> loadListMinus(E bean, Object... minus);

	/**
	 * Same as loadList(bean) except that you can order the list returned by passing an SQL orderBy clause.
	 * 
	 * @param <E>
	 * @param bean
	 *            The bean holding the properties used by the list query.
	 * @param orderBy
	 *            The orderBy SQL clause.
	 * @return A list of beans the match the properties in the given bean. Or an empty list if nothing was found.
	 */
	public <E> List<E> loadList(E bean, OrderBy orderBy);

	public <E> List<E> loadList(E bean, OrderBy orderBy, Object... properties);

	public <E> List<E> loadListMinus(E bean, OrderBy orderBy, Object... minus);

	/**
	 * Same as loadList(bean) except that you can limit the number of beans returned in the list.
	 * 
	 * @param <E>
	 * @param bean
	 *            The bean holding the properties used by the list query.
	 * @param limit
	 *            The max number of beans returned in the list.
	 * @return A list of beans the match the properties in the given bean. Or an empty list if nothing was found.
	 */
	public <E> List<E> loadList(E bean, Limit limit);

	public <E> List<E> loadList(E bean, Limit limit, Object... properties);

	public <E> List<E> loadListMinus(E bean, Limit limit, Object... minus);

	/**
	 * Same as loadList(bean) except that you can limit the number of beans returned in the list as well as sort them by passing a orderBy SQL clause.
	 * 
	 * @param <E>
	 * @param bean
	 *            The bean holding the properties used by the list query.
	 * @param orderBy
	 *            The orderBy SQL clause.
	 * @param limit
	 *            The max number of beans returned in the list.
	 * @return A list of beans the match the properties in the given bean. Or an empty list if nothing was found.
	 */
	public <E> List<E> loadList(E bean, OrderBy orderBy, Limit limit);

	public <E> List<E> loadList(E bean, OrderBy orderBy, Limit limit, Object... properties);

	public <E> List<E> loadListMinus(E bean, OrderBy orderBy, Limit limit, Object... minus);

	/**
	 * Same as loadList(bean) but it attempts to load a single bean only. If more than one bean is found it throws an exception.
	 * 
	 * NOTE: The returned bean will be attached by the session so only the modified properties will be updated in case update() is called.
	 * 
	 * @param <E>
	 * @param bean
	 *            The bean holding the properties used by the load query.
	 * @return A unique bean that match the properties in the given bean. Or null if nothing was found.
	 * @throws BeanException
	 *             if more than one bean is found by the query.
	 */
	public <E> E loadUnique(E bean);
	
	public <E> E loadUnique(E bean, Object... properties);
	
	public <E> E loadUniqueMinus(E bean, Object... minus);

	public String buildSelect(final Class<? extends Object> beanClass);

	public String buildSelect(final Class<? extends Object> beanClass, Object... properties);

	public String buildSelectMinus(final Class<? extends Object> beanClass, Object... minus);

	public String buildSelect(final Class<? extends Object> beanClass, String tableName);

	public String buildSelect(final Class<? extends Object> beanClass, String tableName, Object... properties);

	public String buildSelectMinus(final Class<? extends Object> beanClass, String tableName, Object... minus);

	public void populateBean(final ResultSet rset, final Object bean);

	public void populateBean(final ResultSet rset, final Object bean, Object... properties);

	public void populateBeanMinus(final ResultSet rset, final Object bean, Object... minus);

	public void populateBean(final ResultSet rset, final Object bean, String tableName);

	public void populateBean(final ResultSet rset, final Object bean, String tableName, Object... properties);

	public void populateBeanMinus(final ResultSet rset, final Object bean, String tableName, Object... minus);

	// some experiment with metadata...

	public void createTable(Class<? extends Object> beanKlass);
	
	public void dropTable(Class<? extends Object> beanKlass);

	public void createTables();
	
	// some useful methods to handle with manual queries
	
	public String propertyToColumn(Class<? extends Object> clazz, Object property, String alias);
	
	public String propertyToColumn(Class<? extends Object> clazz, Object property);
	
	/**
	 * Creates an single instance only with primary keys according the given object
	 * @param bean - The bean with all primary key set
	 * @return A new instance of the bean
	 */
	public <E> E createBasicInstance(E bean);
	
	/**
	 * Compare differences between two beans holding the properties of first bean passed and
	 * adding null properties in nullProps list. This method returns null when beans has no differences
	 * @param
	 * 		bean
	 * @param
	 * 		another
	 * @param 
	 * 		nullProps An empty list
	 * @return A new instance of the bean
	 */
	public <E> E compareDifferences(E bean, E another, List<String> nullProps);
	
	/**
	 * Returns the table name configured for the given class
	 * @param clazz - The bean class
	 * @return Table name
	 */
	public String buildTableName(Class<? extends Object> clazz);
	
	/**
	 * Returns the BeanConfig object from the given bean class
	 * @param clazz
	 * @return BeanConfig
	 */
	public BeanConfig getConfigFor(Class<?> clazz);
	
	/**
	 * Builds a new QueryBuilder so it's possible to create fluent custom SQL queries
	 * @return QueryBuilder object
	 */
	public QueryBuilder buildQuery();
	
	//trigger..
	/**
	 * Add a TriggerListener in this session. Remember that it's not added in BeanConfig, only in current session
	 * @param trigger
	 */
	public void addTrigger(TriggerListener trigger);
	
	/**
	 * Removes the TriggerListener from session
	 * @param trigger
	 */
	public void removeTrigger(TriggerListener trigger);
	
	/**
	 * Return a table alias for this bean class
	 * 
	 * @param beanClass
	 * @return TableAlias
	 */
	public <E> TableAlias<E> createTableAlias(Class<? extends E> beanClass);
	
	/**
	 * Return a table alias for this bean class with a prefix.
	 * 
	 * @param beanClass
	 * @param prefix
	 * @return TableAlias
	 */
	public <E> TableAlias<E> createTableAlias(Class<? extends E> beanClass, String prefix);
	
} public class DBTypes {

	public static final StringType STRING = new StringType();
	public static final IntegerType INTEGER = new IntegerType();
	public static final DateType DATE = new DateType();
	public static final SequenceType SEQUENCE = new SequenceType();
	public static final AutoIncrementType AUTOINCREMENT = new AutoIncrementType();
	public static final AutoTimestampType AUTOTIMESTAMP = new AutoTimestampType();
	public static final LongType LONG = new LongType();
	public static final DoubleType DOUBLE = new DoubleType();
	public static final TimeType TIME = new TimeType();
	public static final TimestampType TIMESTAMP = new TimestampType();
	public static final FloatType FLOAT = new FloatType();
	public static final BooleanType BOOLEAN = new BooleanType();
	public static final BooleanStringType BOOLEANSTRING = new BooleanStringType();
	public static final BooleanIntType BOOLEANINT = new BooleanIntType();
	public static final BigDecimalType BIGDECIMAL = new BigDecimalType();
	public static final NowOnUpdateTimestampType NOW_ON_UPDATE_TIMESTAMP = new NowOnUpdateTimestampType();
	public static final NowOnInsertTimestampType NOW_ON_INSERT_TIMESTAMP = new NowOnInsertTimestampType();
	public static final NowOnInsertAndUpdateTimestampType NOW_ON_BOTH_TIMESTAMP = new NowOnInsertAndUpdateTimestampType();
	public static final EnumValueTypeFactory ENUMVALUE = EnumValueTypeFactory.getInstance();
	public static final EnumIdTypeFactory ENUMID = EnumIdTypeFactory.getInstance();
	public static final ByteArrayType BYTE_ARRAY = new ByteArrayType();
	public static final GenericType GENERIC = new GenericType();
	public static final DateTimeJodaType JODA_DATETIME = new DateTimeJodaType();
	public static final LocalDateJodaType JODA_LOCALDATE = new LocalDateJodaType();
	public static final LocalTimeJodaType JODA_LOCALTIME = new LocalTimeJodaType();

	@SuppressWarnings("unchecked")
	public static DBType<?> from(Class<? extends Object> klass) {
		if (klass.isInterface()) {
			return null;
		} else if (klass.equals(String.class)) {
			return STRING;
		} else if (klass.equals(Integer.class) || klass.equals(int.class)) {
			return INTEGER;
		} else if (klass.equals(Date.class)) {
			return TIMESTAMP;
		} else if (klass.equals(Long.class) || klass.equals(long.class)) {
			return LONG;
		} else if (klass.equals(Double.class) || klass.equals(double.class)) {
			return DOUBLE;
		} else if (klass.equals(Float.class) || klass.equals(float.class)) {
			return FLOAT;
		} else if (klass.equals(Boolean.class) || klass.equals(boolean.class)) {
			return BOOLEANSTRING;
		} else if (klass.equals(BigDecimal.class)) {
			return BIGDECIMAL;
		} else if (klass.isEnum()) {
			return ENUMVALUE.from((Class<? extends Enum<?>>) klass);
		} else if (klass.equals(byte[].class)) {
			return BYTE_ARRAY;
		}
		return GENERIC;
	}

}
 public class TableAlias<E> {
	
	private final Class<? extends E> beanClass;
	private final String prefix;
	private final BeanSession session;
	private final BeanConfig config;
	private final E proxy;
	
	public TableAlias(BeanSession session, BeanConfig config, Class<? extends E> beanClass) {
		this(session, config, beanClass, null);
	}
	
	public TableAlias(BeanSession session, BeanConfig config, Class<? extends E> beanClass, String prefix) {
		this.beanClass = beanClass;
		this.prefix = prefix;
		this.session = session;
		this.config = config;
		this.proxy = PropertiesProxy.create(beanClass);
	}
	
	@Override
    public String toString() {
	    return "TableAlias [beanClass=" + beanClass.getSimpleName() + ", prefix=" + prefix + "]";
    }

	/**
	 * Return the db columns of a select statements.
	 * 
	 * @return the columns to build a select statement
	 */
	public String columns(Object... props) {
		if (prefix != null) {
			return session.buildSelect(beanClass, prefix, props);
		} else {
			return session.buildSelect(beanClass, props);
		}
	}
	
	public String columnsMinus(Object... props) {
		if (prefix != null) {
			return session.buildSelectMinus(beanClass, prefix, props);
		} else {
			return session.buildSelectMinus(beanClass, props);
		}
	}
	
	/**
	 * Return the table name.
	 * 
	 * @return the table name
	 */
	public String tableName() {
		if (prefix != null) {
			return config.getTableName() + " " + prefix;
		} else {
			return config.getTableName();
		}
	}
	
	/**
	 * Return the db column name for this bean property.
	 * 
	 * @param prop this is a filler parameter because a proxy call will be performed!
	 * @return the db column name of this property
	 */
	public String column(Object prop) {
		
		String propName = PropertiesProxy.getPropertyName();
		
		DBField field = config.getField(propName);
		
		if (field == null) throw new IllegalStateException("Cannot find field for property \"" + propName + "\" on beanconfig: " + config);
		
		if (prefix != null) {
			return prefix + "." + field.getDbName();
		} else {
			return field.getDbName();
		}
	}
	
	public E pxy() {
		return proxy;
	}
	
	public E proxy() {
		return proxy;
	}
	
	public String prefix() {
		return prefix;
	}
	
	public Class<? extends E> beanClass() {
		return beanClass;
	}
	
} public class EnumIdType implements DBType<Enum<?>> {

	private final Class<? extends Enum<?>> enumType;
	private final Method fromIdMethod;
	private final Method getIdMethod;

	private boolean canBeNull = false;

	@Override
	public boolean canBeNull() {
		return canBeNull;
	}

	public EnumIdType nullable(boolean flag) {
		EnumIdType d = new EnumIdType(enumType);
		d.canBeNull = flag;
		return d;
	}

	public EnumIdType(final Class<? extends Enum<?>> enumType) {
		this.enumType = enumType;
		this.fromIdMethod = getFromIdMethod(enumType);
		this.getIdMethod = getGetIdMethod(enumType);
	}

	@Override
	public String getAnsiType() {
		return "smallint";
	}

	private static Method getFromIdMethod(Class<? extends Enum<?>> enumType) {

		try {

			return enumType.getMethod("fromId", int.class);
		} catch (Exception e) {

			throw new BeanException("Cannot find fromId(int) method from enum class: " + enumType, e);
		}
	}

	private static Method getGetIdMethod(Class<? extends Enum<?>> enumType) {

		try {

			return enumType.getMethod("getId", (Class[]) null);
		} catch (Exception e) {

			throw new BeanException("Cannot find getId() method from enum class: " + enumType, e);
		}
	}

	private Enum<?> fromId(int id) {
		try {
			return (Enum<?>) fromIdMethod.invoke(null, id);
		} catch (Exception e) {
			throw new BeanException(e);
		}
	}

	private int getId(Enum<?> theEnum) {
		try {
			return (Integer) getIdMethod.invoke(theEnum, (Object[]) null);
		} catch (Exception e) {
			throw new BeanException(e);
		}
	}

	@Override
	public String toString() {

		return this.getClass().getSimpleName() + ": " + enumType.getSimpleName();
	}

	@Override
	public Enum<?> getFromResultSet(final ResultSet rset, final int index) throws SQLException {

		final int id = rset.getInt(index);

		if (rset.wasNull()) {
			return null;
		}

		return fromId(id);
	}

	@Override
	public Enum<?> getFromResultSet(final ResultSet rset, final String field) throws SQLException {

		final int id = rset.getInt(field);

		if (rset.wasNull()) {
			return null;
		}

		return fromId(id);
	}

	@Override
	public Class<? extends Object> getTypeClass() {

		return enumType;
	}

	@Override
	public void bindToStmt(final PreparedStatement stmt, final int index, final Enum<?> value) throws SQLException {

		if (value == null) {

			stmt.setNull(index, Types.INTEGER);

			//Support for overwritten enums (superclass on left)
		} else if (enumType.isAssignableFrom(value.getClass())) {

			final int id = getId(value);

			stmt.setInt(index, id);

		} else {

			throw new IllegalArgumentException("Value '"+value+"' from '"+value.getClass()+"' is not an enum!");
		}
	}

	public static enum Test {

		T1(1), T2(2), T3(3);

		private final int id;

		private Test(int id) {
			this.id = id;
		}

		public int getId() {
			return id;
		}

		public static Test fromId(int id) {
			for (Test t : Test.values()) {
				if (t.getId() == id) {
					return t;
				}
			}
			return null;
		}
	}

	public static void main(String[] args) throws Exception {

		EnumIdType eit = new EnumIdType(Test.class);

		System.out.println(eit.fromId(2));

		System.out.println(eit.getId(Test.T3));

	}
} public class SQLBuilder implements CharSequence {
	
	private final StringBuilder sb;
	private final TableAlias<?>[] aliases;
	
	public SQLBuilder() {
		this.sb = new StringBuilder();
		this.aliases = null;
	}
	
	public SQLBuilder(int initialCapacity) {
		this.sb = new StringBuilder(initialCapacity);
		this.aliases = null;
	}
	
	public SQLBuilder(TableAlias<?> ...aliases) {
		this.sb = new StringBuilder();
		this.aliases = aliases;
	}
	
	public SQLBuilder(int initialCapacity, TableAlias<?> ...aliases) {
		this.sb = new StringBuilder(initialCapacity);
		this.aliases = aliases;
	}
	
	private TableAlias<?> findAlias(Object instance) {
		
		if (aliases == null) return null;
		
		for(TableAlias<?> alias : aliases) {
			if (alias.proxy() == instance) {
				return alias;
			}
		}
		
		return null;
	}
	
	public SQLBuilder column(Object obj) {
		
		if (obj instanceof String) {
			// you are really adding a column name:
			append((String) obj);
		} else {
			Object[] instances = PropertiesProxy.getBeanInstances();
			if (instances == null || instances.length == 0) throw new IllegalStateException("Cannot find bean instance!");
			
			// here we assume the very first klass:
			Object instance = instances[0];
			TableAlias<?> alias = findAlias(instance);
			if (alias == null) throw new IllegalStateException("Cannot find alias: " + instance);
			append(alias.column(null)); // null because it will get from proxy
		}
		
		return this;
	}
	
	//////////////////////////////////////////////////
	/////// CharSequence implementation:
	//////////////////////////////////////////////////

	@Override
    public int length() {
	    return sb.length();
    }

	@Override
    public char charAt(int index) {
	    return sb.charAt(index);
    }

	@Override
    public CharSequence subSequence(int start, int end) {
	    return sb.subSequence(start, end);
    }

	//////////////////////////////////////////////////
	/////// Delegate methods to StringBuilder:
	//////////////////////////////////////////////////
	
	public int capacity() {
	    return sb.capacity();
    }

	@Override
	public int hashCode() {
	    return sb.hashCode();
    }

	public void ensureCapacity(int minimumCapacity) {
	    sb.ensureCapacity(minimumCapacity);
    }

	public void trimToSize() {
	    sb.trimToSize();
    }

	public void setLength(int newLength) {
	    sb.setLength(newLength);
    }

	@Override
	public boolean equals(Object obj) {
	    return sb.equals(obj);
    }

	public SQLBuilder append(Object obj) {
	    sb.append(obj);
	    return this;
    }

	public SQLBuilder append(String str) {
	    sb.append(str);
	    return this;
    }

	public SQLBuilder append(StringBuffer sb) {
	    this.sb.append(sb);
	    return this;
    }

	public SQLBuilder append(CharSequence s) {
	    sb.append(s);
	    return this;
    }

	public int codePointAt(int index) {
	    return sb.codePointAt(index);
    }

	public SQLBuilder append(CharSequence s, int start, int end) {
	    sb.append(s, start, end);
	    return this;
    }

	public SQLBuilder append(char[] str) {
	    sb.append(str);
	    return this;
    }

	public SQLBuilder append(char[] str, int offset, int len) {
	    sb.append(str, offset, len);
	    return this;
    }

	public SQLBuilder append(boolean b) {
	    sb.append(b);
	    return this;
    }

	public SQLBuilder append(char c) {
	    sb.append(c);
	    return this;
    }

	public SQLBuilder append(int i) {
	    sb.append(i);
	    return this;
    }

	public int codePointBefore(int index) {
	    return sb.codePointBefore(index);
    }

	public SQLBuilder append(long lng) {
	    sb.append(lng);
	    return this;
    }

	public SQLBuilder append(float f) {
	    sb.append(f);
	    return this;
    }

	public SQLBuilder append(double d) {
	    sb.append(d);
	    return this;
    }

	public SQLBuilder appendCodePoint(int codePoint) {
	    sb.appendCodePoint(codePoint);
	    return this;
    }

	public SQLBuilder delete(int start, int end) {
	    sb.delete(start, end);
	    return this;
    }

	public SQLBuilder deleteCharAt(int index) {
	    sb.deleteCharAt(index);
	    return this;
    }

	public SQLBuilder replace(int start, int end, String str) {
	    sb.replace(start, end, str);
	    return this;
    }

	public int codePointCount(int beginIndex, int endIndex) {
	    return sb.codePointCount(beginIndex, endIndex);
    }

	public SQLBuilder insert(int index, char[] str, int offset, int len) {
	    sb.insert(index, str, offset, len);
	    return this;
    }

	public SQLBuilder insert(int offset, Object obj) {
	    sb.insert(offset, obj);
	    return this;
    }

	public SQLBuilder insert(int offset, String str) {
	    sb.insert(offset, str);
	    return this;
    }

	public SQLBuilder insert(int offset, char[] str) {
	    sb.insert(offset, str);
	    return this;
    }

	public SQLBuilder insert(int dstOffset, CharSequence s) {
	    sb.insert(dstOffset, s);
	    return this;
    }

	public int offsetByCodePoints(int index, int codePointOffset) {
	    return sb.offsetByCodePoints(index, codePointOffset);
    }

	public SQLBuilder insert(int dstOffset, CharSequence s, int start, int end) {
	    sb.insert(dstOffset, s, start, end);
	    return this;
    }

	public SQLBuilder insert(int offset, boolean b) {
	    sb.insert(offset, b);
	    return this;
    }

	public SQLBuilder insert(int offset, char c) {
	    sb.insert(offset, c);
	    return this;
    }

	public SQLBuilder insert(int offset, int i) {
	    sb.insert(offset, i);
	    return this;
    }

	public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) {
	    sb.getChars(srcBegin, srcEnd, dst, dstBegin);
    }

	public SQLBuilder insert(int offset, long l) {
	    sb.insert(offset, l);
	    return this;
    }

	public SQLBuilder insert(int offset, float f) {
	    sb.insert(offset, f);
	    return this;
    }

	public SQLBuilder insert(int offset, double d) {
	    sb.insert(offset, d);
	    return this;
    }

	public int indexOf(String str) {
	    return sb.indexOf(str);
    }

	public int indexOf(String str, int fromIndex) {
	    return sb.indexOf(str, fromIndex);
    }

	public int lastIndexOf(String str) {
	    return sb.lastIndexOf(str);
    }

	public int lastIndexOf(String str, int fromIndex) {
	    return sb.lastIndexOf(str, fromIndex);
    }

	public SQLBuilder reverse() {
	    sb.reverse();
	    return this;
    }

	@Override
	public String toString() {
	    return sb.toString();
    }

	public void setCharAt(int index, char ch) {
	    sb.setCharAt(index, ch);
    }

	public String substring(int start) {
	    return sb.substring(start);
    }

	public String substring(int start, int end) {
	    return sb.substring(start, end);
    }
} public class SQLUtils {
	
	public static final String UNIQUE_KEY_VIOLATED_STATE = "23505";
	public static final String FOREIGN_KEY_VIOLATED_STATE = "23503";
	
	public static void executeScript(Connection conn, String file, String charset) {
		
		FileInputStream fis = null;
		BufferedReader br = null;
		
		try {

			ScriptRunner script = new ScriptRunner(conn);
			
			fis = new FileInputStream(file);
			
			if (charset != null) {
			
				br = new BufferedReader(new InputStreamReader(fis, charset));
				
			} else {
				
				br = new BufferedReader(new InputStreamReader(fis));
			}
			
			script.runScript(br);
			
		} catch(Exception e) {
			throw new BeanException(e);
		}
	}
	
	public static boolean checkIfTableExists(Connection conn, String tableName) {
		ResultSet rset = null;
		try {
			DatabaseMetaData dbm = conn.getMetaData();
		    rset = dbm.getTables(null, null, null, new String[] { "TABLE" });
		    while(rset.next()) {
		    	String tn = rset.getString("TABLE_NAME");
		    	if (tn.equalsIgnoreCase(tableName)) return true;
		    }
		    return false;
		} catch(Exception e) {
			throw new BeanException(e);
		} finally {
			if (rset != null) {
				try { 
					rset.close(); 
				} catch(Exception e) {
					throw new BeanException(e);
				}
			}
		}
	}
	
	public static void close(Statement stmt) {
		if (stmt != null) {
			try {
				stmt.close();
			} catch(SQLException e) {
				throw new BeanException(e);
			}
		}
	}
	
	public static void close(ResultSet rset, Statement stmt) {
		
		SQLException bad = null;
		
		if (rset != null) {
			try {
				rset.close();
			} catch(SQLException e) {
				bad = e;
			}
		}
		if (stmt != null) {
			try {
				stmt.close();
			} catch(SQLException e) {
				bad = e;
			}
		}
		if (bad != null) throw new BeanException(bad);
	}
	
	public static void close(Statement stmt, Connection conn) {
		SQLException bad = null;
		
		if (stmt != null) {
			try {
				stmt.close();
			} catch(SQLException e) {
				bad = e;
			}
		}
		if (conn != null) {
			try {
				conn.close();
			} catch(SQLException e) {
				bad = e;
			}
		}
		if (bad != null) throw new BeanException(bad);
	}
	
	public static void close(Connection conn) {
		
		if (conn != null) {
			try {
				conn.close();
			} catch(SQLException e) {
				throw new BeanException(e);
			}
		}
		
	}
	
	public static void close(ResultSet rset, Statement stmt, Connection conn) {
		SQLException bad = null;
		
		if (rset != null) {
			try {
				rset.close();
			} catch(SQLException e) {
				bad = e;
			}
		}
		if (stmt != null) {
			try {
				stmt.close();
			} catch(SQLException e) {
				bad = e;
			}
		}
		if (conn != null) {
			try {
				conn.close();
			} catch(SQLException e) {
				bad = e;
			}
		}
		if (bad != null) throw new BeanException(bad);
	}
	
	/**
	 * Fill the given {@code PreparedStatement} object with the specified parameters.<br><br>
	 * <i>Note that this method uses the {@code setObject} method which depends 
	 * of a specified JDBC driver implementation to work properly.</i><br>
	 * 
	 * @param stmt - The {@code PreparedStatement} object
	 * @param params - Values that will be added in statement
	 * @return The last index added plus 1. It can be useful for continuous statement filling operations.
	 * @see 	#fillStatementIndx(PreparedStatement, int, Object...)
	 * @see		PreparedStatement#setObject(int, Object)
	 */
	public static int fillStatement(PreparedStatement stmt, Object... params) {
		return fillStatementIndx(stmt, 1, params);
	}

	/**
	 * Fill the given {@code PreparedStatement} object with the specified parameters 
	 * starting at {@code indx} position.<br><br>
	 * <i>Note that this method uses the {@code setObject} method which depends 
	 * of a specified JDBC driver implementation to work properly.</i><br>
	 * 
	 * @param stmt - The {@code PreparedStatement} object
	 * @param indx - Initial index for setting attributes in statement
	 * @param params - Values that will be added in statement
	 * @return The next index (last added plus 1). It can be useful for continuous statement filling operations.
	 * @see 	#fillStatement(PreparedStatement, Object...)
	 * @see		PreparedStatement#setObject(int, Object)
	 */
	public static int fillStatementIndx(PreparedStatement stmt, int indx, Object... params) {

		try {
			for (Object obj : params)
				stmt.setObject(indx++, obj);

			return indx;
			
		}catch (Exception e) {
			throw new BeanException("Error setting values for statement", e);
		}
	}
	
	/**
	 * Prepare a statement (PreparedStatement) with the query and set the parameters.
	 * 
	 * @param conn the connection
	 * @param query the query to prepare
	 * @param params the parameters to set in the query
	 * @return the statement ready to be executed
	 * @throws SQLException
	 */
	public static PreparedStatement prepare(Connection conn, String query, Object... params) throws SQLException {
		PreparedStatement stmt = conn.prepareStatement(query);
		fillStatement(stmt, params);
		return stmt;
	}
	
	public static OrderBy orderByAsc(Object field) {
		OrderBy orderBy = new OrderBy();
		if (field instanceof String) {
			orderBy.orderByAsc((String) field); // i am surprised this is necessary
		} else {
			orderBy.orderByAsc(field);
		}
		return orderBy;
	}
	
	public static OrderBy orderByDesc(Object field) {
		OrderBy orderBy = new OrderBy();
		if (field instanceof String) {
			orderBy.orderByDesc((String) field);  // i am surprised this is necessary
		} else {
			orderBy.orderByDesc(field);
		}
		return orderBy;
	}
	
	public static Limit lim(int x) {
		return new Limit(x);
	}
	
	
	public static void beginTransaction(Connection conn) {
		try {
			if (conn.getAutoCommit())
				conn.setAutoCommit(false);
		} catch (Exception e) {
			throw new BeanException("Unable to begin transaction", e);
		}
	}
	
	public static void commitTransaction(Connection conn) {
		try {
			conn.commit();
		} catch (Exception e) {
			throw new BeanException("Unable to commit transaction", e);
		}
	}
	
	public static void rollbackTransaction(Connection conn) {
		try {
			conn.rollback();
		} catch (Exception e) {
			throw new BeanException("Unable to rollback transaction", e);
		}
	}
}
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import junit.framework.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mentabean.AutoBeanConfig;
import org.mentabean.BeanConfig;
import org.mentabean.BeanException;
import org.mentabean.BeanManager;
import org.mentabean.BeanSession;
import org.mentabean.DBTypes;
import org.mentabean.sql.TableAlias;
import org.mentabean.type.EnumIdType;
import org.mentabean.util.PropertiesProxy;
import org.mentabean.util.SQLBuilder;
import org.mentabean.util.SQLUtils;
			boolean loaded = session.load(p);
			Assert.assertEquals("Test", p.getTitle());
			Assert.assertEquals(1, p.getUser().getId());
			Assert.assertNotNull(p.getUser()); // loads user with id only
			Assert.assertEquals(1, p.getUser().getId());
			Assert.assertNull(p.getUser().getUsername());

			// Load user for this post... (let's do our manual lazy loading)
			u = new User(p.getUser().getId());
			loaded = session.load(u);
			Assert.assertEquals(true, loaded);
			p.setUser(u); // manual lazy loading (forget about automatic lazy loading, you want control!)

			// Use JOIN to load all dependencies with a single query... (you know how to make a join, right?)

			p = new Post(1);

			StringBuilder query = new StringBuilder(256);
			query.append("select ");
			query.append(session.buildSelect(Post.class, "p"));
			query.append(", ");
			query.append(session.buildSelect(User.class, "u"));
			query.append(" from Posts p join Users u on p.user_id = u.id");
			query.append(" where p.id = ?");

			stmt = SQLUtils.prepare(conn, query.toString(), p.getId());
			
			rset = stmt.executeQuery();

			if (rset.next()) {

				session.populateBean(rset, p, "p");

				u = new User();

				session.populateBean(rset, u, "u");

				p.setUser(u); // manual lazy loading (we prefer to have control!)
			}

			Assert.assertEquals(1, p.getId());
			Assert.assertEquals("Test", p.getTitle());
			Assert.assertEquals(1, u.getId());
			Assert.assertEquals("saoj", p.getUser().getUsername());
			Assert.assertTrue((new Date()).getTime() >= p.getInsertTime().getTime());

			rset.close();
			stmt.close();

			// Deleting => No cascade deletes, if you want that implement in the database level...

			u = new User(1);
			boolean deleted = session.delete(u);
			Assert.assertEquals(true, deleted);

			// Post of course is still there...
			p = new Post(1);
			loaded = session.load(p);
			Assert.assertEquals(true, loaded);
			Assert.assertEquals(1, p.getUser().getId()); // of course this user is gone!

			u = new User(1);
			loaded = session.load(u);
			Assert.assertEquals(false, loaded); // use was deleted above...

		} finally {
			close(stmt, rset);
			close(conn);
		}
	}

	@Test
	public void testOneToOneRelationshipCRUDWithTableAlias() throws SQLException {

		BeanManager beanManager = new BeanManager();
		BeanConfig userConfig = getUserBeanConfig();
		getPostBeanConfig(beanManager);
		beanManager.addBeanConfig(userConfig);

		Connection conn = null;
		PreparedStatement stmt = null;
		ResultSet rset = null;

		try {

			conn = getConnection();
			BeanSession session = new H2BeanSession(beanManager, conn);

			createTables(conn);

			User u = new User("saoj", "1980-01-02");
			session.insert(u);

			Assert.assertEquals(1, u.getId());

			// Now insert a post for this user...
			Post p = new Post(new User(1), "Test", "This is a test!");
			session.insert(p);

			Assert.assertEquals(1, p.getId());

			// Load from the database...
			p = new Post(1);
			boolean loaded = session.load(p);
			Assert.assertEquals("Test", p.getTitle());
			Assert.assertEquals(1, p.getUser().getId());
			Assert.assertNotNull(p.getUser());
			Assert.assertEquals(1, p.getUser().getId());
			Assert.assertNull(p.getUser().getUsername());

			// Load user for this post... (let's do our manual lazy loading)
			u = new User(p.getUser().getId());
			loaded = session.load(u);
			Assert.assertEquals(true, loaded);
			p.setUser(u); // manual lazy loading (forget about automatic lazy loading, you want control!)

			// Use JOIN to load all dependencies with a single query... (you know how to make a join, right?)

			p = new Post(1);
			
			TableAlias<Post> postAlias = session.createTableAlias(Post.class, "p");