/*
* #!
* Ontopia Engine
* #-
* Copyright (C) 2001 - 2013 The Ontopia Project
* #-
* 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 net.ontopia.persistence.proxy;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.HashSet;
import net.ontopia.utils.OntopiaRuntimeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* INTERNAL: Class that performs the task of accessing and
* manipulating "collection" type instances in the database.<p>
*
* NOTE: Collection type instances must have an identity field and
* exactly one value field.<p>
*/
public class SQLCollectionAccess implements ClassAccessIF {
// Define a logging category.
static Logger log = LoggerFactory.getLogger(SQLCollectionAccess.class.getName());
protected boolean debug = log.isDebugEnabled();
protected RDBMSAccess access;
protected ClassInfoIF cinfo;
protected String sql_load;
protected String sql_add;
protected String sql_remove;
protected String sql_delete;
protected FieldInfoIF identity_field;
protected FieldInfoIF value_field;
public SQLCollectionAccess(RDBMSAccess access, ClassInfoIF cinfo) {
// TODO: The load method is identical to the one in
// SQLOneToOne.java. Should make sure that they are being shared.
this.access = access;
this.cinfo = cinfo;
// Prepare fields
identity_field = cinfo.getIdentityFieldInfo();
FieldInfoIF[] value_fields = cinfo.getOne2OneFieldInfos();
if (value_fields.length != 1)
throw new OntopiaRuntimeException("Number of value fields for type " + cinfo.getName() +
" must be 1 not " + value_fields.length + ".");
value_field = value_fields[0];
// -----------------------------------------------------------------------------
// Load
// -----------------------------------------------------------------------------
// Generate SQL statement
FieldInfoIF[] fields = new FieldInfoIF[] { identity_field, value_field };
sql_load = SQLGenerator.getSelectStatement(cinfo.getMasterTable(),
fields, new FieldInfoIF[] { identity_field }, 0);
if (debug)
// log.debug("Load SQL (1:1) " + field.getName() + ": " + sql_load);
log.debug("Load SQL (1:1) : " + sql_load);
// SELECT id, topic_id FROM TM_TOPIC_SET WHERE id = ?
// -----------------------------------------------------------------------------
// Add
// -----------------------------------------------------------------------------
// Generate SQL statement
sql_add = SQLGenerator.getInsertStatement(cinfo.getMasterTable(), fields);
if (debug)
log.debug("Create SQL (" + cinfo.getDescriptorClass().getName() + "): " + sql_add);
// INSERT INTO TM_TOPIC_SET (id, topic_id) VALUES (?, ?)
// -----------------------------------------------------------------------------
// Remove
// -----------------------------------------------------------------------------
// Generate SQL statement
sql_remove = SQLGenerator.getDeleteStatement(cinfo.getMasterTable(), fields);
if (debug)
log.debug("Create SQL (" + cinfo.getDescriptorClass().getName() + "): " + sql_remove);
// DELETE FROM TM_TOPIC_SET WHERE id = ? and topic_id = ?
// -----------------------------------------------------------------------------
// Delete
// -----------------------------------------------------------------------------
// Generate SQL statement
sql_delete = SQLGenerator.getDeleteStatement(cinfo.getMasterTable(), new FieldInfoIF[] { identity_field });
if (debug)
log.debug("Delete SQL (" + cinfo.getDescriptorClass().getName() + "): " + sql_delete);
// DELETE FROM TM_TOPIC_SET WHERE id = ?
}
// -----------------------------------------------------------------------------
// Load
public boolean load(AccessRegistrarIF registrar, IdentityIF identity) throws Exception {
// Get ticket
TicketIF ticket = registrar.getTicket();
// Prepare statement
PreparedStatement stm = access.prepareStatement(sql_load);
try {
// Bind identity columns
if (debug)
log.debug("Binding object identity: " + identity);
identity_field.bind(identity, stm, 1);
// Execute statement
if (debug)
log.debug("Executing: " + sql_load);
ResultSet rs = stm.executeQuery();
// Initialize collection value
Collection values = new HashSet();
// Load row data (skip identity column)
int rsindex = 1 + identity_field.getColumnCount();
// Load collection values
while (rs.next()) {
values.add(value_field.load(registrar, ticket, rs, rsindex, false));
}
// Register object identity with registrar
registrar.registerIdentity(ticket, identity);
// Update cache
registrar.registerField(ticket, identity, value_field.getIndex(), values);
//! System.out.println("COLLECTION LOADED: " + identity + " " + value_field.getIndex() + " : " + values);
// Close result set
rs.close();
return true;
} finally {
if (stm != null) stm.close();
}
}
// -----------------------------------------------------------------------------
// Load field
public Object loadField(AccessRegistrarIF registrar, IdentityIF identity, int field) {
throw new UnsupportedOperationException("Persistent collections have no fields.");
}
public Object loadFieldMultiple(AccessRegistrarIF registrar, Collection identities,
IdentityIF current, int field) {
throw new UnsupportedOperationException("Persistent collections have no fields.");
}
// -----------------------------------------------------------------------------
// Create
public void create(ObjectAccessIF oaccess, Object object) throws Exception {
// Make sure trackable is in initialized state
TrackableCollectionIF trackcoll = (TrackableCollectionIF)object;
trackcoll.resetTracking();
// Store all collection elements
if (!trackcoll.isEmpty())
storeAdded(oaccess, oaccess.getIdentity(object), trackcoll);
}
// -----------------------------------------------------------------------------
// Delete
public void delete(ObjectAccessIF oaccess, Object object) throws Exception {
// NOTE: Deletes all collection elements.
// Prepare statement
PreparedStatement stm = access.prepareStatement(sql_delete);
try {
// Bind parameters
bindParametersDelete(stm, oaccess.getIdentity(object));
// Execute statement
if (debug) log.debug("Executing: " + sql_delete);
stm.executeUpdate();
} finally {
if (stm != null) stm.close();
}
}
protected void bindParametersDelete(PreparedStatement stm, IdentityIF identity) throws Exception {
// Bind identity columns
if (debug) log.debug("Binding object identity: " + identity);
identity_field.bind(identity, stm, 1);
}
// -----------------------------------------------------------------------------
// Store dirty
public void storeDirty(ObjectAccessIF oaccess, Object object) throws Exception {
// Store changes
TrackableCollectionIF trackcoll = (TrackableCollectionIF)object;
// Store added collection elements
Collection added = trackcoll.getAdded();
if (added != null && !added.isEmpty())
storeAdded(oaccess, oaccess.getIdentity(object), added);
// Store removed collection elements
Collection removed = trackcoll.getRemoved();
if (removed != null && !removed.isEmpty())
storeRemoved(oaccess, oaccess.getIdentity(object), removed);
// Reset trackable collection
trackcoll.resetTracking();
}
// -----------------------------------------------------------------------------
// Add collection elements
protected void storeAdded(ObjectAccessIF oaccess, IdentityIF identity, Collection elements) throws Exception {
// Prepare statement
PreparedStatement stm = access.prepareStatement(sql_add);
//! System.out.println("STORING COLLECTION ELEMENTS: " + identity + " -> " + elements);
// Store elements individually
try {
int size = elements.size();
Iterator iter = elements.iterator();
for (int i=0; i < size; i++) {
// Bind parameters
bindParametersAddRemove(stm, oaccess, identity, iter.next());
// Execute statement
if (debug) log.debug("Executing: " + sql_add);
stm.executeUpdate();
}
} finally {
if (stm != null) stm.close();
}
}
protected void bindParametersAddRemove(PreparedStatement stm, ObjectAccessIF oaccess,
IdentityIF identity, Object element) throws Exception {
// Note: The fields map is modified by removing keys of fields
// that are being inserted at this point. This hack and might be
// optimized in the future.
// Bind identity columns
if (debug) log.debug("Binding object identity: " + identity);
identity_field.bind(identity, stm, 1);
// Bind value columns
int stmindex = 1 + identity_field.getColumnCount();
// Get field value
if (element != null && value_field.isReferenceField())
element = oaccess.getIdentity(element);
// Bind field value
value_field.bind(element, stm, stmindex);
//! System.out.println("BINDING: " + identity + " -> " + element);
// TODO: Reset all dirty flags in one go: ObjectAccessIF.setDirty(false).
}
// -----------------------------------------------------------------------------
// Remove collection elements
protected void storeRemoved(ObjectAccessIF oaccess, IdentityIF identity, Collection elements) throws Exception {
// Prepare statement
PreparedStatement stm = access.prepareStatement(sql_remove);
// Store elements individually
try {
int size = elements.size();
Iterator iter = elements.iterator();
for (int i=0; i < size; i++) {
// Bind parameters
bindParametersAddRemove(stm, oaccess, identity, iter.next());
// Execute statement
if (debug) log.debug("Executing: " + sql_remove);
stm.executeUpdate();
}
} finally {
if (stm != null) stm.close();
}
}
}