mirror of https://github.com/apache/openjpa.git
OPENJPA-1686: Define generic persistent graph and relation. Implement graph as a set of edges.
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@955425 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
2e827c5eab
commit
a79a96b86c
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 org.apache.openjpa.persistence.graph;
|
||||
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Abstract implementation of a {@linkplain Graph} borrows from {@link AbstractSet abstract} implementation of
|
||||
* {@link Set}. The extended {@link Set#remove(Object) remove()} semantics accounts for
|
||||
* {@link Graph#delink(Object, Object) removal} of all relationship to the removed element.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
* @param <E> type of element of the graph.
|
||||
*/
|
||||
public abstract class AbstractGraph<E> extends AbstractSet<E> implements Graph<E> {
|
||||
/**
|
||||
* Removing an element from this graph has the side effect of removing all
|
||||
* relations directed to the removed element.
|
||||
*/
|
||||
@Override
|
||||
public boolean remove(Object e) {
|
||||
E node = (E)e;
|
||||
Set<Relation<E, E>> rs = getRelationsTo(node);
|
||||
for (Relation<E,E> r : rs) {
|
||||
delink(r.getSource(), node);
|
||||
}
|
||||
return super.remove(e);
|
||||
}
|
||||
}
|
|
@ -18,20 +18,20 @@
|
|||
*/
|
||||
package org.apache.openjpa.persistence.graph;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
/**
|
||||
* A simple persistent entity to become member of a graph.
|
||||
* In this style, a type has to extend {@linkplain Vertex} - an abstract persistent type.
|
||||
* This persistent type has its own identity.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@Entity
|
||||
public class City extends Vertex<City> {
|
||||
public class City implements Serializable {
|
||||
@Id
|
||||
private String name;
|
||||
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 org.apache.openjpa.persistence.graph;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Graph is an extended {@link java.util.Set Set} that is aware of {@link Relation relationship}
|
||||
* between its elements. The linkage between two elements is represented by a {@link Relation relation}.
|
||||
* <br>
|
||||
* The extended behavior of Set allows two member elements to be {@linkplain Graph#link(Object, Object) linked}
|
||||
* or {@linkplain Graph#delink(Object, Object) delinked}.
|
||||
*
|
||||
* @param <E> Type of element of the graph
|
||||
*
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public interface Graph<E> extends Set<E> {
|
||||
/**
|
||||
* Links the pair of given vertices.
|
||||
* If the pair is already linked then the existing relation is returned.
|
||||
* If either of the vertices are currently non a member of this graph,
|
||||
* then they are added to the graph as a side-effect of linking.
|
||||
*
|
||||
* @param source non-null source node
|
||||
* @param target non-null target node
|
||||
*
|
||||
* @param <V1> generic type of source node
|
||||
* @param <V2> generic type of target node
|
||||
*
|
||||
* @return a relation
|
||||
*/
|
||||
public <V1 extends E, V2 extends E> Relation<V1, V2> link(V1 source, V2 target);
|
||||
|
||||
/**
|
||||
* Breaks the relation between the given pair of nodes.
|
||||
*
|
||||
* @param source non-null source node
|
||||
* @param target non-null target node
|
||||
*
|
||||
* @param <V1> generic type of source node
|
||||
* @param <V2> generic type of target node
|
||||
*
|
||||
* @return the existing relation, if any, that had been broken. null otherwise.
|
||||
*/
|
||||
public <V1 extends E, V2 extends E> Relation<V1, V2> delink(V1 source, V2 target);
|
||||
|
||||
/**
|
||||
* Gets the directed relation between the given pair of nodes, if exists.
|
||||
*
|
||||
* @param source non-null source node
|
||||
* @param target non-null target node
|
||||
*
|
||||
* @param <V1> generic type of source node
|
||||
* @param <V2> generic type of target node
|
||||
*
|
||||
* @return a relation between the nodes, if exists. null otherwise.
|
||||
*/
|
||||
public <V1 extends E, V2 extends E> Relation<V1, V2> getRelation(V1 source, V2 target);
|
||||
|
||||
/**
|
||||
* Gets the nodes that are directly reachable from the given source node.
|
||||
*
|
||||
* @return set of target nodes. Empty set if the given source node is not connected to any other nodes.
|
||||
*/
|
||||
public Set<E> getTargets(E source);
|
||||
|
||||
|
||||
/**
|
||||
* Gets the source nodes that are directly connected to the given target node.
|
||||
*
|
||||
* @return set of source nodes. Empty set if the given target node is not connected from any node.
|
||||
*/
|
||||
public Set<E> getSources(E target);
|
||||
|
||||
/**
|
||||
* Gets all the relations originating from the given source.
|
||||
* @param <V>
|
||||
* @param source
|
||||
* @return
|
||||
*/
|
||||
public <V extends E> Set<Relation<V,E>> getRelationsFrom(V source);
|
||||
|
||||
/**
|
||||
* Gets all the relations terminating on the given target.
|
||||
* @param <V>
|
||||
* @param target
|
||||
* @return
|
||||
*/
|
||||
public <V extends E> Set<Relation<E,V>> getRelationsTo(V target);
|
||||
}
|
|
@ -18,23 +18,24 @@
|
|||
*/
|
||||
package org.apache.openjpa.persistence.graph;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
/**
|
||||
* A simple persistent entity to become member of a graph.
|
||||
* In this style, a type has to extend {@linkplain Vertex} - an abstract persistent type.
|
||||
* This persistent type has its own identity.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@Entity
|
||||
public class People extends Vertex<People>{
|
||||
public class People implements Serializable {
|
||||
@Id
|
||||
private long ssn;
|
||||
private String name;
|
||||
|
||||
public long getSsn() {
|
||||
return ssn;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 org.apache.openjpa.persistence.graph;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
|
||||
/**
|
||||
* Persistent Graph is a marker for persistent version of a {@link Graph}.
|
||||
* <br>
|
||||
* A persistent graph does not declare how the persistent state of the graph is captured and hence is abstract.
|
||||
* But Persistent Graph is unique to be persistent in a JPA sense, because JPA does <em>not</em> represent
|
||||
* container instances such as {@link java.util.Set} or {@link java.util.List} as <em>first class</em> type --
|
||||
* i.e. their instances do not carry a persistent identity. But declaring a graph as persistent type amounts
|
||||
* to represent the graph as a first class persistent type with its own persistent identity.
|
||||
* <br>
|
||||
* Persistent Graph defines a auto-generated persistent identity.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
* @param <E> type of element.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@MappedSuperclass
|
||||
public abstract class PersistentGraph<E> extends AbstractGraph<E> implements Serializable {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private long id;
|
||||
|
||||
public final long getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 org.apache.openjpa.persistence.graph;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.OneToOne;
|
||||
|
||||
/**
|
||||
* Generic, directed, attributed Relation as a first-class, persistent entity.
|
||||
* <br>
|
||||
* A relation is
|
||||
* <ol>
|
||||
* <LI>generic because Relation type is parameterized with the type of vertices it links.
|
||||
* <LI>directed because it distinguishes the two vertices as source and target.
|
||||
* <LI>attributed because any arbitrary name-value pair can be associated with a relation.
|
||||
* </ol>
|
||||
* A relation is made persistence capable by annotating its generic source and target as persistent entity.
|
||||
* A relation is also a <em>first-class</em> entity having its own persistent identifier.
|
||||
* <br>
|
||||
* A relation is immutable in terms of its two vertices. The properties
|
||||
* attached to a relation, however, can change.
|
||||
* <br>
|
||||
* @param <V1> the type of <em>source</em> vertex linked by this relation.
|
||||
* @param <V2> the type of <em>target</em> vertex linked by this relation.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@Entity
|
||||
public class PersistentRelation<V1,V2> implements Relation<V1,V2>, Serializable {
|
||||
/**
|
||||
* Relation is a first class object with its own identifier.
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private long id;
|
||||
|
||||
/**
|
||||
* A Relation must have a non-null vertex as source.
|
||||
*/
|
||||
@OneToOne(optional=false, targetEntity=Entity.class,
|
||||
cascade={CascadeType.PERSIST,CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
|
||||
private V1 source;
|
||||
|
||||
/**
|
||||
* A Relation may have a non-null vertex as target.
|
||||
*/
|
||||
@OneToOne(optional=true, targetEntity=Entity.class,
|
||||
cascade={CascadeType.PERSIST,CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
|
||||
private V2 target;
|
||||
|
||||
/**
|
||||
* The properties of a Relation is a set of key-value pairs and is declared as
|
||||
* <code>java.util.Properties</code>.
|
||||
* <br>
|
||||
* Declaring the key-value pairs as <code>java.util.Properties</code> makes OpenJPA
|
||||
* assume that both key and value will be stored in database as String.
|
||||
* This is not <em>strictly</em> correct because <code>java.util.Properties</code>
|
||||
* declares its key and value as <code>java.lang.Object</code>. Hence it is possible for an application
|
||||
* to insert key and/or value that are not a String but that type information will not be preserved in
|
||||
* the database. Subsequently, when loaded from database the key and value
|
||||
* both will appear as String and hence it becomes the application's responsibility to decode the
|
||||
* Strings back to the actual type. While this provision loses type information, it allows the
|
||||
* database record to be readable and more importantly supports query that are predicated on
|
||||
* (equality only) key-value pairs.
|
||||
* <br>
|
||||
* Another possibility to express key-value pair as
|
||||
* <br>
|
||||
* <code>Map<String,Serializable> attrs;</code>
|
||||
* <br>
|
||||
* This will serialize the values but preserve their types. The down-side is neither a query can be
|
||||
* predicated on value nor are the database records readable.
|
||||
* <br>
|
||||
* The third alternative is a Map where keys are String and values are Object
|
||||
* <br>
|
||||
* <code>Map<String,Object> attrs;</code>
|
||||
* This leads to the whole map being serialized as a single blob of data.
|
||||
*/
|
||||
@ManyToMany(cascade={CascadeType.ALL},fetch=FetchType.LAZY)
|
||||
private Properties attrs;
|
||||
|
||||
/**
|
||||
* Special constructor for byte code enhancement.
|
||||
*/
|
||||
protected PersistentRelation() {
|
||||
}
|
||||
|
||||
/**
|
||||
* A relation is immutable in terms of two vertices it connects.
|
||||
*
|
||||
* @param s source vertex must not be null.
|
||||
* @param t target vertex may or may not be null.
|
||||
*/
|
||||
public PersistentRelation(V1 s, V2 t) {
|
||||
if (s == null)
|
||||
throw new NullPointerException("Can not create relation from a null source vertex");
|
||||
source = s;
|
||||
target = t;
|
||||
attrs = new Properties();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets generated persistent identity.
|
||||
*/
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the immutable source vertex.
|
||||
*/
|
||||
public V1 getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the immutable target vertex.
|
||||
*/
|
||||
public V2 getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given attribute is associated with this relation.
|
||||
*/
|
||||
public boolean hasAttribute(String attr) {
|
||||
return attrs.containsKey(attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the given attribute.
|
||||
*
|
||||
* @return value of the given attribute. A null value does not distinguish whether
|
||||
* the attribute was set to a null value or the attribute was absent.
|
||||
*/
|
||||
public Object getAttribute(String attr) {
|
||||
return attrs.get(attr);
|
||||
}
|
||||
|
||||
public Properties getAttributes() {
|
||||
return attrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given key-value pair, overwriting any prior association to the same attribute.
|
||||
*
|
||||
* @return the same relation for fluent method-chaining
|
||||
*/
|
||||
public Relation<V1,V2> addAttribute(String attr, Object v) {
|
||||
attrs.put(attr, v);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given attribute.
|
||||
*
|
||||
* @return value of the given attribute that just has been removed. A null value does not
|
||||
* distinguish whether the attribute was set to a null value or the attribute was absent.
|
||||
*/
|
||||
public Relation<V1,V2> removeAttribute(String attr) {
|
||||
attrs.remove(attr);
|
||||
return this;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return source + "->" + target;
|
||||
}
|
||||
}
|
|
@ -18,167 +18,76 @@
|
|||
*/
|
||||
package org.apache.openjpa.persistence.graph;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.OneToOne;
|
||||
|
||||
/**
|
||||
* Generic, directed, attributed Relation as a first-class entity.
|
||||
* Generic, directed, attributed Relation.
|
||||
* <br>
|
||||
* A relation is
|
||||
* <ol>
|
||||
* <LI>generic because the vertices it links are generically typed.
|
||||
* <LI>directed because it distinguishes the two end points as source and target.
|
||||
* <LI>attributed because any arbitrary name-value pair can be associated with a relation.
|
||||
* <LI>generic because Relation type is parameterized with the type of vertices it links.
|
||||
* <LI>directed because it distinguishes the two vertices as source and target.
|
||||
* <LI>attributed because any arbitrary key-value pair can be associated with a relation.
|
||||
* </ol>
|
||||
* <br>
|
||||
* A relation is immutable in terms of its two vertices. The properties
|
||||
* attached to a relation can change.
|
||||
*
|
||||
* associated to a relation, however, can change.
|
||||
* <br>
|
||||
* @param <V1> the type of <em>source</em> vertex linked by this relation.
|
||||
* @param <V2> the type of <em>target</em> vertex linked by this relation.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@Entity
|
||||
public class Relation<V1,V2> implements Serializable {
|
||||
/**
|
||||
* Relation is a first class object with its own identifier.
|
||||
*/
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private long id;
|
||||
|
||||
/**
|
||||
* A Relation must have a non-null vertex as source.
|
||||
*/
|
||||
@OneToOne(optional=false)
|
||||
private Vertex<V1> source;
|
||||
|
||||
/**
|
||||
* A Relation must have a non-null vertex as source.
|
||||
*/
|
||||
@OneToOne(optional=false)
|
||||
private Vertex<V2> target;
|
||||
|
||||
/**
|
||||
* The properties of a Relation is a set of key-value pairs and is declared as
|
||||
* <code>java.util.Properties</code>.
|
||||
* <br>
|
||||
* Declaring the key-value pairs as <code>java.util.Properties</code> makes OpenJPA
|
||||
* assume that both key and value will be stored in database as String.
|
||||
* This is not <em>strictly</em> correct because <code>java.util.Properties</code>
|
||||
* declares its key and value as <code>java.lang.Object</code>. Hence it is possible for an application
|
||||
* to insert key and/or value that are not a String but that type information will not be preserved in
|
||||
* the database. Subsequently, when loaded from database the key and value
|
||||
* both will appear as String and hence it becomes the application's responsibility to decode the
|
||||
* Strings back to the actual type. While this provision loses type information, it allows the
|
||||
* database record to be readable and more importantly supports query that are predicated on
|
||||
* (equality only) key-value pairs.
|
||||
* <br>
|
||||
* Another possibility to express key-value pair as
|
||||
* <br>
|
||||
* <code>Map<String,Serializable> attrs;</code>
|
||||
* <br>
|
||||
* This will serialize the values but preserve their types. The down-side is neither a query can be
|
||||
* predicated on value nor are the database records readable.
|
||||
* <br>
|
||||
* The third alternative is a Map where keys are String and values are Object
|
||||
* <br>
|
||||
* <code>Map<String,Object> attrs;</code>
|
||||
* This leads to the whole map being serialized as a single blob of data.
|
||||
*/
|
||||
@ManyToMany(cascade={CascadeType.ALL},fetch=FetchType.LAZY)
|
||||
private Properties attrs;
|
||||
|
||||
/**
|
||||
* Special constructor for byte code enhancement.
|
||||
*/
|
||||
protected Relation() {
|
||||
}
|
||||
|
||||
/**
|
||||
* A relation is immutable in terms of two vertices it connects.
|
||||
* Either vertex must not be null.
|
||||
*/
|
||||
public Relation(Vertex<V1> s, Vertex<V2> t) {
|
||||
if (s == null)
|
||||
throw new NullPointerException("Can not create relation from a null source vertex");
|
||||
if (t == null)
|
||||
throw new NullPointerException("Can not create relation to a null target vertex");
|
||||
source = s;
|
||||
target = t;
|
||||
attrs = new Properties();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets generated persistent identity.
|
||||
*/
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public interface Relation<V1,V2> {
|
||||
/**
|
||||
* Gets the immutable source vertex.
|
||||
*/
|
||||
public Vertex<V1> getSource() {
|
||||
return source;
|
||||
}
|
||||
*
|
||||
* @return a non-null source vertex.
|
||||
*/
|
||||
public V1 getSource();
|
||||
|
||||
/**
|
||||
* Gets the immutable target vertex.
|
||||
*/
|
||||
public Vertex<V2> getTarget() {
|
||||
return target;
|
||||
}
|
||||
* Unlike source, a target for a relation may be null.
|
||||
*
|
||||
* @return a target vertex. May be null.
|
||||
*/
|
||||
public V2 getTarget();
|
||||
|
||||
|
||||
/**
|
||||
* Affirms if the given attribute is associated with this relation.
|
||||
* Adds the given key-value pair, overwriting any prior value associated to the same key.
|
||||
*
|
||||
* @return the same relation for <em>fluent</em> method-chaining
|
||||
*/
|
||||
public boolean hasAttribute(String attr) {
|
||||
return attrs.containsKey(attr);
|
||||
}
|
||||
public Relation<V1,V2> addAttribute(String key, Object value);
|
||||
|
||||
/**
|
||||
* Affirms if an attribute value has been associated with the given key.
|
||||
*
|
||||
*/
|
||||
public boolean hasAttribute(String key);
|
||||
|
||||
/**
|
||||
* Gets the value of the given attribute.
|
||||
*
|
||||
* @return value of the given attribute. A null value does not distinguish whether
|
||||
* the attribute was set to a null value or the attribute was absent.
|
||||
*/
|
||||
public Object getAttribute(String attr) {
|
||||
return attrs.get(attr);
|
||||
}
|
||||
|
||||
public Properties getAttributes() {
|
||||
return attrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given key-value pair, overwriting any prior association to the same attribute.
|
||||
*
|
||||
* @return the same relation for fluent method-chaining
|
||||
* @see #hasAttribute(String)
|
||||
*/
|
||||
public Relation<V1,V2> addAttribute(String attr, Object v) {
|
||||
attrs.put(attr, v);
|
||||
return this;
|
||||
}
|
||||
public Object getAttribute(String key);
|
||||
|
||||
/**
|
||||
* Removes the given attribute.
|
||||
*
|
||||
* @return value of the given attribute that just has been removed. A null value does not
|
||||
* distinguish whether the attribute was set to a null value or the attribute was absent.
|
||||
* @return the modified relation for <em>fluent</em> method chaining.
|
||||
*/
|
||||
public Relation<V1,V2> removeAttribute(String attr) {
|
||||
attrs.remove(attr);
|
||||
return this;
|
||||
}
|
||||
public Relation<V1,V2> removeAttribute(String key);
|
||||
|
||||
/**
|
||||
* Gets the key-value pairs associated with this relation.
|
||||
*/
|
||||
public Properties getAttributes();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 org.apache.openjpa.persistence.graph;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.OneToMany;
|
||||
|
||||
/**
|
||||
* RelationGraph is a first-class persistent entity that express its persistent state as a set of
|
||||
* {@link Relation persistent relations}.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
@Entity
|
||||
public class RelationGraph<E> extends PersistentGraph<E> {
|
||||
@OneToMany(cascade={CascadeType.PERSIST,CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
|
||||
private Set<PersistentRelation<E,E>> relations = new HashSet<PersistentRelation<E,E>>();
|
||||
|
||||
/*
|
||||
* Links the given vertices, unless they are already connected.
|
||||
*
|
||||
* @param source non-null source vertex
|
||||
* @param target non-null target vertex
|
||||
*
|
||||
* @see org.apache.openjpa.persistence.graph.Graph#link(V1, V2)
|
||||
*/
|
||||
public <V1 extends E,V2 extends E> Relation<V1,V2> link(V1 source, V2 target) {
|
||||
if (source == null)
|
||||
throw new NullPointerException("Can not link from a null source vertex");
|
||||
if (target == null)
|
||||
throw new NullPointerException("Can not link to a null target vertex");
|
||||
|
||||
Relation<V1,V2> r = getRelation(source, target);
|
||||
if (r == null) {
|
||||
r = new PersistentRelation<V1, V2>(source, target);
|
||||
relations.add((PersistentRelation<E, E>) r);
|
||||
}
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Delinks the given vertices, if they are currently connected.
|
||||
*
|
||||
*
|
||||
* @see org.apache.openjpa.persistence.graph.Graph#delink(V1, V2)
|
||||
*/
|
||||
public <V1 extends E,V2 extends E> Relation<V1,V2> delink(V1 source, V2 target) {
|
||||
Relation<V1,V2> r = getRelation(source, target);
|
||||
if (r != null) {
|
||||
relations.remove(r);
|
||||
}
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the relation between the given vertex.
|
||||
*
|
||||
* @see org.apache.openjpa.persistence.graph2.Graph#getRelation(V1, V2)
|
||||
*/
|
||||
public <V1 extends E,V2 extends E> Relation<V1,V2> getRelation(V1 source, V2 target) {
|
||||
for (Relation<?,?> r : relations) {
|
||||
if (r.getSource().equals(source) && r.getTarget() != null && r.getTarget().equals(target)) {
|
||||
return (Relation<V1,V2>)r;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator over the nodes of this graph.
|
||||
*/
|
||||
public Iterator<E> iterator() {
|
||||
return getNodes().iterator();
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V extends E> Set<Relation<V, E>> getRelationsFrom(V source) {
|
||||
Set<Relation<V,E>> rs = new HashSet<Relation<V,E>>();
|
||||
for (Relation<E,E> r : relations) {
|
||||
if (r.getSource().equals(source) && r.getTarget() != null)
|
||||
rs.add((Relation<V,E>)r);
|
||||
}
|
||||
return rs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <V extends E> Set<Relation<E, V>> getRelationsTo(V target) {
|
||||
Set<Relation<E, V>> rs = new HashSet<Relation<E, V>>();
|
||||
for (Relation<?,?> r : relations) {
|
||||
if (r.getTarget() != null && r.getTarget().equals(target))
|
||||
rs.add((Relation<E, V>)r);
|
||||
}
|
||||
return rs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<E> getSources(Object target) {
|
||||
Set<E> sources = new HashSet<E>();
|
||||
for (Relation<E,E> r : relations) {
|
||||
if (r.getTarget() != null && r.getTarget().equals(target))
|
||||
sources.add(r.getSource());
|
||||
}
|
||||
return sources;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<E> getTargets(Object source) {
|
||||
Set<E> targets = new HashSet<E>();
|
||||
for (Relation<E,E> r : relations) {
|
||||
if (r.getSource().equals(source) && r.getTarget() != null)
|
||||
targets.add(r.getTarget());
|
||||
}
|
||||
return targets;
|
||||
}
|
||||
|
||||
public Set<E> getNodes() {
|
||||
Set all = new HashSet();
|
||||
for (Relation<?,?> r : relations) {
|
||||
all.add(r.getSource());
|
||||
if (r.getTarget() != null)
|
||||
all.add(r.getTarget());
|
||||
}
|
||||
return all;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(E e) {
|
||||
if (contains(e))
|
||||
return false;
|
||||
relations.add(new PersistentRelation<E,E>(e,null));
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -32,13 +32,11 @@ import org.apache.openjpa.jdbc.meta.MappingRepository;
|
|||
import org.apache.openjpa.jdbc.meta.ValueHandler;
|
||||
import org.apache.openjpa.jdbc.meta.strats.HandlerFieldStrategy;
|
||||
import org.apache.openjpa.jdbc.meta.strats.HandlerHandlerMapTableFieldStrategy;
|
||||
import org.apache.openjpa.jdbc.meta.strats.RelationCollectionInverseKeyFieldStrategy;
|
||||
import org.apache.openjpa.jdbc.meta.strats.UntypedPCValueHandler;
|
||||
import org.apache.openjpa.kernel.QueryHints;
|
||||
import org.apache.openjpa.meta.ClassMetaData;
|
||||
import org.apache.openjpa.meta.FieldMetaData;
|
||||
import org.apache.openjpa.meta.MetaDataRepository;
|
||||
import org.apache.openjpa.persistence.OpenJPAPersistence;
|
||||
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
|
||||
|
||||
/**
|
||||
|
@ -80,23 +78,23 @@ public class TestPersistentGraph extends SingleEMFTestCase {
|
|||
};
|
||||
|
||||
private EntityManager em;
|
||||
|
||||
private PersistentGraph<Object> graph;
|
||||
|
||||
public void setUp() throws Exception {
|
||||
super.setUp(CLEAR_TABLES, Vertex.class, Relation.class, People.class, City.class);
|
||||
super.setUp(CLEAR_TABLES, PersistentGraph.class, RelationGraph.class,
|
||||
PersistentRelation.class, People.class, City.class);
|
||||
em = emf.createEntityManager();
|
||||
createData();
|
||||
graph = createData();
|
||||
em.clear();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Verifies that fields are mapped with expected strategy or value handlers.
|
||||
*/
|
||||
public void testMapping() {
|
||||
assertStrategy(People.class, "relations", RelationCollectionInverseKeyFieldStrategy.class, null);
|
||||
assertStrategy(City.class, "relations", RelationCollectionInverseKeyFieldStrategy.class, null);
|
||||
assertStrategy(Relation.class, "source", HandlerFieldStrategy.class, UntypedPCValueHandler.class);
|
||||
assertStrategy(Relation.class, "target", HandlerFieldStrategy.class, UntypedPCValueHandler.class);
|
||||
assertStrategy(Relation.class, "attrs", HandlerHandlerMapTableFieldStrategy.class, null);
|
||||
assertStrategy(PersistentRelation.class, "source", HandlerFieldStrategy.class, UntypedPCValueHandler.class);
|
||||
assertStrategy(PersistentRelation.class, "target", HandlerFieldStrategy.class, UntypedPCValueHandler.class);
|
||||
assertStrategy(PersistentRelation.class, "attrs", HandlerHandlerMapTableFieldStrategy.class, null);
|
||||
}
|
||||
|
||||
private void printMapping(FieldMapping fm) {
|
||||
|
@ -111,7 +109,7 @@ public class TestPersistentGraph extends SingleEMFTestCase {
|
|||
}
|
||||
|
||||
FieldMapping getFieldMapping(Class<?> pcClass, String field) {
|
||||
MappingRepository repos = (MappingRepository) OpenJPAPersistence.cast(emf).getConfiguration()
|
||||
MappingRepository repos = (MappingRepository) emf.getConfiguration()
|
||||
.getMetaDataRepositoryInstance();
|
||||
ClassMapping cmd = repos.getMapping(pcClass, null, true);
|
||||
assertNotNull("No metadata found for " + pcClass, cmd);
|
||||
|
@ -123,7 +121,7 @@ public class TestPersistentGraph extends SingleEMFTestCase {
|
|||
|
||||
/**
|
||||
* Asserts that the given field of the given class has been mapped with the
|
||||
* given straegy or value handler.
|
||||
* given strategy or value handler.
|
||||
*/
|
||||
void assertStrategy(Class<?> pcClass, String field, Class<? extends FieldStrategy> strategy,
|
||||
Class<? extends ValueHandler> handler) {
|
||||
|
@ -151,7 +149,7 @@ public class TestPersistentGraph extends SingleEMFTestCase {
|
|||
}
|
||||
|
||||
FieldStrategy getStrategy(Class<?> cls, String field) {
|
||||
MetaDataRepository repos = OpenJPAPersistence.cast(emf).getConfiguration().getMetaDataRepositoryInstance();
|
||||
MetaDataRepository repos = emf.getConfiguration().getMetaDataRepositoryInstance();
|
||||
ClassMetaData cmd = repos.getMetaData(cls, null, true);
|
||||
assertNotNull("No metadat found for " + cls, cmd);
|
||||
FieldMetaData fmd = cmd.getField(field);
|
||||
|
@ -168,6 +166,9 @@ public class TestPersistentGraph extends SingleEMFTestCase {
|
|||
*/
|
||||
public void testCreateGraph() {
|
||||
em.getTransaction().begin();
|
||||
assertFalse(em.contains(graph));
|
||||
graph = em.find(PersistentGraph.class, graph.getId());
|
||||
assertNotNull(graph);
|
||||
People[] people = new People[SSN.length];
|
||||
for (int i = 0; i < SSN.length; i++) {
|
||||
People p = em.find(People.class, SSN[i]);
|
||||
|
@ -180,7 +181,7 @@ public class TestPersistentGraph extends SingleEMFTestCase {
|
|||
assertNotNull(c);
|
||||
cities[i] = c;
|
||||
}
|
||||
assertDataEquals(people, cities);
|
||||
assertDataEquals(graph, people, cities);
|
||||
|
||||
em.getTransaction().rollback();
|
||||
}
|
||||
|
@ -190,10 +191,11 @@ public class TestPersistentGraph extends SingleEMFTestCase {
|
|||
* correctly.
|
||||
*/
|
||||
public void testQueryRelation() {
|
||||
List<Relation> relations = em.createQuery("select r from Relation r", Relation.class).getResultList();
|
||||
String jpql = "select r from PersistentRelation r";
|
||||
List<PersistentRelation> relations = em.createQuery(jpql, PersistentRelation.class).getResultList();
|
||||
for (Relation<?, ?> r : relations) {
|
||||
Vertex<?> source = r.getSource();
|
||||
Vertex<?> target = r.getTarget();
|
||||
Object source = r.getSource();
|
||||
Object target = r.getTarget();
|
||||
if (source instanceof People) {
|
||||
int i = indexOf((People) source);
|
||||
if (target instanceof People) {
|
||||
|
@ -204,16 +206,16 @@ public class TestPersistentGraph extends SingleEMFTestCase {
|
|||
int j = indexOf((City) target);
|
||||
assertEquals(i % CITY_NAMES.length, j);
|
||||
assertTrue(r.getAttributes().isEmpty());
|
||||
} else {
|
||||
fail();
|
||||
} else if (target != null){
|
||||
fail("Unexpected relation " + r);
|
||||
}
|
||||
} else if (source instanceof City) {
|
||||
int i = indexOf((City) source);
|
||||
if (target instanceof City) {
|
||||
int j = indexOf((City) target);
|
||||
assertEquals(""+ATTR_DISTANCE_VALUE[i][j], r.getAttribute(ATTR_DISTANCE));
|
||||
} else {
|
||||
fail();
|
||||
} else if (target != null) {
|
||||
fail("Unexpected relation " + r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -224,8 +226,8 @@ public class TestPersistentGraph extends SingleEMFTestCase {
|
|||
*/
|
||||
public void testQueryRelationOnSourceParameter() {
|
||||
People p1 = em.find(People.class, SSN[0]);
|
||||
String jpql = "select r from Relation r where r.source = :node";
|
||||
List<Relation> result = em.createQuery(jpql, Relation.class)
|
||||
String jpql = "select r from PersistentRelation r where r.source = :node";
|
||||
List<PersistentRelation> result = em.createQuery(jpql, PersistentRelation.class)
|
||||
.setParameter("node", p1)
|
||||
.getResultList();
|
||||
assertFalse("Result of [" + jpql + "] on source = " + p1 + " should not be empty", result.isEmpty());
|
||||
|
@ -235,8 +237,8 @@ public class TestPersistentGraph extends SingleEMFTestCase {
|
|||
* Tests that a relation can be queried predicated on its attribute key.
|
||||
*/
|
||||
public void testQueryRelationOnSingleAttributeKey() {
|
||||
String jpql = "select r from Relation r join r.attrs a where key(a) = :key";
|
||||
List<Relation> result = em.createQuery(jpql, Relation.class)
|
||||
String jpql = "select r from PersistentRelation r join r.attrs a where key(a) = :key";
|
||||
List<PersistentRelation> result = em.createQuery(jpql, PersistentRelation.class)
|
||||
.setParameter("key", ATTR_EMOTION)
|
||||
.getResultList();
|
||||
|
||||
|
@ -248,9 +250,9 @@ public class TestPersistentGraph extends SingleEMFTestCase {
|
|||
* key-value pair.
|
||||
*/
|
||||
public void testQueryRelationOnSingleAttributeKeyValue() {
|
||||
String jpql = "select r from Relation r join r.attrs a where key(a) = :key and value(a) = :value";
|
||||
String jpql = "select r from PersistentRelation r join r.attrs a where key(a) = :key and value(a) = :value";
|
||||
String value = EMOTIONS[0][2].toString();
|
||||
List<Relation> result = em.createQuery(jpql, Relation.class)
|
||||
List<PersistentRelation> result = em.createQuery(jpql, PersistentRelation.class)
|
||||
.setParameter("key", ATTR_EMOTION)
|
||||
.setParameter("value", value)
|
||||
.getResultList();
|
||||
|
@ -265,10 +267,11 @@ public class TestPersistentGraph extends SingleEMFTestCase {
|
|||
* wrong result.
|
||||
*/
|
||||
public void testQueryRelationOnMultipleAttributeKeyValuePairs() {
|
||||
String jpql = "select r from Relation r join r.attrs a1 join r.attrs a2 "
|
||||
+ "where key(a1) = :key1 and value(a1) = :value1 " + "and key(a2) = :key2 and value(a2) = :value2";
|
||||
String jpql = "select r from PersistentRelation r join r.attrs a1 join r.attrs a2 "
|
||||
+ "where key(a1) = :key1 and value(a1) = :value1 "
|
||||
+ "and key(a2) = :key2 and value(a2) = :value2";
|
||||
String value = EMOTIONS[0][2].toString();
|
||||
List<Relation> result = em.createQuery(jpql, Relation.class)
|
||||
List<PersistentRelation> result = em.createQuery(jpql, PersistentRelation.class)
|
||||
.setParameter("key1", ATTR_EMOTION)
|
||||
.setParameter("value1", value)
|
||||
.setParameter("key2", ATTR_SINCE)
|
||||
|
@ -279,10 +282,10 @@ public class TestPersistentGraph extends SingleEMFTestCase {
|
|||
+ ") and key-value=(" + ATTR_SINCE + "," + SINCE + ") should not be empty",
|
||||
result.isEmpty());
|
||||
|
||||
String wrongJPQL = "select r from Relation r join r.attrs a "
|
||||
String wrongJPQL = "select r from PersistentRelation r join r.attrs a "
|
||||
+ "where key(a) = :key1 and value(a) = :value1 "
|
||||
+ "and key(a) = :key2 and value(a) = :value2";
|
||||
List<Relation> result2 = em.createQuery(wrongJPQL, Relation.class)
|
||||
List<PersistentRelation> result2 = em.createQuery(wrongJPQL, PersistentRelation.class)
|
||||
.setParameter("key1", ATTR_EMOTION)
|
||||
.setParameter("value1", value)
|
||||
.setParameter("key2", ATTR_SINCE)
|
||||
|
@ -297,8 +300,8 @@ public class TestPersistentGraph extends SingleEMFTestCase {
|
|||
public void testAddRemoveAttribute() {
|
||||
em.getTransaction().begin();
|
||||
People p1 = em.find(People.class, SSN[0]);
|
||||
String jpql = "select r from Relation r where r.source = :node";
|
||||
List<Relation> r = em.createQuery(jpql, Relation.class)
|
||||
String jpql = "select r from PersistentRelation r where r.source = :node";
|
||||
List<PersistentRelation> r = em.createQuery(jpql, PersistentRelation.class)
|
||||
.setHint(QueryHints.HINT_IGNORE_PREPARED_QUERY, true)
|
||||
.setParameter("node", p1)
|
||||
.getResultList();
|
||||
|
@ -308,8 +311,8 @@ public class TestPersistentGraph extends SingleEMFTestCase {
|
|||
em.clear();
|
||||
|
||||
em.getTransaction().begin();
|
||||
jpql = "select r from Relation r join r.attrs a where key(a) = :key";
|
||||
Relation newR = em.createQuery(jpql, Relation.class)
|
||||
jpql = "select r from PersistentRelation r join r.attrs a where key(a) = :key";
|
||||
Relation newR = em.createQuery(jpql, PersistentRelation.class)
|
||||
.setParameter("key", "new-key")
|
||||
.getSingleResult();
|
||||
assertNotNull(newR);
|
||||
|
@ -318,9 +321,9 @@ public class TestPersistentGraph extends SingleEMFTestCase {
|
|||
em.getTransaction().commit();
|
||||
|
||||
em.getTransaction().begin();
|
||||
jpql = "select r from Relation r join r.attrs a where key(a) = :key";
|
||||
jpql = "select r from PersistentRelation r join r.attrs a where key(a) = :key";
|
||||
try {
|
||||
newR = em.createQuery(jpql, Relation.class)
|
||||
newR = em.createQuery(jpql, PersistentRelation.class)
|
||||
.setParameter("key", "new-key")
|
||||
.getSingleResult();
|
||||
fail(jpql + " with new-key expected no result");
|
||||
|
@ -339,15 +342,15 @@ public class TestPersistentGraph extends SingleEMFTestCase {
|
|||
* Creates a typical graph of People and Cities. The tests are sensitive to
|
||||
* the actual values and relations set in in this method.
|
||||
*/
|
||||
void createData() {
|
||||
if (isPopulated())
|
||||
return;
|
||||
PersistentGraph<Object> createData() {
|
||||
PersistentGraph<Object> graph = new RelationGraph<Object>();
|
||||
|
||||
em.getTransaction().begin();
|
||||
|
||||
People[] people = new People[SSN.length];
|
||||
for (int i = 0; i < SSN.length; i++) {
|
||||
People p = new People();
|
||||
em.persist(p);
|
||||
graph.add(p);
|
||||
p.setSsn(SSN[i]);
|
||||
p.setName(PERSON_NAMES[i]);
|
||||
people[i] = p;
|
||||
|
@ -355,14 +358,15 @@ public class TestPersistentGraph extends SingleEMFTestCase {
|
|||
City[] cities = new City[CITY_NAMES.length];
|
||||
for (int i = 0; i < CITY_NAMES.length; i++) {
|
||||
City c = new City();
|
||||
em.persist(c);
|
||||
graph.add(c);
|
||||
c.setName(CITY_NAMES[i]);
|
||||
cities[i] = c;
|
||||
}
|
||||
for (int i = 0; i < people.length; i++) {
|
||||
for (int j = 0; j < people.length; j++) {
|
||||
if (EMOTIONS[i][j] != null) {
|
||||
Relation<People, People> r = people[i].link(people[j]).addAttribute(ATTR_EMOTION, EMOTIONS[i][j]);
|
||||
Relation<People, People> r = graph.link(people[i], people[j])
|
||||
.addAttribute(ATTR_EMOTION, EMOTIONS[i][j]);
|
||||
if (i == 0 && j == 2) {
|
||||
r.addAttribute(ATTR_SINCE, SINCE);
|
||||
}
|
||||
|
@ -371,18 +375,20 @@ public class TestPersistentGraph extends SingleEMFTestCase {
|
|||
}
|
||||
for (int i = 0; i < cities.length; i++) {
|
||||
for (int j = 0; j < cities.length; j++) {
|
||||
cities[i].link(cities[j]).addAttribute(ATTR_DISTANCE, ATTR_DISTANCE_VALUE[i][j]);
|
||||
graph.link(cities[i], cities[j]).addAttribute(ATTR_DISTANCE, ATTR_DISTANCE_VALUE[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < people.length; i++) {
|
||||
people[i].link(cities[i % CITY_NAMES.length]);
|
||||
graph.link(people[i], cities[i % CITY_NAMES.length]);
|
||||
}
|
||||
|
||||
em.persist(graph);
|
||||
em.getTransaction().commit();
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
void assertDataEquals(People[] people, City[] cities) {
|
||||
void assertDataEquals(Graph<Object> graph, People[] people, City[] cities) {
|
||||
assertEquals(SSN.length, people.length);
|
||||
assertEquals(CITY_NAMES.length, cities.length);
|
||||
|
||||
|
@ -399,7 +405,7 @@ public class TestPersistentGraph extends SingleEMFTestCase {
|
|||
People p1 = people[i];
|
||||
for (int j = 0; j < people.length; j++) {
|
||||
People p2 = people[j];
|
||||
Relation<People, People> r = p1.getRelationTo(p2);
|
||||
Relation<People, People> r = graph.getRelation(p1,p2);
|
||||
if (EMOTIONS[i][j] != null) {
|
||||
assertNotNull(r);
|
||||
assertEquals(EMOTIONS[i][j].toString(), r.getAttribute(ATTR_EMOTION));
|
||||
|
@ -412,7 +418,7 @@ public class TestPersistentGraph extends SingleEMFTestCase {
|
|||
City c1 = cities[i];
|
||||
for (int j = 0; j < cities.length; j++) {
|
||||
City c2 = cities[j];
|
||||
Relation<City, City> r12 = c1.getRelationTo(c2);
|
||||
Relation<City, City> r12 = graph.getRelation(c1,c2);
|
||||
assertNotNull(r12);
|
||||
assertEquals(""+ATTR_DISTANCE_VALUE[i][j], r12.getAttribute(ATTR_DISTANCE));
|
||||
}
|
||||
|
@ -422,7 +428,7 @@ public class TestPersistentGraph extends SingleEMFTestCase {
|
|||
People p = people[i];
|
||||
for (int j = 0; j < cities.length; j++) {
|
||||
City c = cities[j];
|
||||
Relation<People, City> r = p.getRelationTo(c);
|
||||
Relation<People, City> r = graph.getRelation(p,c);
|
||||
if (i % CITY_NAMES.length == j) {
|
||||
assertNotNull(r);
|
||||
} else {
|
||||
|
|
|
@ -1,155 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 org.apache.openjpa.persistence.graph;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.OneToMany;
|
||||
|
||||
/**
|
||||
* Vertex of a persistent graph.
|
||||
* <br>
|
||||
* A vertex maintains relationship to other connected vertices.
|
||||
* An abstract vertex provides the functionality for a concrete derivation to be member of a graph.
|
||||
* <br>
|
||||
* A generic vertex does <em>not</em> define a persistent identity on the derived types.
|
||||
* <br>
|
||||
* The only persistent state maintained by a generic vertex is its ({@linkplain Relation relations}
|
||||
* to its neighboring vertices.
|
||||
* <br>
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
* @param <V> the type of this vertex.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
|
||||
@MappedSuperclass
|
||||
public abstract class Vertex<V> implements Serializable {
|
||||
/**
|
||||
* A set of relations starting from this vertex.
|
||||
* A vertex owns its relations in a object modeling sense but not in a JPA sense.
|
||||
* In a object modeling sense, a relation can not exist without its source vertex.
|
||||
* In a JPA sense, the relational table for a Relation holds a foreign key to the source vertex,
|
||||
* so Relation is the owner of vertex-Relation relation, confusing, eh?
|
||||
*/
|
||||
@OneToMany(mappedBy="source",
|
||||
cascade=CascadeType.ALL,
|
||||
orphanRemoval=true,
|
||||
targetEntity=Relation.class)
|
||||
private Set<Relation<V,?>> relations;
|
||||
|
||||
/**
|
||||
* Create a relation to the given vertex from this vertex, if no such relation exists.
|
||||
* If a relation exists, returns the existing relation.
|
||||
*
|
||||
* @param n a non-null vertex.
|
||||
*/
|
||||
public <V2> Relation<V,V2> link(Vertex<V2> n) {
|
||||
if (n == null) {
|
||||
throw new NullPointerException(this + " can not link to null target");
|
||||
}
|
||||
Relation<V,V2> r = getRelationTo(n);
|
||||
if (r == null) {
|
||||
r = new Relation<V,V2>(this, n);
|
||||
if (relations == null) {
|
||||
relations = new HashSet<Relation<V,?>>();
|
||||
}
|
||||
relations.add(r);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Breaks the relation, if exists, from this vertex to the given vertex.
|
||||
* Returns the broken link.
|
||||
*
|
||||
* @param n a vertex, possibly null.
|
||||
*/
|
||||
public <V2> Relation<V,V2> delink(Vertex<V2> n) {
|
||||
Relation<V,V2> r = getRelationTo(n);
|
||||
if (r != null) {
|
||||
relations.remove(r);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the relation from this vertex to the given vertex, if exists. Null otherwise.
|
||||
*
|
||||
* @param n a vertex, possibly null.
|
||||
*/
|
||||
public <V2> Relation<V,V2> getRelationTo(Vertex<V2> n) {
|
||||
if (n == null || relations == null)
|
||||
return null;
|
||||
for (Relation<V,?> r : relations) {
|
||||
if (r.getTarget().equals(n)) {
|
||||
return (Relation<V,V2>)r;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Affirms if the given vertex is linked from this vertex.
|
||||
*/
|
||||
public boolean isRelatedTo(Vertex<V> n) {
|
||||
return getRelationTo(n) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the relations starting from this vertex.
|
||||
*/
|
||||
public Set<Relation<V,?>> getRelations() {
|
||||
return relations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the immediate neighbors.
|
||||
*/
|
||||
public Set<Vertex<?>> getNeighbours() {
|
||||
if (relations == null)
|
||||
return null;
|
||||
Set<Vertex<?>> neighbours = new HashSet<Vertex<?>>();
|
||||
for (Relation<V,?> r : relations) {
|
||||
neighbours.add(r.getTarget());
|
||||
}
|
||||
return neighbours;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the immediate neighbors of the given type.
|
||||
*/
|
||||
public <T> Set<Vertex<T>> getNeighbours(Class<T> type, boolean allowSubclass) {
|
||||
if (relations == null)
|
||||
return null;
|
||||
Set<Vertex<T>> neighbours = new HashSet<Vertex<T>>();
|
||||
for (Relation<V,?> r : relations) {
|
||||
Vertex<?> target = r.getTarget();
|
||||
boolean include = allowSubclass ? type.isAssignableFrom(target.getClass()) : type == target.getClass();
|
||||
if (include)
|
||||
neighbours.add((Vertex<T>)target);
|
||||
}
|
||||
return neighbours;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue