package org.springframework.roo.addon.dbre.addon.model;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.springframework.roo.model.JavaPackage;
/**
* Represents the database model, ie. the tables in the database.
*
* @author Alan Stewart
* @author Juan Carlos GarcĂa
* @since 1.1
*/
public class Database {
/** The JavaPackage where entities are created */
private JavaPackage destinationPackage;
/** Whether or not to disable identifier auto generation */
private boolean disableGeneratedIdentifiers;
/** Whether or not to disable version fields */
private boolean disableVersionFields;
/**
* Whether or not to included non-portable JPA attributes in the @Column
* annotation
*/
private boolean includeNonPortableAttributes;
/** The module where the entities are created */
private String moduleName;
/** Whether or not this database has multiple schemas */
private boolean multipleSchemas;
/** Whether or not to generate a repository layer for each entity */
private boolean repository;
/** Whether or not to generate a service layer for each entity */
private boolean service;
/** All tables. */
private final Set<Table> tables;
/** Whether to create integration tests */
private boolean testAutomatically;
/**
* Constructor
*
* @param tables (required)
*/
Database(final Set<Table> tables) {
Validate.notNull(tables, "Tables required");
this.tables = tables;
init();
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Database other = (Database) obj;
if (tables == null) {
if (other.tables != null) {
return false;
}
} else if (!tables.equals(other.tables)) {
return false;
}
return true;
}
public JavaPackage getDestinationPackage() {
return destinationPackage;
}
public String getModuleName() {
return moduleName;
}
public Table getTable(final String name, final String schemaName) {
for (final Table table : tables) {
if (table.getName().equals(name)) {
if (StringUtils.isBlank(schemaName)
|| DbreModelService.NO_SCHEMA_REQUIRED.equals(schemaName)
|| table.getSchema().getName().equals(schemaName)) {
return table;
}
}
}
return null;
}
public Set<Table> getTables() {
return Collections.unmodifiableSet(tables);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (tables == null ? 0 : tables.hashCode());
return result;
}
public boolean hasMultipleSchemas() {
return multipleSchemas;
}
public boolean hasTables() {
return !tables.isEmpty();
}
public boolean isDisableGeneratedIdentifiers() {
return disableGeneratedIdentifiers;
}
public boolean isDisableVersionFields() {
return disableVersionFields;
}
public boolean isIncludeNonPortableAttributes() {
return includeNonPortableAttributes;
}
public boolean isRepository() {
return repository;
}
public boolean isService() {
return service;
}
public boolean isTestAutomatically() {
return testAutomatically;
}
public void setDestinationPackage(final JavaPackage destinationPackage) {
this.destinationPackage = destinationPackage;
}
public void setDisableGeneratedIdentifiers(final boolean disableGeneratedIdentifiers) {
this.disableGeneratedIdentifiers = disableGeneratedIdentifiers;
}
public void setDisableVersionFields(final boolean disableVersionFields) {
this.disableVersionFields = disableVersionFields;
}
public void setIncludeNonPortableAttributes(final boolean includeNonPortableAttributes) {
this.includeNonPortableAttributes = includeNonPortableAttributes;
}
public void setModuleName(final String moduleName) {
this.moduleName = moduleName;
}
public void setRepository(final boolean repository) {
this.repository = repository;
}
public void setService(final boolean service) {
this.service = service;
}
public void setTestAutomatically(final boolean testAutomatically) {
this.testAutomatically = testAutomatically;
}
@Override
public String toString() {
return String
.format(
"Database [destinationPackage=%s, disableGeneratedIdentifiers=%s, disableVersionFields=%s, includeNonPortableAttributes=%s, moduleName=%s, multipleSchemas=%s, repository=%s, service=%s, tables=%s, testAutomatically=%s]",
destinationPackage, disableGeneratedIdentifiers, disableVersionFields,
includeNonPortableAttributes, moduleName, multipleSchemas, repository, service, tables,
testAutomatically);
}
/**
* Initializes the model by establishing the relationships between elements
* in this model eg. in foreign keys etc.
*/
private void init() {
final Set<Schema> schemas = new HashSet<Schema>();
for (final Table table : tables) {
schemas.add(table.getSchema());
initializeImportedKeys(table);
initializeExportedKeys(table);
initializeIndices(table);
initializeJoinTable(table);
}
multipleSchemas = schemas.size() > 1;
}
private void initializeExportedKeys(final Table table) {
final Map<String, Short> keySequenceMap = new LinkedHashMap<String, Short>();
Short keySequence = null;
for (final ForeignKey exportedKey : table.getExportedKeys()) {
if (exportedKey.getForeignTable() != null) {
continue;
}
final String foreignTableName = exportedKey.getForeignTableName();
final String foreignSchemaName = exportedKey.getForeignSchemaName();
final Table targetTable = getTable(foreignTableName, foreignSchemaName);
if (targetTable != null) {
exportedKey.setForeignTable(targetTable);
keySequence = keySequenceMap.get(foreignTableName);
if (keySequence == null) {
keySequence = 0;
keySequenceMap.put(foreignTableName, keySequence);
}
if (table.getExportedKeyCountByForeignTableName(foreignTableName) > 1) {
keySequenceMap.put(foreignTableName, (short) (keySequence.shortValue() + 1));
}
exportedKey.setKeySequence(keySequence);
}
for (final Reference reference : exportedKey.getReferences()) {
if (reference.getLocalColumn() == null) {
final Column localColumn = table.findColumn(reference.getLocalColumnName());
if (localColumn != null) {
reference.setLocalColumn(localColumn);
}
}
if (reference.getForeignColumn() == null && exportedKey.getForeignTable() != null) {
final Column foreignColumn =
exportedKey.getForeignTable().findColumn(reference.getForeignColumnName());
if (foreignColumn != null) {
reference.setForeignColumn(foreignColumn);
}
}
}
}
}
private void initializeImportedKeys(final Table table) {
final Map<String, Short> keySequenceMap = new LinkedHashMap<String, Short>();
Short keySequence = null;
final Map<Column, Set<ForeignKey>> repeatedColumns =
new LinkedHashMap<Column, Set<ForeignKey>>();
for (final ForeignKey foreignKey : table.getImportedKeys()) {
if (foreignKey.getForeignTable() != null) {
continue;
}
final String foreignTableName = foreignKey.getForeignTableName();
final String foreignSchemaName = foreignKey.getForeignSchemaName();
final Table targetTable = getTable(foreignTableName, foreignSchemaName);
if (targetTable != null) {
keySequence = keySequenceMap.get(foreignTableName);
if (keySequence == null) {
keySequence = 0;
keySequenceMap.put(foreignTableName, keySequence);
}
foreignKey.setForeignTable(targetTable);
if (table.getImportedKeyCountByForeignTableName(foreignTableName) > 1) {
keySequenceMap.put(foreignTableName, (short) (keySequence.shortValue() + 1));
}
foreignKey.setKeySequence(keySequence);
}
for (final Reference reference : foreignKey.getReferences()) {
if (reference.getLocalColumn() == null) {
final Column localColumn = table.findColumn(reference.getLocalColumnName());
if (localColumn != null) {
reference.setLocalColumn(localColumn);
final Set<ForeignKey> fkSet =
repeatedColumns.containsKey(localColumn) ? repeatedColumns.get(localColumn)
: new LinkedHashSet<ForeignKey>();
fkSet.add(foreignKey);
repeatedColumns.put(localColumn, fkSet);
}
}
if (reference.getForeignColumn() == null && foreignKey.getForeignTable() != null) {
final Column foreignColumn =
foreignKey.getForeignTable().findColumn(reference.getForeignColumnName());
if (foreignColumn != null) {
reference.setForeignColumn(foreignColumn);
}
}
}
}
// Mark repeated columns with insertable = false and updatable = false
for (final Map.Entry<Column, Set<ForeignKey>> entrySet : repeatedColumns.entrySet()) {
final Set<ForeignKey> foreignKeys = entrySet.getValue();
for (final ForeignKey foreignKey : foreignKeys) {
if (foreignKeys.size() > 1 || foreignKey.getForeignTableName().equals(table.getName())) {
for (final Reference reference : foreignKey.getReferences()) {
reference.setInsertableOrUpdatable(false);
}
}
}
}
}
private void initializeIndices(final Table table) {
for (final Index index : table.getIndices()) {
for (final IndexColumn indexColumn : index.getColumns()) {
final Column column = table.findColumn(indexColumn.getName());
if (column != null && index.isUnique()) {
column.setUnique(true);
}
}
}
}
/**
* Determines if a table is a many-to-many join table.
* <p>
* To be identified as a many-to-many join table, the table must have have
* exactly two primary keys and have exactly two foreign-keys pointing to
* other entity tables and have no other columns.
*/
private void initializeJoinTable(final Table table) {
boolean equals =
table.getColumnCount() == 2 && table.getPrimaryKeyCount() == 2
&& table.getImportedKeyCount() == 2
&& table.getPrimaryKeyCount() == table.getImportedKeyCount();
final Iterator<Column> iter = table.getColumns().iterator();
while (equals && iter.hasNext()) {
final Column column = iter.next();
equals &= table.findImportedKeyByLocalColumnName(column.getName()) != null;
}
if (equals) {
table.setJoinTable(true);
}
}
}