Hibernate Search ContainedIn on 1-way Relationship - hibernate-search

In hibernate search is there a way to use ContainedIn to update the index on the unrelated end of a 1-way relationship?
Say I have three classes A, B, C that are associated like this:
#Indexed
Class A {
#IndexedEmbedded
B classBObject
}
Class B {
#IndexedEmbedded
C classCObject
}
Class C {
#ContainedIn
B classBObject
#Field
int myInt
}
Is there a way, short of making the relationship bi-directional, to mark Class B as contained in Class A so that when I update myInt in Class C it will trigger an update in the index to update the embedded field in Class A's index?

No, the relationship needs to be two-way for this.
N.B. there is no real drawback in Hibernate by adding the opposite side of the relation, other than having to add the property (or field).
So it might be interesting to also comment on why you'd want this.

Related

Mixing Table-Per-Hierarchy and Table-Per-Type in Entity Framework Code First to Existing Database

tl;dr: I'm trying to map a code-first model to an existing database where a certain hierarchy of objects has a mixed inheritance scheme. Some concrete classes use TPH and some use TPT. I can't seem to get the mapping correct.
I have a hierarchy of objects which I'm trying to map to an existing database. Some of the concrete classes contain additional properties, so they have their own table; some of the concrete classes do not, so they live in the base table and rely on a discriminator column. To simplify things, I've created a POC. The database structure is like this:
CREATE TABLE Foos (
Id INT IDENTITY NOT NULL PRIMARY KEY,
FooType TINYINT NOT NULL
)
CREATE TABLE FooTpts (
Id INT NOT NULL PRIMARY KEY
FOREIGN KEY REFERENCES Foos(Id),
Value INT NOT NULL
)
And the equivalent POCOs would be:
public abstract class Foo
{
public int Id { get; set; }
}
public class FooTph : Foo {}
public class FooTpt : Foo
{
public int Value { get; set; }
}
Seems simple enough. So my first try was the following mapping (fluent or with attributes, the result is the same):
modelBuilder.Entity<Foo>()
.ToTable("Foos")
.Map<FooTph>(m => m.ToTable("Foos").Requires("FooType").HasValue(1))
.Map<FooTpt>(m => m.ToTable("FooTpts").Requires("FooType").HasValue(2));
But that didn't work because:
It wants to create the FooTpt.FooType descriminator in the FooTpts table
Trying to execute a command gives me the following error (presumably because of point #1 above):
(6,10) : error 3032: Problem in mapping fragments starting at lines 6, 11:EntityTypes ConsoleApplication1.FooTph, ConsoleApplication1.FooTpt are being mapped to the same rows in table Foo. Mapping conditions can be used to distinguish the rows that these types are mapped to.
Back to the drawing board. This answer suggests creating an intermediate abstract entity mapped to the parent (TPH) table. Everything can always be solved with another layer of abstraction, right? So I make a few changes:
+ public abstract class FooTptBase : Foo {}
- public class FooTpt : Foo
+ public class FooTpt : FooTptBase
And change the mapping:
modelBuilder.Entity<Foo>()
.ToTable("Foos")
.Map<FooTph>(m => m.ToTable("Foos").Requires("FooType").HasValue(1))
.Map<FooTptBase>(m => m.ToTable("Foos").Requires("FooType").HasValue(2));
modelBuilder.Entity<FooTpt>().ToTable("FooTpts");
The database now looks good and we have a single discriminator in the parent table. But something is still missing and we get the same error:
(6,10) : error 3032: Problem in mapping fragments starting at lines 6, 11:EntityTypes ConsoleApplication1.FooTph, ConsoleApplication1.FooTpt are being mapped to the same rows in table Foo. Mapping conditions can be used to distinguish the rows that these types are mapped to.
That doesn't really make sense because all FooTpts have to be a FooTptBase by definition, which should require FooType == 2. (It's almost as if the model builder is ignoring my intermediate FooTptBase abstract type?)
So, what am I missing? How can I accomplish what I'm trying to do?

Inherited Relationship not working as expected

I have the following classes:
Class SearchTemplateDO [ Abstract ]
{
Relationship QueryParts As QueryPartDO [ Cardinality = many, Inverse = SearchTemplate ];
}
Class MyCustomSearchDO Extends (%Persistent, SearchTemplateDO)
{
/// inherits all properties / relationships from SearchTemplateDO
}
Class QueryPartDO Extends %Persistent
{
...
Relationship SearchTemplate As SearchTemplateDO
[ Cardinality = one, Inverse = QueryParts ];
Index SearchTemplateIndex On SearchTemplate;
}
When I look at these two tables in SQL I see that QueryPartDO's SearchTemplate field is empty and when I look at MyCustomSearchDO I do not see a "QueryParts" field, although both tables have data
from documentation
The MANY or CHILD side is not projected as a field, since these relationships are stateless on disk and SQL does not deal with in-memory objects. Instead, you must perform a simple join based on the ONE or CHILD side’s ID value and the MANY or PARENT side’s reference field:
You can't reference abstract parent in non-abstract child class, because abstract classes have no storage strategy defined. Both classes in relationship have to be either abstract (in this case there is no data stored at all) or non-abstract (in this case you'll have proper storage strategy)
In the end I was able to get this to work by extending %Persistent on the abstract class. Before when I tried it, nothing was working and I kept getting a strange error. However, I tried this in a sample project and now it works.

JPA - Force Lazy loading for only a given query

