blob: 80dec0b176609528166efca4afe1dd4912150b24 [file] [log] [blame]
// Copyright 2008 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gwtorm.jdbc;
import com.google.common.base.Preconditions;
import com.google.gwtorm.schema.ColumnModel;
import com.google.gwtorm.schema.RelationModel;
import com.google.gwtorm.schema.SchemaModel;
import com.google.gwtorm.schema.SequenceModel;
import com.google.gwtorm.schema.sql.SqlDialect;
import com.google.gwtorm.server.AbstractSchema;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.Schema;
import com.google.gwtorm.server.StatementExecutor;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set;
/** Internal base class for implementations of {@link Schema}. */
public abstract class JdbcSchema extends AbstractSchema {
private final Database<?> dbDef;
private Connection conn;
protected JdbcSchema(final Database<?> d) throws OrmException {
dbDef = d;
conn = dbDef.newConnection();
}
public final Connection getConnection() {
return conn;
}
public final SqlDialect getDialect() {
return dbDef.getDialect();
}
@Override
public void commit() throws OrmException {
try {
if (!conn.getAutoCommit()) {
conn.commit();
}
} catch (SQLException err) {
throw new OrmException("Cannot commit transaction", err);
} finally {
try {
conn.setAutoCommit(true);
} catch (SQLException err) {
throw new OrmException("Cannot set auto commit mode", err);
}
}
}
@Override
public void rollback() throws OrmException {
try {
if (!conn.getAutoCommit()) {
conn.rollback();
}
} catch (SQLException err) {
throw new OrmException("Cannot rollback transaction", err);
} finally {
try {
conn.setAutoCommit(true);
} catch (SQLException err) {
throw new OrmException("Cannot set auto commit mode", err);
}
}
}
@Override
public void updateSchema(final StatementExecutor e) throws OrmException {
try {
createSequences(e);
createRelations(e);
for (final RelationModel rel : dbDef.getSchemaModel().getRelations()) {
addColumns(e, rel);
}
} catch (SQLException err) {
throw new OrmException("Cannot update schema", err);
}
}
private void createSequences(final StatementExecutor e) throws OrmException,
SQLException {
final SqlDialect dialect = dbDef.getDialect();
final SchemaModel model = dbDef.getSchemaModel();
Set<String> have = dialect.listSequences(getConnection());
for (final SequenceModel s : model.getSequences()) {
if (!have.contains(s.getSequenceName().toLowerCase())) {
e.execute(s.getCreateSequenceSql(dialect));
}
}
}
private void createRelations(final StatementExecutor e) throws SQLException,
OrmException {
final SqlDialect dialect = dbDef.getDialect();
final SchemaModel model = dbDef.getSchemaModel();
Set<String> have = dialect.listTables(getConnection());
for (final RelationModel r : model.getRelations()) {
if (!have.contains(r.getRelationName().toLowerCase())) {
e.execute(r.getCreateTableSql(dialect));
}
}
}
private void addColumns(final StatementExecutor e, final RelationModel rel)
throws SQLException, OrmException {
final SqlDialect dialect = dbDef.getDialect();
Set<String> have = dialect.listColumns( //
getConnection(), rel.getRelationName().toLowerCase());
for (final ColumnModel c : rel.getColumns()) {
if (!have.contains(c.getColumnName().toLowerCase())) {
dialect.addColumn(e, rel.getRelationName(), c);
}
}
}
public void renameTable(final StatementExecutor e, String from, String to)
throws OrmException {
Preconditions.checkNotNull(e);
Preconditions.checkNotNull(from);
Preconditions.checkNotNull(to);
getDialect().renameTable(e, from, to);
}
public void renameField(final StatementExecutor e, String table, String from,
String to) throws OrmException {
final RelationModel rel = findRelationModel(table);
if (rel == null) {
throw new OrmException("Relation " + table + " not defined");
}
final ColumnModel col = rel.getField(to);
if (col == null) {
throw new OrmException("Relation " + table + " does not have " + to);
}
getDialect().renameColumn(e, table, from, col);
}
public void renameColumn(final StatementExecutor e, String table, String from,
String to) throws OrmException {
final RelationModel rel = findRelationModel(table);
if (rel == null) {
throw new OrmException("Relation " + table + " not defined");
}
final ColumnModel col = rel.getColumn(to);
if (col == null) {
throw new OrmException("Relation " + table + " does not have " + to);
}
getDialect().renameColumn(e, table, from, col);
}
private RelationModel findRelationModel(String table) {
for (final RelationModel rel : dbDef.getSchemaModel().getRelations()) {
if (table.equalsIgnoreCase(rel.getRelationName())) {
return rel;
}
}
return null;
}
@Override
public void pruneSchema(final StatementExecutor e) throws OrmException {
try {
pruneSequences(e);
pruneRelations(e);
for (final RelationModel rel : dbDef.getSchemaModel().getRelations()) {
pruneColumns(e, rel);
}
} catch (SQLException err) {
throw new OrmException("Schema prune failure", err);
}
}
private void pruneSequences(final StatementExecutor e) throws SQLException,
OrmException {
final SqlDialect dialect = dbDef.getDialect();
final SchemaModel model = dbDef.getSchemaModel();
HashSet<String> want = new HashSet<>();
for (final SequenceModel s : model.getSequences()) {
want.add(s.getSequenceName().toLowerCase());
}
for (final String sequence : dialect.listSequences(getConnection())) {
if (!want.contains(sequence)) {
e.execute(dialect.getDropSequenceSql(sequence));
}
}
}
private void pruneRelations(final StatementExecutor e) throws SQLException,
OrmException {
final SqlDialect dialect = dbDef.getDialect();
final SchemaModel model = dbDef.getSchemaModel();
HashSet<String> want = new HashSet<>();
for (final RelationModel r : model.getRelations()) {
want.add(r.getRelationName().toLowerCase());
}
for (final String table : dialect.listTables(getConnection())) {
if (!want.contains(table)) {
e.execute("DROP TABLE " + table);
}
}
}
private void pruneColumns(final StatementExecutor e, final RelationModel rel)
throws SQLException, OrmException {
final SqlDialect dialect = dbDef.getDialect();
HashSet<String> want = new HashSet<>();
for (final ColumnModel c : rel.getColumns()) {
want.add(c.getColumnName().toLowerCase());
}
for (String column : dialect.listColumns( //
getConnection(), rel.getRelationName().toLowerCase())) {
if (!want.contains(column)) {
dialect.dropColumn(e, rel.getRelationName(), column);
}
}
}
@Override
protected long nextLong(final String poolName) throws OrmException {
return getDialect().nextLong(getConnection(), poolName);
}
@Override
public void close() {
if (conn != null) {
try {
conn.close();
} catch (SQLException err) {
// TODO Handle an exception while closing a connection
}
conn = null;
}
}
}