JDBC: select entities with Many to one relation - jdbc

I have the two entity classes with bi-directional Many-to-one relation.
class A {
#Column(name="ID")
long Id;
}
class B {
#ManyToOne
#JoinColumn(name="A_ID")
A a;
}
The entities are well-coded with additional data fields and getters and setters. And now I want to construct a query string to fetch data from table B, where B's "A_ID" column is equal to A's "ID".
I tried something like this:
"select b.data1, b.data2 from B b, A a WHERE b.a.Id=a.Id"
But it does not work. What is the correct way to construct such a query? And if A and B are in a uni directional relation, would there be any difference?
Thanks in advance.

You don't need to join the tables, the whole idea behind #ManyToOne and #OneToMany is to do away with the need for most joins.
I refer you to a tutorial on JPA, like http://en.wikibooks.org/wiki/Java_Persistence/ManyToOne and http://en.wikibooks.org/wiki/Java_Persistence/OneToMany.
Now, without seeing your actual db definitions it's a bit difficult to guess the actual structure of your program and database, but it should be something like this:
class A {
#Id
#Column(name="ID")
long Id;
#OneToMany(mappedBy="a")
List<B> bees;
}
class B {
#ManyToOne
#JoinColumn(name="A_ID") // Note that A_ID is a column in the B table!!!
A a;
}
With the example above you could just select any list of B's you need, and JPA will automatically fetch the associated A for each found B. You don't need to do anything to be able to access it, b.a.Id will just work.
As we also have the OneToMany relationship, every A can have multiple B's associated with it. So, for any select that fetches a set of A's, each returned A's bees field will give access to the proper list of B objects, without the need to pull the B able into the query.

Related

JPQL, Can I Create DTO using Constructor which Takes Entities by Selecting All Columns In Once?

