All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mplates.1.2.source-code.PojoServiceBase.ftl Maven / Gradle / Ivy

<#include "license.ftl">
<@license/>
<#assign object = doc.object>
package ${object.@package}.service.base;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException;
<#assign stream = "false">
<#list doc["/object/attributes/*"] as att>
    <#if att?node_name == "html">
        <#assign stream = "true">
    <#elseif att?node_name == "string" && att.@maxlength[0]?number > 65000>
        <#assign stream = "true">
    

<#if stream == "true">
import java.io.IOException;
import java.sql.PreparedStatement;

import java.sql.ResultSet;
import java.sql.SQLException;
<#if stream == "true">
import java.sql.Timestamp;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import redora.api.Persistable;
import redora.api.fetch.*;
import redora.exceptions.*;
import redora.service.*;
import ${object.@package}.model.*;
import ${object.@package}.model.fields.*;
import ${object.@package}.service.*;

<#list doc["/object/attributes/enum[@scope='global']"] as att>
import ${object.@package}.model.enums.${att.@class};

<#if object.trashcan == "true">
import redora.configuration.rdo.service.TrashService;


import static ${object.@package}.sql.${object.@name}SQL.*;
import static ${object.@package}.businessrules.${object.@name}BusinessRules.check;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.SEVERE;
import static redora.db.SQLParameter.*;

