I am trying to implement a three-way join relationship in JPA 2.0 (using annotations).
My domain is as follows:
I had a look at the #JoinTable annotation and I am not sure how to use it in order to implement the relationship.
Can anyone please provide clues or code samples?
If I understand your question well, you actually have another Entity, let's call it AdvertisementAssignment. Then, this entity should have OneToOne association with each of your 3-way counterparts.
#Entity
#Table(name = "ADV_ASSIGNMENTS")
public class AdvertisementAssignment {
private Advertisement advertisement;
private TimeSlot timeSlot;
private Day day;
// other properties definition (e.g. id, assigner etc.)
// define constructor
#OneToOne(cascade = CascadeType.ALL)
public Advertisement getAdvertisement() {
return this.advertisement;
}
public void setAdvertisement(Advertisement advertisement) {
this.advertisement = advertisement;
}
// same for 'timeSlot' and 'day' properties
}
Related
Is it possible to implement Pattern querying with JPA Criteria API?
In my case, regex patterns are stored into a quick bag property; and I'm trying to avoid using native queries (e.g. PostgreSQL POSIX support).
#Entity #Table(name = "rcp")
public class Recipient {
#Id #Column(name = "rcp_email_id")
#Email
private String email;
#CollectionTable(joinColumns = #JoinColumn(name = "rcp_email_fk"))
#ElementCollection(fetch = EAGER)
#Convert(converter = PatternConverter.class)
private Set<Pattern> rules = new HashSet<>();
...
}
So I figured I could use the Criteria API but failed to properly develop the technlogy, obviously:
#AllArgsConstructor
public class RecipientSpecification implements Specification<Recipient> {
private String sample;
#Override
public Predicate toPredicate(Root<Recipient> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
return builder.exists(root.join("rules").as(Pattern.class).matcher(sample).find());
}
}
I thought I could work on the join with a cast and execute the Java Pattern logic by casting the properties, which I realize to be dumb now because it has no sense from the JPA DSL point of view. It doesn't even compile! But is there a proper way to proceed?
Thanks in advance
I have the following entities:
#NodeEntity(label = "A")
public class A {
#Property(name = "something")
private String someProperty;
//... getters and setters
}
#NodeEntity(label = "B")
public class B {
#Property(name = "someOtherThing")
private String otherProperty;
//... getters and setters
}
#RelationshipEntity(type = "AB")
public class AB {
#StartNode
private A start;
#EndNode
private B end;
#Property(name = "evenOtherThing")
private String prop;
//... getters and setters
}
So, in this situation I have (:A)-[:AB]->(:B). I can have several ABs (meaning I can connect A to B several times, having different properties each time).
With that configuration I can save AB instances without problems, but when it comes to deleting just the relationship, I couldn't find a way to do so, using the spring-data-neo4j methods.
Things that I tried:
1- Custom query:
#Repository
public interface ABRepository extends GraphRepository<AB> {
#Query("MATCH (a:A)-[ab:AB]->(b:B) WHERE a.something={something} DELETE ab")
void deleteBySomething(#Param("something") String something);
}
Usage:
#Autowired
ABRepository repository;
//...
repository.deleteBySomething(something);
It didn't work as expected. The A node is removed altogether with the AB relationship. If I run the query directly at the database, it works as expected.
2- Delete from the repository:
#Repository
public interface ABRepository extends GraphRepository<AB> {
#Query("MATCH (a:A)-[ab:AB]->(b:B) WHERE a.something={something} RETURN a,ab,b")
Iterable<AB> findBySomething(#Param("something") String something);
}
Usage:
Iterable<AB> it = repository.findBySomething(something);
repository.delete(it);
Same stuff. The nodes are removed. I tried to iterate over the Iterable<AB> and remove the relationships one by one, without success as well.
3- Nulling the references of A and B inside AB and saving AB:
Same code of the repository, with a different usage:
Iterable<AB> it = repository.findBySomething(something);
for (AB ab : it) {
ab.setA(null);
ab.setB(null);
}
repository.save(it);
Here I'm just trying random stuff. It didn't work as expected. The framework rises an exception stating that the start and end nodes can't be null.
So, what am I doing wrong? What does it take to remove a simple relationship from the database using spring-data-neo4j, without removing the linking nodes?
For the record: my neo4j database is v.3.0.4 and my spring-data-neo4j is v.4.1.4.RELEASE. Running Java 8.
In the end the problem was a sum of two factors.
First: not mentioned in the question, but the way I saved the AB entity wasn't ideal. I was using repository.save(ab) directly, and that can make the framework do some magic with the A and B entities inside. To save just the relationship, without touching the related entities, the repository.save(ab, 0) should be used.
Second: removing entities using a custom query is intuitively faster than fetching the entities and then removing them, so using that approach was my first goal. And here again I was confused by some magic behind the scenes, better described at this question: Spring Data Neo4j 4returning cached results?
In summary, after removing entities or relationships using custom queries, I should clear the session:
#Autowired
Session session;
//...
repository.deleteBySomething(something);
session.clear();
These two tweaks fixed the weird behavior I was having with the framework.
I have 2 classes, Widget & Person. The Widget class references an instance of Person as it's owner. They are stored/retrieved from the database via doctrine2.
I have created a Zend Framework 2 Form to enter the details of a Widget which includes a select where you can choose the owner of the Widget, this Form element posts back the id of the Person object to be set at the owner.
In it's current state the setOwner() method of Widget class will take an object of type Person or an id which can then be loaded from the database using the Doctrine2 entity manager.
While this works I think it is not the best way as the Widget object is dealing with the loading from the database and it needs to have access to the entity manager to do this.
As I currently understand the only 2 ways I can do this is either the way I have it working or by creating a new Hydrator which will fish the object out of the database before Hydrating the object or is there another method?
I want the architecture to be as tidy as possible so I would like to know what people think would be the best way to do this in ZF2?
This is the setup I currently have:
class Widget {
protected $id;
protected $name;
protected $owner;
// Doctrine Entity Manager
protected $entityManager;
public function __construct($entityManager)
{
$this->entityManager = $entityManager;
}
// methods, getters & setters
public function setOwner($owner)
{
if (is_object($owner) && $owner instanceof Person)
{
$this->owner = $owner;
}
else if (is_scalar($owner)
{
$this->owner = $this->entityManager->find('Person', $owner);
}
}
}
class Person {
protected $id;
protected $name;
// other member variables & methods
}
I have a problem. I'm learning JPA. I'm using embedded OpenEJB container in unit tests, but only working is #OneToMany(fetch=EAGER). Otherwise is the collection allways null. I haven't found, how the lazy strategy works, how the container fills the data and in which circumstances triggers the container the loading action?
I have read, that the action triggers when the getter is being called. But when I have the code:
#OneToMany(fetch = LAZY, mappedBy="someField")
private Set<AnotherEntities> entities = new Set<AnotherEntities>();
...
public Set<AnotherEntities> getEntities() {
return entities;
}
I'm always getting null. I thing, the LAZY strategy cannot be tested with embedded container. The problem might be also in the bidirectional relation.
Does have anybody else similar expiriences with the JPA testing?
Attachments
The real test case with setup:
#RunWith(UnitilsJUnit4TestClassRunner.class)
#DataSet("dataSource.xml")
public class UnitilsCheck extends UnitilsJUnit4 {
private Persister prs;
public UnitilsCheck() {
Throwable err = null;
try {
Class.forName("org.hsqldb.jdbcDriver").newInstance();
Properties props = new Properties();
props.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
props.put("ds", "new://Resource?type=DataSource");
props.put("ds.JdbcDriver", "org.hsqldb.jdbcDriver");
props.put("ds.JdbcUrl", "jdbc:hsqldb:mem:PhoneBookDB");
props.put("ds.UserName", "sa");
props.put("ds.Password", "");
props.put("ds.JtaManaged", "true");
Context context = new InitialContext(props);
prs = (Persister) context.lookup("PersisterImplRemote");
}
catch (Throwable e) {
e.printStackTrace();
err = e;
}
TestCase.assertNull(err);
}
#Test
public void obtainNickNamesLazily() {
TestCase.assertNotNull(prs);
PersistableObject po = prs.findByPrimaryKey("Ferenc");
TestCase.assertNotNull(po);
Collection<NickNames> nicks = po.getNickNames();
TestCase.assertNotNull(nicks);
TestCase.assertEquals("[Nick name: Kutyafája, belongs to Ferenc]", nicks.toString());
}
}
The bean Presister is the bean mediating access to the entity beans. The crucial code of class follows:
#PersistenceUnit(unitName="PhonePU")
protected EntityManagerFactory emf;
public PhoneBook findByPrimaryKey(String name) {
EntityManager em = emf.createEntityManager();
PhoneBook phonebook = (PhoneBook)em.find(PhoneBook.class, name);
em.close();
return phonebook;
}
Entity PhoneBook is one line of phone book (also person). One person can have zero or more nick names. With EAGER strategy it works. With LAZY the collection is allways null. May be the problem is in the detaching of objects. (See OpenEJB - JPA Concepts, part Caches and detaching.) But in the manual is written, that the collection can be sometimes (more like manytimes) empty, but not null.
The problem is in the life cycle of an entity. (Geronimo uses OpenJPA, so le't see OpenJPA tutorial, part Entity Lifecycle Management.) The application uses container managed transactions. Each method call on the bean Persiser runs in an own transation. And the persistency context depends on the transaction. The entity is disconnected from its context at the end of the transaction, thus at the end of the method. I tried to get the entity and on second line in the same method to get the collection of nick names and it worked. So the problem was identifyed: I cannot get additionally any entity data from the data store without re-attaching the entity to some persistency context. The entity is re-attached by the EntityManager.merge() method.
The code needs more correctures. Because the entity cannot obtain the EntityManager reference and re-attach itself, the method returning nick names must be moved to the Persister class. (The comment Heureka marks the critical line re-attaching the entity.)
public Collection<NickNames> getNickNamesFor(PhoneBook pb) {
//emf is an EntityManagerFactory reference
EntityManager em = emf.createEntityManager();
PhoneBook pb = em.merge(pb); //Heureka!
Collection<NickNames> nicks = pb.getNickNames();
em.close();
return nicks;
}
The collection is then obtained in this way:
//I have a PhoneBook instance pb
//pb.getNickNames() returns null only
//I have a Persister instance pe
nicks = pe.getNickNames(pb);
That's all.
You can have a look at my second question concerning this topic I'have asked on this forum. It is the qustion OpenJPA - lazy fetching does not work.
How I would write the code
#Entity
public class MyEntity {
#OneToMany(fetch = LAZY, mappedBy="someField")
private Set<AnotherEntities> entities;
// Constructor for JPA
// Fields aren't initalized here so that each em.load
// won't create unnecessary objects
private MyEntity() {}
// Factory method for the rest
// Have field initialization with default values here
public static MyEntity create() {
MyEntity e = new MyEntity();
e.entities = new Set<AnotherEntities>();
return e;
}
public Set<AnotherEntities> getEntities() {
return entities;
}
}
Idea no 2:
I just thought that the order of operations in EAGER and LAZY fetching may differ i.e. EAGER fetching may
Declare field entities
Fetch value for entities (I'd assume null)
Set value of entities to new Set<T>()
while LAZY may
Declare field `entities
set value of entities to new Set<T>()
Fetch value for entities (I'd assume null)'
Have to find a citation for this as well.
Idea no 1: (Not the right answer)
What if you'd annotate the getter instead of the field? This should instruct JPA to use getters and setters instead of field access.
In the Java Persistence API, an entity can have field-based or
property-based access. In field-based access, the persistence provider
accesses the state of the entity directly through its instance
variables. In property-based access, the persistence provider uses
JavaBeans-style get/set accessor methods to access the entity's
persistent properties.
From The Java Persistence API - A Simpler Programming Model for Entity Persistence
How can I send JPA generated entities over an JAX WS web service without getting the
an XML infinite cycle exception because of the cycle of references in those entities?
Any idea? I found this MOXy that can do it...partially. But i already have the entities generated and to manually add XmlTransient and such annotations to each of them it's crazy.
Do you have any other idea how to do it?
Thanks!
EclipseLink JAXB (MOXy) can handle this with its bidirectional mapping with #XmlInverseReference:
import javax.persistence.*;
#Entity
public class Customer {
#Id
private long id;
#OneToOne(mappedBy="customer", cascade={CascadeType.ALL})
private Address address;
}
and
import javax.persistence.*;
import org.eclipse.persistence.oxm.annotations.*;
#Entity
public class Address implements Serializable {
#Id
private long id;
#OneToOne
#JoinColumn(name="ID")
#MapsId
#XmlInverseReference(mappedBy="address")
private Customer customer;
}
For more information see:
http://bdoughan.blogspot.com/2010/07/jpa-entities-to-xml-bidirectional.html
http://wiki.eclipse.org/EclipseLink/Examples/MOXy/JPA
You can also use MOXy's externalized representation of the metadata for this. For more information see:
XML to Java mapping tool - with mapping descriptor
http://wiki.eclipse.org/EclipseLink/Examples/MOXy/EclipseLink-OXM.XML
make your getCustomer #XmlTransient
#XmlTransient
public Customer getCustomer() {
...