I'm using Spring Data JPA and Hibernate. I made repository having a method returning List<AWithBDto> like
#Query(
"""
select new com.test.example.AWithBDto(a.a1, a.a2, ..., b.b1, b.b2, ...)
from A a join B b where ~
order by ~
"""
)
Because I thought listing all columns of table a and b is tedious, I added a constructor in AWithBDto as follows. (in Kotlin)
data class AWithBDto(
...
) {
constructor(a: A, b: B) : this(
a1 = a.a1,
a2 = a.a2,
...,
b1 = b.b1,
b2 = b.b2,
...,
)
}
So I could shorten my repository codes as follows.
#Query(
"""
select new com.test.example.AWithBDto(a, b)
from A a join B b where ~
order by ~
"""
)
However, the query made kind of N+1 problems because it selected only id columns from both tables.
select
a0_.id as col_0_0_,
b1_.id as col_1_0_
from
a a0_
inner join
b b1_
on a0_.id=b1_.id
where
~
order by
~
and multiple queries selecting all columns for each single row from each table were executed.
select
a0_.id as id1_3_0_,
...,
...,
from
a a0_
where
a0_.id=?
...multiple times
select
b0_.id as id1_4_0_,
...,
...,
from
b b0_
where
b0_.id=?
...multiple times
In JPQL, Can I create DTO using a constructor which takes entities as params by selecting all columns in once? I want Hibernate to execute a single query for this.
Or if I can't, could you suggest good alternatives for me?
I think this is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(A.class)
public interface AWithBDto {
#IdMapping
Long getId();
String getA1();
String getA2();
// Either map properties through nested mapping path expressions
#Mapping("b.b1")
String getB1();
#Mapping("b.b2")
String getB2();
// Or through a subview
BDto getB();
#EntityView(B.class)
interface BDto {
#IdMapping
Long getId();
String getB1();
String getB2();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
AWithBDto a = entityViewManager.find(entityManager, AWithBDto.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<AWithBDto> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!

#SecondaryTable with where condition

I am creating entity for table created outside of my system. I want to bind data from other table to entity field by using #SecondaryTable (or possibly better solution), but only to do so if condition is met. IE. my table has 1 row, I want to bind data from other table (oneToMany) where certain condition is met (exactly one match from other table(transform to one to one)). Can I use #Where annotation and how? If not is there alternative?
Edit: here is the entity and additional info on the related table
#Entity
#Table(name = "RE_STORAGE_INSTANCE")
public class Movie {
#Id
#Column(name="ID_")
private Long id;
...
//Column I want to fetch
private Date dueDate;
}
Table RE_VARIABLES manyToOne to table RE_STORAGE_INSTANCE, contains fields: re_key, re_value. I want to fetch re_value only if 're_key' equals dueDate. Even though it's manyToOne, only one row of RE_VARIABLES contains due date for each RE_STORAGE_INSTANCE row.

Hibernate search not updating the index for a view, when view is updated on database

In my project we are using Lucene and Hibernate Search/ORM 5.9.2. It works perfectly fine if a table is updated on database, same changes are reflected on ES indexes. But the index for a database view is not updated. Values are present under the database but not under the indexes.
What corresponding strategy/logic do I need to implement this through Hibernate search?
For any of the code referencing my implementation could be found under implemented code under the Java files.
Further, Lets assume I've 2 classes A & B and C is the view for A+B. And I'm storing C in my elastic indexes. Once A & B are updated under the Database, is it possible C will get updated? As I'm using Hibernate search+lucene annotations.
When a table is updated under database through Hibernate entities, Hibernate search is updating the same under the indexes. But this behavior is not happening for views as they are updated on the SQL Server side. Could this be achieved through Lucene/Hibernate search?
Hibernate Search is operating at the entity level. So it does not see the changes you make directly in your database.
For Hibernate Search to manage your index, you need to rely on entities.
There's really no option for it to do things differently as this is what it has been designed for.
If the changes are made to entities, you can implement your own listener layer to deal with C or make C an entity and implement a trigger dropping the update queries on it if your database can do it.
If the changes are made directly to the database, then you're out of luck with Hibernate Search. Or you have to trigger some service in your app, every time you make a change to the database to trigger the indexing.
BTW, do you need this view at the database level? Because if you only need it at the Elasticsearch level, you can make C a proper entity having a #OneToOne to A and B and index the content of A and B using #IndexedEmbedded and #ContainedIn on the other side. That would solve your issue with native Hibernate Search.
I think I would try something like:
#Entity
#Indexed
public class C {
#Id
#GeneratedValue
private Long id;
#OneToOne(mappedBy = "a")
// you might need a prefix if some fields are common between a and b
#IndexedEmbedded
private A a;
#OneToOne(mappedBy = "b")
// you might need a prefix if some fields are common between a and b
#IndexedEmbedded
private B b;
}
Then in A (same for B):
#Entity
#Indexed
public class A {
// the existing content of your A class
#OneToOne(cascade = CascadeType.ALL)
#ContainedIn
private C c;
public setC(C c) {
if ( c != null ) {
c.setA( this );
}
this.c = c;
}
}
Then when first creating A and B, you also need to create a C and inject it in A and B:
C c = new C();
a.setC( c );
b.setC( c );
em.persist( a );
em.persist( c );
Once this is done, whenever you update A or B, C should be reindexed thanks to the #ContainedIn annotation.

Upgrade of spring data neo4j 3.x to 4.x Relationship Operations

In Spring data neo4j 3.x To create relation ship between two nodes and relationship contains set of properties earlier used to achieve this by apis
create :
n4jOperations.createRelationshipBetween(Object start, Object end, Class<R> relationshipEntityClass, String relationshipType, boolean allowDuplicates);
delete:
n4jOperations.deleteRelationshipBetween(Object start, Object end, String type);
get:
n4jOperations.getRelationshipBetween( from, to, relationshipClass, relationshipType );
But after migration i didnt't find above apis
as per docs says
#NodeEntity
public class Student {
private String name;
#Relationship(type = "ENROLLED")
private Set<Enrollment> enrollments;
}
By repo.save(Student);
//Relation creation was possible but new api's how can i achieve below use cases
1.How can avoid duplicate relation creation?
2.get Relation ship between two nodes ?
2.delete relation ship between two nodes ?
SDN 4 does not provide low-level graph-operations like setting nodes and relationships directly.
Relationships in the graph are modelled and manipulated using object references in your domain classes. They come in two flavours: implicit and explicit. Implicit relationships are described by simple references between two node entities, e.g. Customer and Address:
class Customer {
#Relationship(type="LIVES_AT")
Address address; // implied (:Customer)-[:LIVES_AT]->(:Address)
...
}
Explicit relationships are modelled using RelationshipEntity objects, and are allowed to have properties (but don't have to). They are still accessed as references in your domain model.
class Person {
#Relationship(type="RATED")
List<Rating> ratings
}
class Movie {
}
#RelationshipEntity(type="RATED")
class Rating {
#StartNode Person person;
#EndNode Movie movie;
int stars;
}
Note: If you don't need properties on a particular relationship, you don't need to use a RelationshipEntity.
To answer your specific questions:
1) SDN 4.0 doesn't create duplicate relationships. No matter how many times you persist a specific object reference, it will represented by only one relationship in the graph.
2) Hopefully that is clear now!
3) Setting an object reference to null and saving the parent object will remove the relationship. Or, if the reference is part of Collection, remove it from the collection. You must ensure that the object references are removed from both sides. For example if A holds a reference to B and B holds a reference to A, you must remove A's reference to B as well as B's reference to A.

Grails many to many with 3 classes: sorting by the number of relationships

Let's say we have 3 domain classes: 2 classes related with each other through a 3rd class.
Ok, some code:
class A {
String subject
String description
static hasMany = [cs: C]
static transients = ['numberOfCs']
Long getNumberOfCs() {
return cs.size()
}
}
class B {
String title
}
class C {
A objectA
B objectB
static belongsTo = [a: A]
}
Pretty clear? I hope so. This work perfectly with my domain.
You can see the transient property numberOfCs, which is used to calculate the number of C instances related to my A object. And it works just fine.
The problem: listing all my A objects, I want to sort them by the number of relationships with C objects, but the transient property numberOfCs cannot be used for the scope.
How can I handle the situation? How can I tell GORM to sort the As list by numberOfCs as it would be a regular (non transient) field?
Thanks in advance.
I'm not sure that Grails' criteria do support this, as you need both to select the A object itself and aggregate by a child objects (C). That means grouping by all the A's fields, which is not done automatically.
If you only need some fields from A, you can group by them:
def instances = A.withCriteria {
projections {
groupProperty('subject')
count('cs', 'cCount')
}
order 'cCount'
}
otherwise you'll need to retrieve only ids and make a second query, like in this question.
Another way is to use derived properties like described here (not sure it will work though):
class A {
static mapping = {
numberOfCs formula: 'select count(*) from C where c.b_id = id'
}
}
I wouldn't consider your Problem GORM related but rather general Hibernate or even SQL related.
Take a look at the HQL Docu they are a lot of examples.
check the following HQL this pretty close what you are asking.
select mySortedAs from A mySortedAs left join mySortedAs.cs myCsOfA order by
count(myCsOfA)
I think I saw somewhere that you also can do something like this myCsOfA.length or myCsOfA.size

Resources