I have an application that works with objects, that are linked in many ways among each other.
What is the best way to save them?
I try to to it by long transactions, because the Objects are used while the whole application
and and i don't want to hold one transaction all the time to hold them persistent.
Its a multi-user web-application and i can't be sure, that the conection will be closed correctly.
So i just open a connection, when some objects have to be read/saved/updated and want to
save them by long transactions. When i do this:
db.setAutoStore(false);
db.begin();
db.update(myObject);
db.commit();
i get the error:
Commit failed: org.exolab.castor.jdo.TransactionAbortedException: Nested error:
org.exolab.castor.jdo.PersistenceException: Object, com.sourcepark.ticketsystem.Ticket@305236,
links to another object, com.sourcepark.ticketsystem.Priority@6a0105 that is not
loaded/updated/created in this transaction
If i set db.setAutoStore(true) i get the error:
Error while saving object: org.exolab.castor.jdo.DuplicateIdentityException: update object
which is already in the transaction
because i use some objects in many ways, i.e. the object 'state' can be a pre-state and a
successor-state for another object and there are many other relations like this. I've tried
to set the affected objects to access="read-only" in mapping.xml but without success.
In the JDO-FAQ is written, that castor only supports bi-directional relationships, does
that mean, if object1 contains a reference to object2, object2 should contain a vector
of object1's?
I expanded my mapping.xml, but now castor works very slowly, when i load a single
user, the whole datamodel is loaded from DB cause all objects are linked among each other.
Is it usefull to use castor for a web-application with complex datamodel? Whats about
performance when there are many users and large databases. Could lazy-loading solve this
problem?
I've appended the whole mapping.xml for getting an impression about the object-structute.
My main question is, if it's advisable to use castor for a multi-user web-application with
such a datamodel and if yes, what is the most performance-friendly approach to save and
load the objects.
Thanks in advance,
Steffen
<?xml version="1.0"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Object Mapping DTD Version 1.0//EN"
"http://castor.exolab.org/mapping.dtd">
<mapping>
<!--/////////////////// Mapping for Person //////////////////////////-->
<class name="com.sourcepark.access.Person" identity="id" key-generator="MAX">
<cache-type type="unlimited"/>
<map-to table="PERSON" xml="person" />
<field name="id" type="integer" >
<sql name="ID" type="integer"/>
<xml node="attribute"/>
</field>
<field name="firstName" type="string">
<sql name="FIRST_NAME" type="char" dirty="check" />
<xml node="text" />
</field>
<field name="lastName" type="string">
<sql name="LAST_NAME" type="char" dirty="check" />
<xml node="text" />
</field>
<field name="users" type="com.sourcepark.access.User" collection="vector"
set-method="setUsersAsReference" get-method="getUsersAsReference">
<sql many-key="PERSON_ID"/>
</field>
</class>
<!--/////////////////// Mapping for User ////////////////////////////-->
<class name="com.sourcepark.access.User" identity="id" key-generator="MAX">
<cache-type type="unlimited"/>
<map-to table="USERS"/>
<field name="id" type="integer" >
<sql name="ID" type="integer"/>
</field>
<field name="person" type="com.sourcepark.access.Person" required="true">
<sql name="PERSON_ID"/>
</field>
<field name="name" type="string">
<sql name="NAME" type="char" required="true"/>
</field>
<field name="password" type="string">
<sql name="PASSWORD" type="char" required="true"/>
</field>
<field name="roles" type="com.sourcepark.workflows.Role" collection="vector"
set-method="setRolesAsReference" get-method="getRolesAsReference">
<sql name="ROLE" many-table="ROLES_USERS" many-key="USERS" />
</field>
<field name="userGroups" type="com.sourcepark.access.UserGroup" collection="vector"
set-method="setUserGroupsAsReference" get-method="getUserGroupsAsReference">
<sql name="USERGROUP" many-table="USER_TO_USERGROUPS" many-key="USERID" />
</field>
<field name="events" type="com.sourcepark.ticketsystem.Event" collection="vector"
set-method="setEventsAsReference" get-method="getEventsAsReference">
<sql many-key="USER_ID"/>
</field>
<field name="recipientTickets" type="com.sourcepark.ticketsystem.Ticket" collection="vector"
set-method="setRecipientTicketsAsReference" get-method="getRecipientTicketsAsReference">
<sql many-key="RECIPIENT"/>
</field>
<field name="transactorTickets" type="com.sourcepark.ticketsystem.Ticket" collection="vector"
set-method="setTransactorTicketsAsReference" get-method="getTransactorTicketsAsReference">
<sql many-key="TRANSACTOR"/>
</field>
</class>
<!--/////////////////// Mapping for UserGroup //////////////////////-->
<class name="com.sourcepark.access.UserGroup" identity="id" key-generator="MAX">
<cache-type type="unlimited"/>
<map-to table="USERGROUPS" xml="userGroups" />
<field name="id" type="integer" >
<sql name="ID" type="integer"/>
<xml node="attribute"/>
</field>
<field name="name" type="string">
<sql name="NAME" type="char" dirty="check" />
<xml node="text" />
</field>
<field name="users" type="com.sourcepark.access.User" collection="vector"
set-method="setUsersAsReference" get-method="getUsersAsReference">
<sql name="USERID" many-table="USER_TO_USERGROUPS" many-key="USERGROUP" />
</field>
</class>
<!--/////////////////// Mapping for Project ////////////////////////-->
<class name="com.sourcepark.projects.Project" identity="id" key-generator="MAX">
<cache-type type="unlimited"/>
<map-to table="PROJECTS" xml="projects" />
<field name="id" type="integer" >
<sql name="ID" type="integer"/>
<xml node="attribute"/>
</field>
<!--________________NAME______________________-->
<field name="name" type="string">
<sql name="NAME" type="char" dirty="check" />
<xml node="text" />
</field>
<!--________________SUPER_PROJECT________________________-->
<field name="superProjects" type="com.sourcepark.projects.Project" collection="vector"
set-method="setSuperProjectsAsReference" get-method="getSuperProjectsAsReference">
<sql name="SUPERPROJECT_ID" many-table="PROJECT_SUPERPROJECT" many-key="PROJECT_ID" />
</field>
<field name="tickets" type="com.sourcepark.ticketsystem.Ticket" collection="vector"
set-method="setTicketsAsReference" get-method="getTicketsAsReference">
<sql many-key="PROJECT"/>
</field>
</class>
<!--/////////////////// Mapping for Ticket///////////////////////////-->
<class name="com.sourcepark.ticketsystem.Ticket" identity="id" key-generator="MAX">
<cache-type type="unlimited"/>
<map-to table="TICKET" xml="ticket" />
<field name="id" type="integer" >
<sql name="ID" type="integer"/>
<xml node="attribute"/>
</field>
<field name="name" type="string">
<sql name="NAME" type="char"/>
<xml node="text" />
</field>
<field name="priority" type="com.sourcepark.ticketsystem.Priority">
<sql name="PRIORITY"/>
</field>
<field name="type" type="com.sourcepark.ticketsystem.Type">
<sql name="TYPE" read-only="true" dirty="ignore"/>
</field>
<field name="recipient" type="com.sourcepark.access.User">
<sql name="RECIPIENT"/>
</field>
<field name="recipientType" type="integer">
<sql name="RECIPIENT_TYPE" type="integer"/>
</field>
<field name="state" type="com.sourcepark.workflows.State">
<sql name="STATE"/>
</field>
<field name="project" type="com.sourcepark.projects.Project">
<sql name="PROJECT"/>
</field>
<field name="transactor" type="com.sourcepark.access.User">
<sql name="TRANSACTOR"/>
</field>
<field name="workflow" type="com.sourcepark.workflows.Workflow">
<sql name="WORKFLOW"/>
</field>
<field name="deadline" type="string">
<sql name="DEADLINE" type="char"/>
</field>
<field name="description" type="string">
<sql name="DESCRIPTION" type="char"/>
</field>
<field name="events" type="com.sourcepark.ticketsystem.Event" collection="vector"
set-method="setEventsAsReference" get-method="getEventsAsReference">
<sql many-key="TICKET_ID"/>
</field>
</class>
<!--/////////////////// Mapping for Event ///////////////////////////-->
<class name="com.sourcepark.ticketsystem.Event" depends="com.sourcepark.ticketsystem.Ticket" identity="id" key-generator="MAX">
<cache-type type="unlimited"/>
<map-to table="EVENTS" xml="events" />
<field name="id" type="integer" >
<sql name="ID" type="numeric"/><!--type="integer"-->
</field>
<field name="ticket" type="com.sourcepark.ticketsystem.Ticket" >
<sql name="TICKET_ID"/>
</field>
<field name="user" type="com.sourcepark.access.User" >
<sql name="USER_ID"/>
</field>
<field name="day" type="string">
<sql name="DAY" type="char" dirty="check" />
</field>
<field name="state" type="com.sourcepark.workflows.State" >
<sql name="STATE" read-only="true" dirty="ignore"/>
</field>
</class>
<!--/////////////////// Mapping for Type ////////////////////////-->
<class name="com.sourcepark.ticketsystem.Type" identity="id" key-generator="MAX">
<cache-type type="unlimited"/>
<map-to table="TYPES" xml="type" />
<field name="id" type="integer" >
<sql name="ID" type="integer"/>
<xml node="attribute"/>
</field>
<field name="name" type="string">
<sql name="NAME" type="char" dirty="check" />
<xml node="text" />
</field>
<field name="workflow" type="com.sourcepark.workflows.Workflow" >
<sql name="WORKFLOW"/>
</field>
<field name="tickets" type="com.sourcepark.ticketsystem.Ticket" collection="vector"
set-method="setTicketsAsReference" get-method="getTicketsAsReference">
<sql many-key="TYPE"/>
</field>
</class>
<!--/////////////////// Mapping for PRIORITY ////////////////////////-->
<class name="com.sourcepark.ticketsystem.Priority" identity="id" key-generator="MAX">
<cache-type type="unlimited"/>
<map-to table="PRIORITIES" xml="priority" />
<field name="id" type="integer" >
<sql name="ID" type="integer"/>
<xml node="attribute"/>
</field>
<field name="name" type="string">
<sql name="NAME" type="char" dirty="check" />
<xml node="text" />
</field>
<field name="workflow" type="com.sourcepark.workflows.Workflow" >
<sql name="WORKFLOW"/>
<xml node="attribute"/>
</field>
<field name="tickets" type="com.sourcepark.ticketsystem.Ticket" collection="vector"
set-method="setTicketsAsReference" get-method="getTicketsAsReference">
<sql many-key="PRIORITY"/>
</field>
</class>
<!--/////////////////// Mapping for Workflow /////////////////////////-->
<class name="com.sourcepark.workflows.Workflow" identity="id" key-generator="MAX">
<cache-type type="unlimited"/>
<map-to table="WORKFLOWS" xml="workflows" />
<field name="id" type="integer" >
<sql name="ID" type="integer"/>
<xml node="attribute"/>
</field>
<field name="name" type="string">
<sql name="NAME" type="char" dirty="check" />
<xml node="text" />
</field>
<field name="states" type="com.sourcepark.workflows.State" collection="vector"
set-method="setStatesAsReference" get-method="getStatesAsReference">
<sql many-key="WORKFLOW" read-only="true" dirty="ignore"/>
</field>
<field name="sequences" type="com.sourcepark.workflows.Sequence" collection="vector"
set-method="setSequencesAsReference" get-method="getSequencesAsReference">
<sql many-key="WORKFLOW" read-only="true"/>
</field>
<field name="roles" type="com.sourcepark.workflows.Role" collection="vector"
set-method="setRolesAsReference" get-method="getRolesAsReference">
<sql name="ROLE" many-table="ROLES_WORKFLOW" many-key="WORKFLOW" />
</field>
<field name="types" type="com.sourcepark.ticketsystem.Type" collection="vector"
set-method="setTypesAsReference" get-method="getTypesAsReference">
<sql many-key="WORKFLOW" read-only="true"/>
</field>
<field name="tickets" type="com.sourcepark.ticketsystem.Ticket" collection="vector"
set-method="setTicketsAsReference" get-method="getTicketsAsReference">
<sql many-key="WORKFLOW"/>
</field>
<field name="actions" type="com.sourcepark.workflows.Action" collection="vector"
set-method="setActionsAsReference" get-method="getActionsAsReference">
<sql many-key="WORKFLOW"/>
</field>
<field name="priorities" type="com.sourcepark.ticketsystem.Priority" collection="vector"
set-method="setPrioritiesAsReference" get-method="getPrioritiesAsReference">
<sql many-key="WORKFLOW"/>
</field>
</class>
<!--/////////////////// Mapping for Role /////////////////////////////-->
<class name="com.sourcepark.workflows.Role" identity="id" key-generator="MAX">
<cache-type type="unlimited"/>
<map-to table="ROLES" />
<field name="id" type="integer" >
<sql name="ID" type="integer"/>
</field>
<field name="name" type="string">
<sql name="NAME" type="char" dirty="check" />
</field>
<field name="users" type="com.sourcepark.access.User" collection="vector"
set-method="setUsersAsReference" get-method="getUsersAsReference">
<sql name="USERS" many-table="ROLES_USERS" many-key="ROLE" />
</field>
<field name="workflows" type="com.sourcepark.workflows.Workflow" collection="vector"
set-method="setWorkflowsAsReference" get-method="getWorkflowsAsReference">
<sql name="WORKFLOW" many-table="ROLES_WORKFLOW" many-key="ROLE" />
</field>
<field name="preStates" type="com.sourcepark.workflows.State" collection="vector"
set-method="setPreStatesAsReference" get-method="getPreStatesAsReference">
<sql name="PRE_STATE" many-table="ROLES_PRE_STATES" many-key="ROLE" read-only="true" dirty="ignore"/>
</field>
<field name="sucStates" type="com.sourcepark.workflows.State" collection="vector"
set-method="setSucStatesAsReference" get-method="getSucStatesAsReference">
<sql name="SUC_STATE" many-table="ROLES_SUC_STATES" many-key="ROLE" read-only="true" dirty="ignore" />
</field>
<field name="recievedStates" type="com.sourcepark.workflows.State" collection="vector"
set-method="setRecievedStatesAsReference" get-method="getRecievedStatesAsReference">
<sql name="STATE" many-table="SELECT_RECIPIENTS" many-key="ROLE" read-only="true" dirty="ignore"/>
</field>
<field name="actions" type="com.sourcepark.workflows.Action" collection="vector"
set-method="setActionsAsReference" get-method="getActionsAsReference">
<sql name="ACTION_ID" many-table="ACTIONS_ROLES" many-key="ROLE_ID" />
</field>
</class>
<!--/////////////////// Mapping for State /////////////////////////////-->
<class name="com.sourcepark.workflows.State" identity="id" key-generator="MAX">
<cache-type type="unlimited"/>
<map-to table="STATES" xml="states" />
<field name="id" type="integer" >
<sql name="ID" type="integer"/>
<xml node="attribute"/>
</field>
<field name="name" type="string">
<sql name="NAME" type="char" dirty="check" />
<xml node="text" />
</field>
<field name="workflow" type="com.sourcepark.workflows.Workflow" >
<sql name="WORKFLOW"/>
</field>
<field name="rolesForThisPreState" type="com.sourcepark.workflows.Role" collection="vector"
set-method="setRolesForThisPreStateAsReference" get-method="getRolesForThisPreStateAsReference">
<sql name="ROLE" many-table="ROLES_PRE_STATES" many-key="PRE_STATE" read-only="true" dirty="ignore"/>
</field>
<field name="rolesForThisSucState" type="com.sourcepark.workflows.Role" collection="vector"
set-method="setRolesForThisSucStateAsReference" get-method="getRolesForThisSucStateAsReference">
<sql name="ROLE" many-table="ROLES_SUC_STATES" many-key="SUC_STATE" read-only="true" dirty="ignore" />
</field>
<field name="recipientRoles" type="com.sourcepark.workflows.Role" collection="vector"
set-method="setRecipientRolesAsReference" get-method="getRecipientRolesAsReference">
<sql name="ROLE" many-table="SELECT_RECIPIENTS" many-key="STATE" read-only="true" dirty="ignore" />
</field>
<field name="tickets" type="com.sourcepark.ticketsystem.Ticket" collection="vector"
set-method="setTicketsAsReference" get-method="getTicketsAsReference">
<sql many-key="STATE"/>
</field>
<field name="preStateSequences" type="com.sourcepark.workflows.Sequence" collection="vector"
set-method="setPreStateSequencesAsReference" get-method="getPreStateSequencesAsReference">
<sql many-key="PRE_STATE"/>
</field>
<field name="sucStateSequences" type="com.sourcepark.workflows.Sequence" collection="vector"
set-method="setSucStateSequencesAsReference" get-method="getSucStateSequencesAsReference">
<sql many-key="SUC_STATE"/>
</field>
<field name="events" type="com.sourcepark.ticketsystem.Event" collection="vector"
set-method="setEventsAsReference" get-method="getEventsAsReference">
<sql many-key="STATE"/>
</field>
</class>
<!--/////////////////// Mapping for Sequence //////////////////////////-->
<class name="com.sourcepark.workflows.Sequence" identity="id" key-generator="MAX">
<cache-type type="unlimited"/>
<map-to table="SEQUENCE" xml="sequence" />
<field name="id" type="integer" >
<sql name="ID" type="integer"/>
<xml node="attribute"/>
</field>
<field name="preState" type="com.sourcepark.workflows.State" >
<sql name="PRE_STATE" read-only="true" dirty="ignore"/>
</field>
<field name="sucState" type="com.sourcepark.workflows.State" >
<sql name="SUC_STATE" read-only="true" dirty="ignore"/>
</field>
<field name="workflow" type="com.sourcepark.workflows.Workflow" >
<sql name="WORKFLOW"/>
</field>
<field name="ranking" type="integer" >
<sql name="RANKING" type="integer"/>
<xml node="attribute"/>
</field>
</class>
<!--/////////////////// Mapping for Action /////////////////////////////-->
<class name="com.sourcepark.workflows.Action" identity="id" key-generator="MAX">
<cache-type type="unlimited"/>
<map-to table="ACTIONS" xml="actions" />
<field name="id" type="integer" >
<sql name="ID" type="integer"/>
<xml node="attribute"/>
</field>
<field name="name" type="string">
<sql name="NAME" type="char" dirty="check" />
<xml node="text" />
</field>
<field name="workflow" type="com.sourcepark.workflows.Workflow" >
<sql name="WORKFLOW"/>
</field>
<field name="roles" type="com.sourcepark.workflows.Role" collection="vector"
set-method="setRolesAsReference" get-method="getRolesAsReference">
<sql name="ROLE_ID" many-table="ACTIONS_ROLES" many-key="ACTION_ID" />
</field>
</class>
</mapping>
