Factored the ancestor-descendent and merged from-to relationships

out of AVMNode.  Easier to get hibernate to do what I want and appears
to be a little more efficient as it removes several FK constraints
from a frequently updated table.


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/WCM-DEV2/root@3137 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Britt Park
2006-06-18 21:35:17 +00:00
parent 606096011c
commit d3f6048fbc
8 changed files with 338 additions and 40 deletions

View File

@@ -19,6 +19,9 @@ package org.alfresco.repo.avm;
import java.io.Serializable; import java.io.Serializable;
import org.hibernate.Query;
import org.hibernate.Session;
/** /**
* Base class for all repository file system like objects. * Base class for all repository file system like objects.
* @author britt * @author britt
@@ -35,16 +38,6 @@ abstract class AVMNodeImpl implements AVMNode, Serializable
*/ */
private int fVersionID; private int fVersionID;
/**
* The ancestor of this.
*/
private AVMNode fAncestor;
/**
* The node that was merged into this.
*/
private AVMNode fMergedFrom;
/** /**
* The Repository that owns this. * The Repository that owns this.
*/ */
@@ -87,8 +80,6 @@ abstract class AVMNodeImpl implements AVMNode, Serializable
{ {
fID = id; fID = id;
fVersionID = -1; fVersionID = -1;
fAncestor = null;
fMergedFrom = null;
fRepository = repo; fRepository = repo;
fIsRoot = false; fIsRoot = false;
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
@@ -107,7 +98,14 @@ abstract class AVMNodeImpl implements AVMNode, Serializable
*/ */
public void setAncestor(AVMNode ancestor) public void setAncestor(AVMNode ancestor)
{ {
fAncestor = ancestor; if (ancestor == null)
{
return;
}
HistoryLinkImpl link = new HistoryLinkImpl();
link.setAncestor(ancestor);
link.setDescendent(this);
SuperRepository.GetInstance().getSession().save(link);
} }
/** /**
@@ -116,7 +114,10 @@ abstract class AVMNodeImpl implements AVMNode, Serializable
*/ */
public AVMNode getAncestor() public AVMNode getAncestor()
{ {
return fAncestor; Session sess = SuperRepository.GetInstance().getSession();
Query query = sess.createQuery("select hl.ancestor from HistoryLinkImpl hl where hl.descendent = :desc");
query.setEntity("desc", this);
return (AVMNode)query.uniqueResult();
} }
/** /**
@@ -125,7 +126,14 @@ abstract class AVMNodeImpl implements AVMNode, Serializable
*/ */
public void setMergedFrom(AVMNode mergedFrom) public void setMergedFrom(AVMNode mergedFrom)
{ {
fMergedFrom = mergedFrom; if (mergedFrom == null)
{
return;
}
MergeLinkImpl link = new MergeLinkImpl();
link.setMfrom(mergedFrom);
link.setMto(this);
SuperRepository.GetInstance().getSession().save(link);
} }
/** /**
@@ -134,7 +142,10 @@ abstract class AVMNodeImpl implements AVMNode, Serializable
*/ */
public AVMNode getMergedFrom() public AVMNode getMergedFrom()
{ {
return fMergedFrom; Session sess = SuperRepository.GetInstance().getSession();
Query query = sess.createQuery("select ml.mfrom from MergeLinkImpl ml where ml.mto = :to");
query.setEntity("to", this);
return (AVMNode)query.uniqueResult();
} }
/** /**

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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 org.alfresco.repo.avm;
/**
* Interface for the ancestor-descendent relationship.
* @author britt
*/
interface HistoryLink
{
/**
* Set the ancestor part of this.
* @param ancestor
*/
public void setAncestor(AVMNode ancestor);
/**
* Get the ancestor part of this.
* @return The ancestor.
*/
public AVMNode getAncestor();
/**
* Set the descendent part of this.
* @param descendent
*/
public void setDescendent(AVMNode descendent);
/**
* Get the descendent part of this.
* @return
*/
public AVMNode getDescendent();
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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 org.alfresco.repo.avm;
import java.io.Serializable;
/**
* Holds a ancestor-descendent relationship.
* @author britt
*/
class HistoryLinkImpl implements HistoryLink, Serializable
{
private static final long serialVersionUID = -430859344980137718L;
/**
* The ancestor.
*/
private AVMNode fAncestor;
/**
* The descendent.
*/
private AVMNode fDescendent;
/**
* Set the ancestor part of this.
* @param ancestor
*/
public void setAncestor(AVMNode ancestor)
{
fAncestor = ancestor;
}
/**
* Get the ancestor part of this.
* @return The ancestor.
*/
public AVMNode getAncestor()
{
return fAncestor;
}
/**
* Set the descendent part of this.
* @param descendent
*/
public void setDescendent(AVMNode descendent)
{
fDescendent = descendent;
}
/**
* Get the descendent part of this.
* @return The descendent.
*/
public AVMNode getDescendent()
{
return fDescendent;
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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 org.alfresco.repo.avm;
/**
* This is the interface for the merged from - to relationship.
* @author britt
*/
interface MergeLink
{
/**
* Set the from part.
* @param from
*/
public void setMfrom(AVMNode from);
/**
* Get the from part.
* @return The from part.
*/
public AVMNode getMfrom();
/**
* Set the to part.
* @param to
*/
public void setMto(AVMNode to);
/**
* Get the to part.
* @return The to part.
*/
public AVMNode getMto();
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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 org.alfresco.repo.avm;
import java.io.Serializable;
/**
* This contains a single merged from-to relationship.
* @author britt
*/
class MergeLinkImpl implements MergeLink, Serializable
{
private static final long serialVersionUID = 6672271083042424944L;
/**
* The node that was merged from.
*/
private AVMNode fFrom;
/**
* The node that was merged to.
*/
private AVMNode fTo;
/**
* Set the from part.
* @param from
*/
public void setMfrom(AVMNode from)
{
fFrom = from;
}
/**
* Get the from part.
* @return The from part.
*/
public AVMNode getMfrom()
{
return fFrom;
}
/**
* Set the to part.
* @param to
*/
public void setMto(AVMNode to)
{
fTo = to;
}
/**
* Get the to part.
* @return The to part.
*/
public AVMNode getMto()
{
return fTo;
}
}

View File

@@ -184,6 +184,7 @@ class OrphanReaper implements Runnable
{ {
public void perform(Session session) public void perform(Session session)
{ {
SuperRepository.GetInstance().setSession(session);
Query query = session.getNamedQuery("FindOrphans"); Query query = session.getNamedQuery("FindOrphans");
query.setMaxResults(fBatchSize); query.setMaxResults(fBatchSize);
List<AVMNode> nodes = (List<AVMNode>)query.list(); List<AVMNode> nodes = (List<AVMNode>)query.list();
@@ -196,28 +197,48 @@ class OrphanReaper implements Runnable
for (AVMNode node : nodes) for (AVMNode node : nodes)
{ {
// Save away the ancestor and merged from fields from this node. // Save away the ancestor and merged from fields from this node.
AVMNode ancestor = node.getAncestor(); query = session.createQuery("from HistoryLinkImpl hl where hl.descendent = :desc");
AVMNode mergedFrom = node.getMergedFrom(); query.setEntity("desc", node);
// Get all the nodes that have this node as ancestor. HistoryLink hlink = (HistoryLink)query.uniqueResult();
query = session.getNamedQuery("AVMNode.GetDescendents"); AVMNode ancestor = null;
query.setEntity("node", node); if (hlink != null)
List<AVMNode> descendents = (List<AVMNode>)query.list();
for (AVMNode desc : descendents)
{ {
ancestor = hlink.getAncestor();
session.delete(hlink);
}
query = session.createQuery("from MergeLinkImpl ml where ml.mto = :to");
query.setEntity("to", node);
MergeLink mlink = (MergeLink)query.uniqueResult();
AVMNode mergedFrom = null;
if (mlink != null)
{
mergedFrom = mlink.getMfrom();
session.delete(mlink);
}
// Get all the nodes that have this node as ancestor.
query = session.getNamedQuery("HistoryLink.ByAncestor");
query.setEntity("node", node);
List<HistoryLink> links = (List<HistoryLink>)query.list();
for (HistoryLink link : links)
{
AVMNode desc = link.getDescendent();
desc.setAncestor(ancestor); desc.setAncestor(ancestor);
if (desc.getMergedFrom() == null) if (desc.getMergedFrom() == null)
{ {
desc.setMergedFrom(mergedFrom); desc.setMergedFrom(mergedFrom);
} }
session.delete(link);
} }
// Get all the nodes that have this node as mergedFrom // Get all the nodes that have this node as mergedFrom
query = session.getNamedQuery("AVMNode.GetMergedTo"); query = session.getNamedQuery("MergeLink.ByFrom");
query.setEntity("merged", node); query.setEntity("merged", node);
List<AVMNode> merged = (List<AVMNode>)query.list(); List<MergeLink> mlinks = (List<MergeLink>)query.list();
for (AVMNode merge : merged) for (MergeLink link : mlinks)
{ {
merge.setMergedFrom(ancestor); link.getMto().setMergedFrom(ancestor);
session.delete(link);
} }
session.flush();
node = AVMNodeUnwrapper.Unwrap(node); node = AVMNodeUnwrapper.Unwrap(node);
// Extra work for directories. // Extra work for directories.
if (node instanceof DirectoryNode) if (node instanceof DirectoryNode)

View File

@@ -37,7 +37,7 @@ public class PurgeTest extends AVMServiceTestBase
setupBasicTree(); setupBasicTree();
BulkLoader loader = new BulkLoader(fService); BulkLoader loader = new BulkLoader(fService);
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
loader.recursiveLoad("source", "main:/"); loader.recursiveLoad("source/web", "main:/");
System.err.println("Load time: " + (System.currentTimeMillis() - start) + "ms"); System.err.println("Load time: " + (System.currentTimeMillis() - start) + "ms");
fService.createSnapshot("main"); fService.createSnapshot("main");
System.err.println("Load time + snapshot: " + (System.currentTimeMillis() - start) + "ms"); System.err.println("Load time + snapshot: " + (System.currentTimeMillis() - start) + "ms");

View File

@@ -24,14 +24,6 @@
<version column="vers" <version column="vers"
name="vers" name="vers"
type="long"/> type="long"/>
<!-- BasicAttributes are attributes that pretty much all AVMNodes will
have. -->
<many-to-one name="ancestor" column="ancestor_id"
class="AVMNodeImpl"/>
<!-- Nothing does anything with this yet. We'll need it for
complete versioning semantics. -->
<many-to-one name="mergedFrom" column="merged_from"
class="AVMNodeImpl"/>
<!-- This should really be not null, but I haven't figured out <!-- This should really be not null, but I haven't figured out
the right way to build the relation so that nullability constraints the right way to build the relation so that nullability constraints
won't cause violations in the db during saves. --> won't cause violations in the db during saves. -->
@@ -174,6 +166,18 @@
column="parent_id"/> column="parent_id"/>
</composite-id> </composite-id>
</class> </class>
<class name="HistoryLinkImpl" proxy="HistoryLink" table="history_links">
<composite-id>
<key-many-to-one name="ancestor" class="AVMNodeImpl" column="ancestor"/>
<key-many-to-one name="descendent" class="AVMNodeImpl" column="descendent"/>
</composite-id>
</class>
<class name="MergeLinkImpl" proxy="MergeLink" table="merge_links">
<composite-id>
<key-many-to-one name="mfrom" class="AVMNodeImpl" column="mfrom"/>
<key-many-to-one name="mto" class="AVMNodeImpl" column="mto"/>
</composite-id>
</class>
<query name="ChildEntry.ByNameParent"> <query name="ChildEntry.ByNameParent">
<![CDATA[ <![CDATA[
from ChildEntryImpl ce from ChildEntryImpl ce
@@ -210,14 +214,28 @@
</query> </query>
<query name="AVMNode.GetDescendents"> <query name="AVMNode.GetDescendents">
<![CDATA[ <![CDATA[
from AVMNodeImpl an select hl.descendent
where an.ancestor = :node from HistoryLinkImpl hl
where hl.ancestor = :node
]]>
</query>
<query name="HistoryLink.ByAncestor">
<![CDATA[
from HistoryLinkImpl hl
where hl.ancestor = :node
]]> ]]>
</query> </query>
<query name="AVMNode.GetMergedTo"> <query name="AVMNode.GetMergedTo">
<![CDATA[ <![CDATA[
from AVMNodeImpl an select ml.mto
where an.mergedFrom = :merged from MergeLinkImpl ml
where ml.mfrom = :merged
]]>
</query>
<query name="MergeLink.ByFrom">
<![CDATA[
from MergeLinkImpl ml
where ml.mfrom = :merged
]]> ]]>
</query> </query>
<query name="VersionRoot.GetVersionRoot"> <query name="VersionRoot.GetVersionRoot">