How do i enforce lazy loading strategy just for a given NamedQuery.
for eg. Consider the below pseudo code (just to explain the case)
I have an entity
#Entity
class Xyz {
int a;
int b;
#Fetch = EAGER
Set<ABC> listOfItems;
}
In this case, we have declared listOfItems to be EAGERLY fetched.
Now suppose , I have an NamedQuery (query="getXyz" , name="select x from Xyz x where a=?")
For this query , i just need the result to be lazy i.e i dont want the listOfItems to be retrieved.
What are the ways by which i can acheive them ?
p.s :
1. I dont want to change the listOfItems to be Lazy in the Entity class
2. I dont want to select specific fields in the query like name="select a,b from Xyz z where a = ? "
Thanks in advance for the suggestions
If you don't want to fetch the Set eagerly you have to define it as lazy. However note that the implementation is permitted to fetch eagerly when you specify lazy.
Quoting the specification:
public enum FetchType
extends java.lang.Enum
Defines strategies for fetching data from the database. The EAGER strategy is a requirement on the persistence provider runtime that data must be eagerly fetched. The LAZY strategy is a hint to the persistence provider runtime that data should be fetched lazily when it is first accessed. The implementation is permitted to eagerly fetch data for which the LAZY strategy hint has been specified.
If you however don't want to fetch such a Set I would as an alternative create a small class to fit your needs:
#Entity
#Table(name = "XYZ")
public class XyzStub
{
int a;
int b;
}
You can query for this using a TypedQuery:
EntityManager em;
//....
TypedQuery<XyzStub> q = em.createNamedQuery("select x from XyzStub x where x.a = :a", XyzStub.class)
q.setParameter("a", a);
If you are using EclipseLink, you can use fetch groups,
http://wiki.eclipse.org/EclipseLink/Examples/JPA/AttributeGroup

Flush() required for multiple Eclipselink merges in the same transaction?

I'm having an issue with multiple EntityManager.merge() calls in a single transaction. This is using an Oracle database. Neither object exists yet. Entities:
public class A {
#Id
#Column("ID")
public Long getID();
#OneToOne(targetEntity = B.class)
#JoinColumn("ID")
public B getB();
}
public class B {
#Id
#Column("ID")
public Long getID();
}
The merge code looks something like this:
#Transactional
public void create(Object A, Object B) {
Object A = entitymanager.merge(A);
B.setId(A.getId());
entitymanager.merge(B);
}
Object A's ID is generated through a sequence and it gets correctly set on B. Looking at the log, merge on A is called before merge on B is called. There is a #OneToOne mapping from A to B. However, at the end of the method when it goes to actually commit, it tries to do an INSERT on B before it goes to do an INSERT on A, which throws an IntegrityConstraintViolation because the "parent key not found".
If I add entitymanager.flush() before the 2nd merge, it works fine.
#Transactional
public void create(Object A, Object B) {
Object A = entitymanager.merge(A);
entitymanager.flush();
B.setId(A.getId());
entitymanager.merge(B);
}
However, flush() is an expensive operation that shouldn't be necessary. All of this should be happening in the same transaction (default propagation of #Transactional is Propagation.REQUIRED).
Any idea why this doesn't work without flush(), and why even though the merge on A happens before the merge on B, the actual INSERT on COMMIT is reversed?
If entity A and B do not have a relationship (i.e. #OneToOne, #OneToMany, ...), then the persistence provider cannot calculate the correct insertion order. IIRC EclipseLink does not use the object-creation order when it comes to sending SQL statements to the database.
If you like to refrain from using flush(), simply set your constraints to be deferred.
As Frank mentioned, the code you have shown does not set a A->B relationship, so there is no way for the provider to know that this B object needs to be inserted before the A. Other relationships may cause it to think that in general A needs to be inserted first.
Deferring constraints can be done on some databases, and refers to setting the database to defer constraint processing until the end of the transaction. If you defer or remove the constraints, you can then see if the SQL that is being generated is correct or if there is another problem with the code and mappings that is being missed.
It appears that the merges are alphabetical (at least, that is one possibility) unless there are bidirectional #OneToOne annotations.
Previously:
public class A {
#OneToOne(targetEntity = B.class)
#JoinColumn("ID")
public B getB();
}
public class B {
#Id
#Column("ID")
public Long getID();
}
Now:
public class A {
#OneToOne(targetEntity = B.class)
#JoinColumn("ID")
public B getB();
}
public class B {
#Id
#Column("ID")
public Long getID();
#OneToOne(targetEntity = A.class)
#JoinColumn("ID")
public A getA();
}
For what I'm doing it doesn't matter that B has a way to get A, but I still don't understand why the annotations in A aren't sufficient.

How else to ensure the order of a #ManyToMany List?

Consider:
#Entity
public class M {
#ManyToMany
private List<E> es = new ArrayList<E>();
private E newE;
public M(E newE) {
this.newE = newE;
es.add(newE);
}
Cannot I assert(m.newE == m.getEs(0))?
I could only if after rebooting the app/PU is:
public List<E> getEs() {
final int indexOf = es.indexOf(newE);
if (indexOf != 0) {
es.remove(indexOf);
es.add(0, newE);
}
return Collections.unmodifiableList(es);
}
However this burdensome code is even inefficient as it forces loading the E entitities from the PU before they may actually be needed. Any alternative that works?
Cannot I assert(m.newE == m.getEs(0))?
No, if there are any objects in the #ManyToMany, newE will be added at the end of the list. Also, I believe the list will be lazy-loaded before the insert, so your concern about things being inefficient doesn't really apply.
If you're only concerned about the ordering of elements when loaded from the persistence unit, #OrderBy will do the trick.
#ManyToMany
#Orderby("someproperty ASC")
private List<E> es = new ArrayList<E>();
If you also want to maintain order at runtime while adding elements, you'll have to implement compareTo() or a separate Comparator, then use Collections.sort(), or use a naturally sorted collection like SortedSet.
JPA also provides an #OrderColumn if you want the order maintained.
See this link
Otherwise changes to the order of the elements of the list are not considered a persistent change.