/**
* You can work with the pojo's using this service. For example if you create command line J2SE functionality
* or server side logic.
* If you want to use your data in a web application, you also might want to look at {@link ${object.@name}ServiceJSON} * and {@link ${object.@package}.service.http.${object.@name}Servlet}.
* Whenever business rule logic needs to be checked, it will go through here. For example a web user wants to persist * some data. He will post this for example as JSON to {@link ${object.@package}.service.http.${object.@name}Servlet} * which in turn invokes {@link ${object.@name}ServiceJSON} which will serialize the JSON stream into a pojo * and then will invoke the persist method of this service. * @author Redora (www.redora.net) */ public class ${object.@name}ServiceBase extends ServiceBase { static final transient Logger l = Logger.getLogger("${object.@package}.service.${object.@name}Service"); /** * Don't use this directly but use the ServiceFactory instead: * * ${object.@name}Service() myService = ServiceFactory.${object.@name?uncap_first}(); * ... do stuff * ServiceFactory.close(myService); * * Initializes ${object.@name}Service. * @see ${object.@package}.service.ServiceFactory */ protected ${object.@name}ServiceBase() throws ConnectException { super("${object.schema[0]!"default"}"); } /** * Chains this service object to another service object. The database connection * and transaction is managed by the other service. * @param chain (Mandatory) Service you want to make this service dependent on. */ protected ${object.@name}ServiceBase(@NotNull ServiceBase chain) { super(chain, "${object.schema[0]!"default"}"); inTransaction = true; //Let the calling service handle the transaction } /** * Simple finder to get ${object.@name} by it's id. * @param id (Mandatory) The primary key. * @param scope (Mandatory) The allowed fetch scope: Scope.Table, Scope.Form, Scope.List. * @return The desired ${object.@name} * @throws ObjectNotFoundException When there is no object in the DB with this Id. */ @NotNull public ${object.@name} findById(@NotNull Long id, @NotNull Scope scope) throws QueryException, CopyException, ObjectNotFoundException { ${object.@name} retVal = null; String SQL; switch (scope) { case Table: SQL = FIND_BY_ID_TABLE; break; case Form: SQL = FIND_BY_ID_FORM; break; case List: SQL = FIND_BY_ID_LIST; break; default: throw new IllegalArgumentException("Illegal scope for this finder: " + scope); } SQL = prepare(SQL, id); ResultSet rs = null; try { rs = sqlQuery(SQL); if (rs.next()) { retVal = new ${object.@name}(rs, 0, scope); } else { throw new ObjectNotFoundException("Could not find ${object.@name} " + id); } } catch(SQLException e) { l.log(SEVERE, "Failed to run query " + SQL, e); throw new QueryException("Failed to run query: " + SQL, e); } finally { close(rs); } return retVal; } /** * Finds records by custom SQL. Custom queries should be defined in the queries section in the model. * @param sql (Mandatory) The SQL statement you wish to use. See also ${object.@name}SQL. * @param params (Optional) List of parameters. When omitted it is assumed there are no parameters. * @param page (Mandatory) The Page strategy. Allowed scopes are: Scope.Table, Scope.Form, Scope.List, Scope.Lazy. * @return Empty or filled list with ${object.@name} */ @NotNull public List<${object.@name}> find(@NotNull String sql, @Nullable List params, @NotNull Page page) throws QueryException, CopyException, PagingException { List<${object.@name}> retVal = new ArrayList<${object.@name}>(); String SQL = sql; if (params != null) { for (Object param : params) { SQL = prepare(SQL, param); } } SQL = preparePage(SQL, page); ResultSet rs = null; try { rs = sqlQuery(SQL); while (rs.next()) { retVal.add(new ${object.@name}(rs, 0, page.getScope())); } } catch(SQLException e) { l.log(SEVERE, "Failed to run query: " + SQL, e); throw new QueryException("Failed to run query: " + SQL, e); } finally { close(rs); } return retVal; } @NotNull public List<${object.@name}> finder(DefaultFinder finder, Object param, Page page) throws QueryException, CopyException, PagingException { if (!(page.getScope() == Scope.List || page.getScope() == Scope.Table)) { throw new PagingException("Illegal scope (" + page.getScope() + ") for this finder: " + finder); } String SQL = page.getScope() == Scope.Table ? finder.sqlTable : finder.sqlList; if (param instanceof List) { return find(page.getScope() == Scope.Table ? finder.sqlTable : finder.sqlList, (List)param, page); } else if (finder != DefaultFinder.FindAll) { SQL = prepare(SQL, param); } return find(SQL, null, page); } /** * Finds All records * @param page (Mandatory) The Page strategy. The allowed fetch scopes are: Scope.Table and Scope.List. * @return Empty or filled list with ${object.@name} */ @NotNull public List<${object.@name}> findAll(@NotNull Page page) throws QueryException, CopyException, PagingException { return finder(DefaultFinder.FindAll, null, page); } <#list doc["/object/attributes/*/finder"] as finder> /** * @param page (Mandatory) Allowed: Scope.Table, Scope.List. * @return Empty or filled list */ @NotNull public List<${object.@name}> ${finder.@name}(@NotNull <#if finder?parent?node_name == "set" || finder?parent?node_name == "object">Long<#else><#if finder?parent?node_name == "enum" && finder?parent.@scope == "local">${object.@name}.${finder?parent.@className} finder, @NotNull Page page) throws QueryException, CopyException, PagingException { if (!(page.getScope() == Scope.List || page.getScope() == Scope.Table)) { throw new PagingException("Illegal scope: " + page.getScope()); } List<${object.@name}> retVal = new ArrayList<${object.@name}>(); String SQL = page.getScope() == Scope.Table ? ${finder.@tableName} : ${finder.@listName}; <#if finder?parent?node_name == "object" || finder?parent?node_name == "long" || finder?parent?node_name == "integer" || finder?parent?node_name == "date" || finder?parent?node_name == "double"> SQL = prepare(SQL, finder); <#elseif finder?parent?node_name == "string"> SQL = prepareDirty(SQL, finder); <#elseif finder?parent?node_name == "enum"> SQL = prepare(SQL, finder.name()); SQL = preparePage(SQL, page); ResultSet rs = null; try { rs = sqlQuery(SQL); while (rs.next()) { retVal.add(new ${object.@name}(rs, 0, page.getScope())); } } catch(SQLException e) { l.log(SEVERE, "Failed to run query " + SQL, e); throw new QueryException("Failed to run query: " + SQL, e); } finally { close(rs); } return retVal; } <#if finder?parent?node_name == "object" && object.trashcan == "true"> /** * Trashes all ${object.@name}s without checking on business rules. This is usually invoked * by the related parent which has already checked business rule violations. * @param id (Mandatory) Related object id */ public void trashBy${finder?parent.@fieldName?cap_first}Id(@NotNull Long id, boolean undo, boolean asBatch) throws QueryException<#if finder?parent.@parentClass[0]??>, PersistException, CopyException { String SQL = prepare(TRASH_BY_${finder?parent.@fieldName?upper_case}, !undo); SQL = prepare(SQL, id); execute(SQL); } <#if object.lazyScope[0]??> /** * Fetches all lazy=true attributes. Lazy relationships and objects are not fetched. * This means the difference between Table and Form scope is fetched. * This method does not exist if there are no lazy fetchable attributes for ${object.@name}. * @param id Id of te lazy object * @return Null or filled map with lazy objects */ @Nullable public Map fetchLazy(@NotNull Long id) throws LazyException { Map map = null; String SQL = prepare(FETCH_LAZY_BY_ID, id); ResultSet rs = null; try { rs = sqlQuery(SQL); if (rs.next()) { map = ${object.@name}Util.copyLazy(rs, 0); } else { throw new LazyException("Could not find ${object.@name} " + SQL); } } catch(SQLException e) { l.log(SEVERE, "Failed to run query " + SQL, e); throw new LazyException("Failed to run query: " + SQL, e); } catch(CopyException e) { l.log(SEVERE, "Failed to copy results " + SQL, e); throw new LazyException("Failed to copy results " + SQL, e); } finally { close(rs); } return map; } <#list doc["/object/attributes/set[@multiplicity='n-to-m']"] as att> /** * Removes the relationship with ${att.@class} by deleting a record in the ${att.@relationTableName} table. * @param ${att.@myName}Id (Mandatory) 'My' side of relationship * @param ${att.@theirName}Id (Mandatory) 'Their' side of the relationship */ public void deleteRelationWith${att.@class}(@NotNull Long ${att.@myName}Id, @NotNull Long ${att.@theirName}Id , boolean asBatch) throws QueryException { String SQL = prepare(DELETE_${att.@relationTableName?upper_case}_RELATION, ${att.@myName}Id); SQL = prepare(SQL, ${att.@theirName}Id); execute(SQL); } <#if object.trashcan == "true"> /** * Moves the relationship with ${att.@class} to the trash can. * @param ${att.@myName}Id (Mandatory) 'My' side of relationship * @param ${att.@theirName}Id (Mandatory) 'Their' side of the relationship * @param undo True to undo it instead of trash */ protected void trashRelationWith${att.@class}(@NotNull Long ${att.@myName}Id , @NotNull Long ${att.@theirName}Id, boolean asBatch, boolean undo) throws QueryException { String SQL = prepare(TRASH_${att.@relationTableName?upper_case}_RELATION, !undo); SQL = prepare(SQL, ${att.@myName}Id); SQL = prepare(SQL, ${att.@theirName}Id); execute(SQL); } /** * Moves all relationships with ${att.@class} to the trash can. * @param ${att.@myName}Id (Mandatory) 'My' side of relationship * @param undo True will undo all trashed relations */ protected void trashAllRelationsWith${att.@class}(@NotNull Long ${att.@myName}Id, boolean undo, boolean asBatch) throws QueryException { String SQL = prepare(TRASH_${att.@relationTableName?upper_case}_RELATION_BY_${att.@myName?upper_case}_ID, !undo); SQL = prepare(SQL, ${att.@myName}Id); execute(SQL); } /** * Creates a relationship with {att.@class} by creating a record in the ${att.@relationTableName} table. * @param ${att.@myName}Id (Mandatory) 'My' side of relationship * @param ${att.@theirName}Id (Mandatory) 'Their' side of the relationship <#if att.@sorted == "both" || att.@sorted == "them"> * @param sortOrder Position in the sorted relation */ public void connectTo${att.@class}(@NotNull Long ${att.@myName}Id, @NotNull Long ${att.@theirName}Id<#if att.@sorted == "both" || att.@sorted == "them">, int sortOrder, boolean asBatch) throws QueryException, CopyException { String SQL = prepare(INSERT_${att.@relationTableName?upper_case}_RELATION, ${att.@myName}Id); SQL = prepare(SQL, ${att.@theirName}Id); <#if att.@sorted == "both" || att.@sorted == "them"> SQL = prepare(SQL, sortOrder); execute(SQL); } <#if att.@sorted == "both" || att.@sorted == "them"> /** * Re-orders a sortable relationship with {att.@class} by updating the sortOrder in the ${att.@relationTableName} table. * @param ${att.@myName}Id (Mandatory) 'My' side of relationship * @param ${att.@theirName}Id (Mandatory) 'Their' side of the relationship * @param sortOrder New position in the sorted relation */ public void reconnectTo${att.@class}(@NotNull Long ${att.@myName}Id, @NotNull Long ${att.@theirName}Id, int sortOrder) throws QueryException, CopyException { String SQL = prepare(UPDATE_${att.@relationTableName?upper_case}_RELATION, sortOrder); SQL = prepare(SQL, ${att.@myName}Id); SQL = prepare(SQL, ${att.@theirName}Id); execute(SQL); } /** * Count the connections in the relationship with {att.@class}. * @param ${att.@myName}Id (Mandatory) 'Our' side object id * @return 0 or more! */ public int countConnectionsTo${att.@class}(@NotNull Long ${att.@myName}Id) throws PersistException, QueryException, CopyException { String SQL = prepare(COUNT_${att.@relationTableName?upper_case}_RELATION, ${att.@myName}Id); int retVal; ResultSet rs = null; try { rs = sqlQuery(SQL); rs.next(); retVal = rs.getInt(1); } catch(SQLException e) { l.log(SEVERE, "Failed to run query " + SQL, e); throw new QueryException("Failed to run query: " + SQL, e); } finally { close(rs); } return retVal; } /** * Inserts or updates a collection of ${object.@name}. * Collections associated to ${object.@name} are persisted as well. * JDBC batch update is used to improve performance. * NOTE that this method assumes you already checked if given ${object.plural} * have changed and if they did not violate any business rules. * @param pojos List of pojo objects you want to persist * @return Empty or filled list with violations. Note that (at least the unique * key) is only evaluated in the database. */ @NotNull public Set persist(@NotNull Collection<${object.@name}> pojos) throws RedoraException { Set retVal = new HashSet(); boolean transact = !inTransaction; if (transact) { beginTransaction(); } try { for (${object.@name} pojo : pojos) retVal.addAll(persist(pojo)); } catch (RedoraException e) { if (transact) { transact = false; rollback(); } throw e; } finally { if (transact) if (retVal.isEmpty()) commit(); else rollback(); } return retVal; } /** * Inserts or updates ${object.@name}. Collections associated to ${object.@name} are persisted as well. * @param pojo (Mandatory) Object you want to persist * @return Empty list when OK, else a list of violations. */ @NotNull public Set persist(@NotNull ${object.@name} pojo) throws RedoraException { if (!pojo.isDirty()) { return new HashSet(); } Set br = check(pojo, pojo.isNew ? BusinessRuleViolation.Action.Insert : BusinessRuleViolation.Action.Update); if (!br.isEmpty()) { return br; } return persist(pojo, false); } /** * Persists the changes of ${object.@name} to the database and also any changed related children. * Business rules are not checked, that is why this method is protected. However, unique key * constrains are checked in the database, so this type of business rule is checked. * When a unique key constraint is violated, the transaction is rolled back and a BusinessRuleViolation * with the violating attribute is returned. If you have set up a transaction yourself (with service.beginTransaction()), * you are supposed to rollback (or commit) yourself.
* The persist function 'knows' the changes in ${object.@name} because of the dirty set in this * object. All the setters in ${object.@name} will set the object to dirty when they notice a change * in value.
<#if doc["/object/attributes/set"][0]??> * Persist will be performed in two actions. First it will persist the ${object.@name}, * using {@link #soloPersist(${object.@name})}. Then it will persist related children and parents * using {@link #familyPersist(${object.@name}, boolean, boolean)}. The whole action is wrapped in a transaction and will be rolled back * if somewhere something has failed. * @see #familyPersist(${object.@name}, boolean, boolean) * @see #soloPersist(${object.@name}) * @param asBatch Set to true if you want to execute the statements outside this method. You need to invoke ps.executeBatch() yourself. * @return Empty when OK or filled with a unique key constraint violation * @throws PersistException When the action through JDBC / DB has an unexpected problem. */ @NotNull protected Set persist(@NotNull ${object.@name} pojo, boolean asBatch) throws RedoraException { l.log(FINE, "Updating {0}", pojo.getId()); if (pojo.fetchScope == Scope.List) { throw new PersistException("Modification is not allowed, this object is fetched as Scope.List and cannot be persisted. Fetch the object with Table or Form scope instead."); } final boolean isNew = pojo.isNew; Set retVal = null; <#if doc["/object/attributes/set"][0]??> boolean transact = !inTransaction; //only commit/rollback when you are not already in a transaction if (transact) { beginTransaction(); } try { retVal = soloPersist(pojo); <#if doc["/object/attributes/set"][0]??> if (retVal.isEmpty()) { retVal = familyPersist(pojo, isNew, asBatch); } } catch (RedoraException e) { if (transact) { transact = false; rollback(); } throw e; } finally { if (transact) { if (retVal != null && retVal.isEmpty()) { commit(); } else { rollback(); } } } return retVal; } /** * Persists ${object.@name} only. Ignores the children. Does not perform business rule * violation checking. So, know what you are doing when using this. * @param pojo Object you'd wish persisted * @return Empty when OK, or filled with unique key violation. */ @NotNull public Set soloPersist(@NotNull ${object.@name} pojo) throws RedoraException { Set retVal = new HashSet(); if (!pojo.dirty.isEmpty() || pojo.isNew) { String SQL; if (pojo.isNew) { pojo.dirty.put(${object.@name}Fields.creationDate, null); pojo.creationDate = new Date((new Date().getTime()/1000)*1000); //Strip nanoseconds, they are not persisted in MySQL StringBuilder sqlInsert = new StringBuilder("insert into `${object.@name}` ("); char comma = ' '; for (${object.@name}Fields field : pojo.dirty.keySet()) { sqlInsert.append(comma); sqlInsert.append(field.name()); comma = ','; } sqlInsert.append(") values ("); comma = ' '; for (int i = 0; i < pojo.dirty.size(); i++) { sqlInsert.append(comma); sqlInsert.append('?'); comma = ','; } sqlInsert.append(')'); SQL = sqlInsert.toString(); } else { pojo.dirty.put(${object.@name}Fields.updateDate, pojo.updateDate); pojo.updateDate = new Date((new Date().getTime()/1000)*1000); //Strip nanoseconds, they are not persisted in MySQL StringBuilder sqlUpdate = new StringBuilder("update `${object.@name}` set "); char comma = ' '; for (${object.@name}Fields field : pojo.dirty.keySet()) { sqlUpdate.append(comma); sqlUpdate.append(field.name()).append("=?"); comma = ','; } sqlUpdate.append(" where id=?"); SQL = sqlUpdate.toString(); } <#if stream == "true"> boolean usePreparedStatement = false; //Determine if there is a streaming parameter. If yes, use PreparedStatement. for (${object.@name}Fields field : pojo.dirty.keySet()) { <#list doc["/object/attributes/*"] as att> <#if att?node_name == "html"> if (field == ${object.@name}Fields.${att.@fieldName}) { usePreparedStatement = true; break; } <#elseif att?node_name == "string" && att.@maxlength[0]?number > 65000> if (field == ${object.@name}Fields.${att.@fieldName}) { usePreparedStatement = true; break; } } <#if stream == "true"> PreparedStatement ps = null; int position = 1; if (usePreparedStatement) { try { ps = st.con.con.prepareStatement(SQL, java.sql.Statement.RETURN_GENERATED_KEYS); for (${object.@name}Fields field : pojo.dirty.keySet()) { switch (field) { case updateDate: ps.setTimestamp(position++, new Timestamp(pojo.updateDate.getTime())); break; case creationDate: ps.setTimestamp(position++, new Timestamp(pojo.creationDate.getTime())); break; <#list object.attributes?children as att> <#if att?node_name != "set" && att?node_name != "object" && att?node_type == "element"> case ${att.@fieldName}: <#if att.@notnull == "false"> if (pojo.${att.@fieldName} == null) { ps.setNull(position++, ${object.@name}Fields.${att.@fieldName}.sqlType); } else { <#if att?node_name="boolean"> ps.setBoolean(position++, pojo.${att.@fieldName}.booleanValue()); <#elseif att?node_name == "date"> ps.setDate(position++, new java.sql.Date(pojo.${att.@fieldName}.getTime())); <#elseif att?node_name == "datetime"> ps.setTimestamp(position++, new Timestamp(pojo.${att.@fieldName}.getTime())); <#elseif att?node_name == "enum"> ps.setString(position++, pojo.${att.@fieldName}.name()); <#elseif att?node_name == "integer"> ps.setInt(position++, pojo.${att.@fieldName}); <#elseif att?node_name == "long"> ps.setLong(position++, pojo.${att.@fieldName}); <#elseif att?node_name == "double"> ps.setDouble(position++, pojo.${att.@fieldName}); <#elseif att?node_name == "string" || att?node_name == "html"> <#if att?node_name == "html" || att.@maxlength[0]?number > 65000> ps.setBinaryStream(position++, org.apache.commons.io.IOUtils.toInputStream(pojo.${att.@fieldName},"UTF-8")); <#else> ps.setString(position++, pojo.${att.@fieldName}); <#else> ERROR undefined type ${att?node_name} <#if att.@notnull == "false"> } break; } //end switch } } catch (SQLException e) { l.log(SEVERE, "Did not set field at position: " + position + " - " + SQL, e); try {if (ps != null) ps.close();} catch (SQLException esql) {} throw new PersistException("Did not set field at position: " + position + " - " + SQL, e); } catch (IOException e) { l.log(SEVERE, "Can't stream field at position: " + position + " - " + SQL, e); try {if (ps != null) ps.close();} catch (SQLException esql) {} throw new PersistException("Can't stream field at position: " + position + " - " + SQL, e); } } else { for (${object.@name}Fields field : pojo.dirty.keySet()) { switch (field) { case updateDate: SQL = prepareTime(SQL, pojo.updateDate); break; case creationDate: SQL = prepareTime(SQL, pojo.creationDate); break; <#list object.attributes?children as att> <#if att?node_name != "set" && att?node_name != "object" && att?node_type == "element" && (att?node_name != "string" || att.@maxlength[0]?number < 65001)> case ${att.@fieldName}: <#if att.@notnull == "false"> if (pojo.${att.@fieldName} == null) { SQL = prepareNull(SQL); } else { <#if att?node_name="boolean" || att?node_name == "integer" || att?node_name == "long" || att?node_name == "date" || att?node_name == "double"> SQL = prepare(SQL, pojo.${att.@fieldName}); <#elseif att?node_name == "datetime"> SQL = prepareTime(SQL, pojo.${att.@fieldName}); <#elseif att?node_name == "enum"> SQL = prepare(SQL, pojo.${att.@fieldName}.name()); <#elseif att?node_name == "string"> SQL = prepareDirty(SQL, pojo.${att.@fieldName}); <#if att.@notnull == "false"> } break; } //switch } <#if stream == "true"> } ResultSet rs = null; try { <#if stream == "true"> if (usePreparedStatement) { if (!pojo.isNew) { ps.setLong(position, pojo.getId()); //sets where id = ? } ps.execute(); if (ps.getWarnings() != null) { throw new PersistException("${object.@name} was not persisted " + ps.getWarnings().getMessage()); } if (pojo.isNew) { rs = ps.getGeneratedKeys(); rs.next(); pojo.id = rs.getLong(1); } } else { if (pojo.isNew) { st.st.execute(SQL, java.sql.Statement.RETURN_GENERATED_KEYS); if (st.st.getWarnings() != null) { throw new PersistException("${object.@name} was not persisted " + st.st.getWarnings().getMessage()); } rs = st.st.getGeneratedKeys(); rs.next(); pojo.id = rs.getLong(1); } else { SQL = prepare(SQL, pojo.getId()); execute(SQL); } <#if stream == "true"> } pojo.dirty.clear(); pojo.isNew = false; } catch (MySQLIntegrityConstraintViolationException e) { int start = e.toString().indexOf("for key '"); if (start > 0) { String indexName = e.toString().substring(start + 9); indexName = indexName.substring(0, indexName.indexOf("'")); retVal.add(new BusinessRuleViolation( pojo, ${object.@name}Fields.valueOf(uniqueKeyAttribute("${object.@name}", indexName)), BusinessRuleViolation.StandardRule.UniqueKey.ruleId, pojo.getId() == null?BusinessRuleViolation.Action.Insert:BusinessRuleViolation.Action.Update) ); } else { retVal.add(new BusinessRuleViolation( pojo, null, BusinessRuleViolation.StandardRule.UniqueKey.ruleId, pojo.getId() == null?BusinessRuleViolation.Action.Insert:BusinessRuleViolation.Action.Update) ); } } catch(SQLException e) { l.log(SEVERE, "Failed to perform persist: " + SQL, e); throw new PersistException("Failed to perform persist: " + SQL, e); } finally { close(rs); <#if stream == "true"> try {if (ps != null) ps.close();} catch (SQLException esql) {} } } return retVal; } <#if doc["/object/attributes/set"][0]??> /** * Goes through dirty children and parents, and persists them. Use only when * you know what you are doing. * @param pojo (Mandatory) Object from whom you wish to save the children * @return Empty when OK, or filled with unique key constraints violations */ @NotNull public Set familyPersist(@NotNull ${object.@name} pojo, boolean wasNew, boolean asBatch) throws RedoraException { Set retVal = new HashSet(); <#list object.attributes.set as att> // ${att.@plural?cap_first} (${att.@multiplicity}) actions, sorted: ${att.@sorted} <#if att.@multiplicity == "n-to-m"> if (pojo.${att.@fieldName}IsRetrieved()) { if (!wasNew) { for (${att.@class} delete : pojo.get${att.@plural?cap_first}().getRemovedObjects()) { deleteRelationWith${att.@class}(pojo.getId(), delete.getId(), asBatch); } } <#if att.@sorted == "both" || att.@sorted == "them"> int sort = 0; for (${att.@class} list : pojo.get${att.@fieldName?cap_first}()) { if (list.isNew || list.isDirty()) { retVal.addAll(<#if att.@class != object.@name>new ${att.@class}Service(this).persist(list, asBatch)); } if (pojo.get${att.@fieldName?cap_first}().getAddedObjects().contains(list)) { <#if att.@sorted == "both" || att.@sorted == "them"> connectTo${att.@class}(pojo.getId(), list.getId(), sort, asBatch); <#else> connectTo${att.@class}(pojo.getId(), list.getId(), asBatch); <#if att.@sorted == "both" || att.@sorted == "them"> } else if (pojo.get${att.@fieldName?cap_first}().hasShuffled()) { reconnectTo${att.@class}(pojo.getId(), list.getId(), sort); } <#if att.@sorted == "both" || att.@sorted == "them"> sort++; } pojo.get${att.@plural?cap_first}().reset(); } <#else> <#assign finderName = att.@myName> <#if att.@class == object.@name> //pigs ear <#assign finderName = att.@theirName> if (pojo.${att.@fieldName}IsRetrieved()) { if (!wasNew && !pojo.get${att.@fieldName?cap_first}().getRemovedObjects().isEmpty()) { for (${att.@class} delete : pojo.get${att.@plural?cap_first}().getRemovedObjects()) { <#if att.@class != object.@name>new ${att.@class}Service(this).delete(delete); } } if (pojo.get${att.@plural?cap_first}().isDirty(new HashSet())) { List<${att.@class}> update${att.@plural?cap_first} = new ArrayList<${att.@class}>(); for (${att.@class} child : pojo.get${att.@plural?cap_first}()) { if (child.isDirty()) { update${att.@plural?cap_first}.add(child); child.set${finderName?cap_first}(pojo); } } for (${att.@class} child : pojo.get${att.@plural?cap_first}().getAddedObjects()) { if (!child.isDirty()) { //Dirty is already added before update${att.@plural?cap_first}.add(child); child.set${finderName?cap_first}(pojo); } } if (!update${att.@plural?cap_first}.isEmpty()) { retVal.addAll(<#if att.@class != object.@name>new ${att.@class}Service(this).persist(update${att.@plural?cap_first})); } pojo.get${att.@plural?cap_first}().reset(); } } return retVal; } /** * Deletes ${object.@name}. * Collections associated to ${object.@name} are deleted as well when cascade delete is indicated. * @param pojo (Mandatory) ${object.@name} you want to delete. * @return Empty when OK, or filled set with violated business rules * @throws ObjectNotFoundException when ${object.@name} has no id (was never persisted). */ @NotNull public Set delete(@NotNull ${object.@name} pojo) throws RedoraException { if (pojo.getId() == null) { throw new ObjectNotFoundException("You are trying to delete a ${object.@name} that is not in the database"); } Set br = check(pojo, BusinessRuleViolation.Action.Delete); if (br.isEmpty()) { delete(pojo, false); } return br; } /** Deletes ${object.@name} without checking on business rule violations. */ protected void delete(${object.@name} pojo, boolean asBatch) throws QueryException { l.log(FINE, "Deleting {0}", pojo.getId()); String SQL = prepare(DELETE, pojo.getId()); execute(SQL); } <#if object.trashcan == "true"> /** * Finds trashed ${object.@name}s. * @param ${object.@name?uncap_first}Id (Optional) if set only a specific trashed object is retrieved. * @return Empty or filled list with results */ @NotNull public List<${object.@name}> findTrash(@Nullable Long ${object.@name?uncap_first}Id) throws QueryException, CopyException { List<${object.@name}> retVal = new ArrayList<${object.@name}>(); String SQL = ${object.@name?uncap_first}Id == null ? FIND_TRASH : FIND_TRASH_BY_ID; if (${object.@name?uncap_first}Id != null) { SQL = prepare(SQL, ${object.@name?uncap_first}Id); } ResultSet rs = null; try { rs = sqlQuery(SQL); while (rs.next()) { retVal.add(new ${object.@name}(rs, 0, ${object.@name?uncap_first}Id == null ? Scope.Table : Scope.Form)); } } catch(SQLException e) { l.log(SEVERE, "Failed to find trash " + SQL, e); throw new QueryException("Failed to find trash " + SQL, e); } finally { close(rs); } return retVal; } /** * Trashes ${object.@name}. * Collections associated to ${object.@name} are trashed as well when cascade delete is indicated. * @param undoHash (Optional) When provided, ${object.@name} will be tashed with given hash, otherwise a new hash will be calculated and returned. * @param pojo (Mandatory) The ${object.@name} you want to trash. * @return Empty when OK, or set with violated business rules. * @throws ObjectNotFoundException When ${object.@name} was not persisted. */ @NotNull public Set trash(@Nullable String undoHash, @NotNull ${object.@name} pojo) throws RedoraException { if (pojo.getId() == null) { throw new ObjectNotFoundException("You are trying to delete a ${object.@name} that is not in the database"); } Set br = check(pojo, BusinessRuleViolation.Action.Delete); if (br.isEmpty()) { TrashService.trash(undoHash, pojo.getId(), "${object.@name}"); trash(pojo, false, false); } return br; } /** * Restores a trashed ${object.@name} and also restored possible trashed children. * @param ${object.@name?uncap_first}Id (Mandatory) Id of to be retrieved object * @throws QueryException When ${object.@name} was not found (or was untrashed before). */ public void undo(Long ${object.@name?uncap_first}Id) throws RedoraException { List<${object.@name}> trash = findTrash(${object.@name?uncap_first}Id); if (trash.isEmpty()) { throw new QueryException("Could not find trashed ${object.@name}, maybe it was already undeleted."); } trash(trash.get(0), false, true); TrashService.deleteTrash(trash.get(0).getId(), "${object.@name}"); } /** Trashes ${object.@name} without checking on business rule violations. */ private void trash(${object.@name} pojo, boolean asBatch, boolean undo) throws RedoraException { l.log(FINE, "Trashing {0}", pojo.getId()); String SQL = prepare(TRASH, !undo); SQL = prepare(SQL, pojo.getId()); execute(SQL); <#list object.attributes.set as att> <#if att.@multiplicity == "n-to-m"> if (undo || !pojo.get${att.@plural?cap_first}().isEmpty()) { //trashAllRelationsWith${att.@class}(pojo.getId(), undo, asBatch); } <#elseif att.@cascade == "true"> <#assign finderName = att.@myName> <#if att.@class == object.@name> <#assign finderName = att.@theirName> if (undo || !pojo.get${att.@plural?cap_first}().isEmpty()) { <#if att.@class != object.@name>new ${att.@class}Service(this).trashBy${finderName?cap_first}Id(pojo.getId(), undo, asBatch); } } /** * Simple test method. It will dump the results of findAll to System.out * @param args Unused * @throws RedoraException Passing on */ public static void main(String[] args) throws RedoraException { ${object.@name}Service _${object.@name?uncap_first}Service = ServiceFactory.${object.@name?uncap_first}Service(); for (${object.@name} testRecord : _${object.@name?uncap_first}Service.findAll(new Page(Scope.Table, Mode.Page, 100))) { <#list object.tableScope?children as att> <#if att?node_name != "object" && att?node_type == "element"> if (testRecord.${att.@fieldName} != null) { System.out.print(testRecord.${att.@fieldName}); } else { System.out.print("NULL"); } System.out.print("-"); System.out.println(); } ServiceFactory.close(_${object.@name?uncap_first}Service); } }