@Table(uniqueConstraints) support from Zell.

git-svn-id: https://samskivert.googlecode.com/svn/trunk@2033 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
mdb
2007-01-15 01:57:35 +00:00
parent e4eabab6f8
commit 689ec8c231
@@ -31,8 +31,12 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
@@ -42,8 +46,10 @@ import com.samskivert.jdbc.depot.annotation.Entity;
import com.samskivert.jdbc.depot.annotation.GeneratedValue;
import com.samskivert.jdbc.depot.annotation.Id;
import com.samskivert.jdbc.depot.annotation.Index;
import com.samskivert.jdbc.depot.annotation.Table;
import com.samskivert.jdbc.depot.annotation.TableGenerator;
import com.samskivert.jdbc.depot.annotation.Transient;
import com.samskivert.jdbc.depot.annotation.UniqueConstraint;
import com.samskivert.jdbc.JDBCUtil;
import com.samskivert.jdbc.depot.clause.Where;
@@ -71,8 +77,10 @@ public class DepotMarshaller<T>
{
_pclass = pclass;
// see if this is a computed entity
Entity entity = pclass.getAnnotation(Entity.class);
Table table = null;
// see if this is a computed entity
Computed computed = pclass.getAnnotation(Computed.class);
if (computed == null) {
// if not, this class has a corresponding SQL table
@@ -86,6 +94,9 @@ public class DepotMarshaller<T>
}
_postamble = entity.postamble();
}
// check for a Table annotation, for unique constraints
table = pclass.getAnnotation(Table.class);
}
// if the entity defines a new TableGenerator, map that in our static table as those are
@@ -221,6 +232,14 @@ public class DepotMarshaller<T>
}
}
// add any unique constraints given
if (table != null) {
for (UniqueConstraint constraint : table.uniqueConstraints()) {
_declarations.add(
"UNIQUE (" + StringUtil.join(constraint.columnNames(), ", ") + ")");
}
}
// add the primary key, if we have one
if (hasPrimaryKey()) {
_declarations.add("PRIMARY KEY (" + getPrimaryKeyColumns() + ")");
@@ -666,8 +685,8 @@ public class DepotMarshaller<T>
}
// enumerate all of the columns now that we've run our pre-migrations
final HashSet<String> columns = new HashSet<String>();
final HashSet<String> indices = new HashSet<String>();
final Set<String> columns = new HashSet<String>();
final Map<String, Set<String>> indexColumns = new HashMap<String, Set<String>>();
ctx.invoke(new Modifier() {
public int invoke (Connection conn) throws SQLException {
DatabaseMetaData meta = conn.getMetaData();
@@ -675,14 +694,34 @@ public class DepotMarshaller<T>
while (rs.next()) {
columns.add(rs.getString("COLUMN_NAME"));
}
rs = meta.getIndexInfo(null, null, getTableName(), false, false);
while (rs.next()) {
indices.add(rs.getString("INDEX_NAME"));
String indexName = rs.getString("INDEX_NAME");
Set<String> set = indexColumns.get(indexName);
if (rs.getBoolean("NON_UNIQUE")) {
// not a unique index: just make sure there's an entry in the keyset
if (set == null) {
indexColumns.put(indexName, null);
}
} else {
// for unique indices we collect the column names
if (set == null) {
set = new HashSet<String>();
indexColumns.put(indexName, set);
}
set.add(rs.getString("COLUMN_NAME"));
}
}
return 0;
}
});
// this is a little silly, but we need a copy for name disambiguation later
Set<String> indicesCopy = new HashSet<String>(indexColumns.keySet());
// add any missing columns
for (String fname : _columnFields) {
FieldMarshaller fmarsh = _fields.get(fname);
@@ -717,12 +756,12 @@ public class DepotMarshaller<T>
}
// add or remove the primary key as needed
if (hasPrimaryKey() && !indices.remove("PRIMARY")) {
if (hasPrimaryKey() && !indexColumns.containsKey("PRIMARY")) {
String pkdef = "primary key (" + getPrimaryKeyColumns() + ")";
log.info("Adding primary key to " + getTableName() + ": " + pkdef);
ctx.invoke(new Modifier.Simple("alter table " + getTableName() + " add " + pkdef));
} else if (!hasPrimaryKey() && indices.remove("PRIMARY")) {
} else if (!hasPrimaryKey() && indexColumns.remove("PRIMARY") != null) {
log.info("Dropping primary from " + getTableName());
ctx.invoke(new Modifier.Simple("alter table " + getTableName() + " drop primary key"));
}
@@ -730,7 +769,7 @@ public class DepotMarshaller<T>
// add any missing indices
Entity entity = _pclass.getAnnotation(Entity.class);
for (Index index : (entity == null ? new Index[0] : entity.indices())) {
if (indices.remove(index.name())) {
if (indexColumns.remove(index.name()) != null) {
continue;
}
String indexdef = "create " + index.type() + " index " + index.name() +
@@ -739,6 +778,42 @@ public class DepotMarshaller<T>
ctx.invoke(new Modifier.Simple(indexdef));
}
// to get the @Table(uniqueIndices...) indices, we use our clever set of column name sets
Set<Set<String>> uniqueIndices = new HashSet<Set<String>>(indexColumns.values());
Table table;
if (getTableName() != null && (table = _pclass.getAnnotation(Table.class)) != null) {
Set<String> colSet = new HashSet<String>();
for (UniqueConstraint constraint : table.uniqueConstraints()) {
// for each given UniqueConstraint, build a new column set
colSet.clear();
for (String column : constraint.columnNames()) {
colSet.add(column);
}
// and check if the table contained this set
if (uniqueIndices.contains(colSet)) {
continue; // good, carry on
}
// else build the index; we'll use mysql's convention of naming it after a column,
// with possible _N disambiguation; luckily we made a copy of the index names!
String indexName = colSet.iterator().next();
if (indicesCopy.contains(indexName)) {
int num = 1;
indexName += "_";
while (indicesCopy.contains(indexName + num)) {
num ++;
}
indexName += num;
}
String[] columnArr = colSet.toArray(new String[colSet.size()]);
String indexdef = "create unique index " + indexName + " on " +
getTableName() + " (" + StringUtil.join(columnArr, ", ") + ")";
log.info("Adding unique index: " + indexdef);
ctx.invoke(new Modifier.Simple(indexdef));
}
}
// we do not auto-remove columns but rather require that EntityMigration.Drop records be
// registered by hand to avoid accidentally causin the loss of data