Java tutorial
// // Depot library - a Java relational persistence library // https://github.com/threerings/depot/blob/master/LICENSE package com.samskivert.depot.impl; import java.sql.SQLException; import java.util.Arrays; import java.util.Map; import java.util.Set; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.samskivert.depot.DatabaseException; import com.samskivert.depot.PersistenceContext; import com.samskivert.depot.PersistentRecord; import com.samskivert.depot.clause.QueryClause; import com.samskivert.depot.expression.SQLExpression; import static com.google.common.base.Preconditions.checkArgument; /** * Maintains a record of the persistent classes brought into the context of the associated SQL, * i.e. any class associated with a concrete table that would appear in FROM or JOIN clauses or as * the target of an UPDATE or an INSERT or any other place where a table abbreviation could be * constructed. * * The main motivation for breaking this functionality out into its own class is to encapsulate the * operation that throws {@link DatabaseException} as separate from the operations that throw * {@link SQLException}. Once this class has been constructed, it may be used to create {@link * SQLBuilder} instances without any {@link DatabaseException} worries. */ public class DepotTypes { /** A trivial instance that is accessible in places where we want the dialectal benefits of the * SQLBuilder without really requiring per-persistent-class context. */ public static DepotTypes TRIVIAL = new DepotTypes(); /** * Conveniently constructs a {@link DepotTypes} object given {@link QueryClause} objects, which * are interrogated for their class definition sets through {@link SQLExpression#addClasses}. */ public static <T extends PersistentRecord> DepotTypes getDepotTypes(PersistenceContext ctx, Iterable<? extends QueryClause> clauses) throws DatabaseException { Set<Class<? extends PersistentRecord>> classSet = Sets.newLinkedHashSet(); for (QueryClause clause : clauses) { if (clause != null) { clause.addClasses(classSet); } } return new DepotTypes(ctx, classSet); } /** * A varargs version of {@link #getDepotTypes(PersistenceContext,Iterable)}. */ public static <T extends PersistentRecord> DepotTypes getDepotTypes(PersistenceContext ctx, QueryClause... clauses) throws DatabaseException { return getDepotTypes(ctx, Arrays.asList(clauses)); } /** * Create a new DepotTypes with the given {@link PersistenceContext} and a collection of * persistent record classes. */ public DepotTypes(PersistenceContext ctx, Iterable<Class<? extends PersistentRecord>> others) throws DatabaseException { for (Class<? extends PersistentRecord> c : others) { addClass(ctx, c); } } /** * Create a new DepotTypes with the given {@link PersistenceContext} and the given * persistent record. */ public DepotTypes(PersistenceContext ctx, Class<? extends PersistentRecord> pClass) throws DatabaseException { addClass(ctx, pClass); } /** * Return the full table name of the given persistent class, which must have been previously * registered with this object. */ public String getTableName(Class<? extends PersistentRecord> cl) { return getMarshaller(cl).getTableName(); } /** * Return the current abbreviation by which we refer to the table associated with the given * persistent record -- which must have been previously registered with this object. If the * useTableAbbreviations flag is false, we return the full table name instead. * * @exception IllegalArgumentException thrown if the specified class is not known. */ public String getTableAbbreviation(Class<? extends PersistentRecord> cl) { if (_useTableAbbreviations) { Integer ix = _classIx.get(cl); checkArgument(ix != null, "Unknown persistence class: " + cl); return "T" + (ix + 1); } return getTableName(cl); } /** * Return the associated database column of the given field of the given persistent class, * throwing an exception if the record has not been registered with this object, or if the * field is unknown on the record. * * @exception IllegalArgumentException thrown if the specified field is not part of the * specified persistent class. */ public String getColumnName(Class<? extends PersistentRecord> cl, String field) { FieldMarshaller<?> fm = getMarshaller(cl).getFieldMarshaller(field); checkArgument(fm != null, "Field not known on class [field=%s, class=%s]", field, cl); return fm.getColumnName(); } /** * Return the {@link DepotMarshaller} associated with the given persistent class, if it's been * registered with this object. * * @exception IllegalArgumentException thrown if the specified class is not known. */ public <T extends PersistentRecord> DepotMarshaller<T> getMarshaller(Class<T> cl) { @SuppressWarnings("unchecked") DepotMarshaller<T> marsh = (DepotMarshaller<T>) _classMap.get(cl); checkArgument(marsh != null, "Persistent class not known: " + cl); return marsh; } /** * Register a new persistent class with this object. */ public void addClass(PersistenceContext ctx, Class<? extends PersistentRecord> type) throws DatabaseException { if (_classMap.containsKey(type)) { return; } // add the class in question DepotMarshaller<?> marsh = ctx.getMarshaller(type); _classMap.put(type, marsh); _classIx.put(type, _classIx.size()); // if this class is @Computed and has a shadow, add its shadow if (marsh.getComputed() != null && !PersistentRecord.class.equals(marsh.getComputed().shadowOf())) { addClass(ctx, marsh.getComputed().shadowOf()); } } /** * Return the value of the useTableAbbreviations flag, which governs the behaviour when * referencing columns during SQL construction. Normally, this flag is on, and tables are * referenced as e.g. T1.itemId, but there are cases of weak/broken SQL where abbreviations * may not be brought into play. In these cases we prepend the full table name. */ public boolean getUseTableAbbreviations() { return _useTableAbbreviations; } /** * Sets the value of the useTableAbbreviations flag, which governs the behaviour when * referencing columns during SQL construction. Normally, this flag is on, and tables are * referenced as e.g. T1.itemId, but there are cases of weak/broken SQL where abbreviations * may not be brought into play. In these cases we prepend the full table name. */ public void setUseTableAbbreviations(boolean doUse) { _useTableAbbreviations = doUse; } // constructor used to create TRIVIAL protected DepotTypes() { } /** Classes mapped to integers, used for table abbreviation indexing. */ protected Map<Class<?>, Integer> _classIx = Maps.newHashMap(); /** Classes mapped to marshallers, used for table names and field lists. */ protected Map<Class<?>, DepotMarshaller<?>> _classMap = Maps.newHashMap(); /** When false, override the normal table abbreviations and return full table names instead. */ protected boolean _useTableAbbreviations = true; }