Spring Roo Cardinality Mapped By attribute - jpa

I am trying to map an Address entity and a Person entity using a OneToMany mapping: "Each person has only one address but an Address can have many people".
#RooJavaBean
#RooToString
#RooEntity(identifierColumn = "addressID")
public class Address {
#OneToMany(cascade = CascadeType.ALL, **mappedBy = "address**")
private Set<Person> persons = new HashSet<Person>();
}
I cannot figure out what to map the Address entity with (bold), i have very little experience with JPA(Eclipse Link) and Spring roo but i thought the mappedBy should equal addressID and for a bidirectional ManyToOne on my Person entity mappedBy should equal personID?

As any JPA docs would tell you (all JPA implementations provide them), mappedBy is the name of the field of type "Address" in Person class. If you don't have a field of that type in Person, then the relation is not bidirectional and so you don't use "mappedBy"

Related

JaVers, SpringDatat, JPA: Querying for Entity Update inside a Collection

I'm new to Stackoverflow, so I will make my best to conforms with usage. I was wondering if there were a way to get a complete list of changes/snapshots of a given Entity. For now it works well with edition of Singular Properties, as well as Addition and Deletion to Collection Property. But I'm unable to find when a Child Entity in the Collection Property was updated.
Given two Entities, and a LinkEntity:
#Entity
class Person {
#Id
Long id;
#OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
Set<LinkAddress> addresses;
}
#Entity
class Address {
#Id
Long id;
#OneToMany(mappedBy = "address")
Set<Address> persons;
}
#Entity
class LinkPersonAddress {
#Id
Long id;
#ManyToOne
#ShallowReference
Person person;
#ManyToOne
#ShallowReference
Address address;
String linkType;
}
My use case is following. I get a specific Person by Id #1, and then mutate the type of specific Address (ie. HOME --> WORK). I save the Person back with the modified Set and let JPA Cascade my changes. Although all Spring Data Repositories for Person, Address, and LinkPersonAddress are annotated with #JaversSpringDataAuditable, I cannot retrieve this "update" using Javers QueryBuilder with the class Person and Id #1. It makes sense as I should query the class LinkPersonAddress instead, but how can I specify that I want only the changes from LinkPersonAddress relevant to Person with Id #1.
PS: Please apologize any typos in code snippets, as I didn't write it in my Dev Environment.
Let's start from the mapping. You did it wrong, Address is a classical ValueObject (see https://javers.org/documentation/domain-configuration/#value-object) not Entity. Because:
Address doesn't have its own identity (primary key genereted by a db sequence doesn't count)
Address is owned by the Person Entity. Person with its Addresses forms the Aggregate.
When you correct the mapping, you can use ChildValueObjects filter, see https://javers.org/documentation/jql-examples/#child-value-objects-filter

Hibernate Search - search in ManyToOne relation

I have two indexed entities.
Player:
#Indexed
#Entity
public class Player {
#Field
private String firstName;
#ContainedIn
#ManyToOne
private Club playersClub;
}
and Club:
#Indexed
#Entity
public class Club {
#Fields({
#Field(store=Store.COMPRESS),
#Field(name = "sorting_name", analyze = Analyze.NO)
})
private String name;
#IndexedEmbedded(depth = 1)
#OneToMany(mappedBy = "playersClub")
private Set<Player> players;
}
Now when I search into ClubSearchService like this:
luceneQuery = queryBuilder
...
.onField("name").andField("players.firstName")
...
it works fine, but when I want search in other way(PlayerSearchService):
.onField("firstName").andField("lastName").andField("number").andField("country").andField("playersClub.name")
there is an error
org.hibernate.search.SearchException: Unable to find field playersClub.name in pl.domain.Player
Hibernate Search cannot search into ManyToOne item?
Yes it is possible to index ManyToOne relations as well.
In most simple cases you have one entity such as Club marked as #Indexed and then you want to index some of its fields and also embed via #IndexedEmbedded attributes from a related entity Player.
So far you got it right, and this is defining essentially the "flattened" schema for your club index.
The problem is that when defining the schema for the player index you marked firstname as an indexed field, but didn't instruct it to embed the relation to playersClub. Essentially you're missing the #IndexedEmbedded on this property.
To not be confused with the #ContainedIn annotation: this is purely to make sure that Hibernate Search will re-index the entities in which it is contained; it is not making both sides embedding each other in a symmetric way.
In more formal terms, the relation established by #IndexedEmbedded is a directed graph: if you want it to follow both directions you have to establish them both explicitly.

Does #OneToOne imply uniqueness?

I annotated my fields with only #OneToOne and when I check the database (generated using liquibase) saw that there are unique constraints on database columns.
Does this mean #OneToOne implies uniqueness just by itself, eg. one Building can only be in one city, and no other Buildings can be in the same city?
What do I do when I want to tell that there may be other other buildings in the same city?
add #JoinColumn(unique = false),
only use #JoinColumn(unique = false) without #oneToOne,
or use #ManyToOne?
or leave it without any annotations?
I don't want to put a Buildings field in the city class, because I wouldn't ever call city.getBuildings();. Does any of the below require a bidirectional reference?
class Building {
#OneToOne(optional = false)
City city;
}
class Building {
#OneToOne(optional = false)
#JoinColumn(unique = false)
City city;
}
class Building {
#JoinColumn(unique = true)
City city;
}
class Building {
#ManyToOne
City city;
}
The JPA specification says that for a bidirectional OneToOne relationship (2.10.1 Bidirectional OneToOne Relationships):
Assuming that:
Entity A references a single instance of Entity B.
Entity B references a single instance of Entity A.
Entity A is specified as the owner of the relationship.
The following mapping defaults apply:
Entity A is mapped to a table named A.
Entity B is mapped to a table named B.
Table A contains a foreign key to table B. [...] The foreign key column has the same type as the primary key of table B and there is a unique key constraint on it.
In case of unidirectional OneToOne relationship (2.10.3.1 Unidirectional OneToOne Relationships):
The following mapping defaults apply:
Entity A is mapped to a table named A.
Entity B is mapped to a table named B.
Table A contains a foreign key to table B. [...] The foreign key column has the same type as the primary key of table B and there is a unique key constraint on it.
If you have a City-Building relationship, then for any reasonable city it would be a OneToMany/ManyToOne relationship, since a given city can have multiple buildings, but a given building can be only in one city.

JPA annotation for mapped entity with values based on the root entity?

I'm trying to annotate my JPA-Entity. In the entity I want to map another Entity which should be loaded by values of the root entity.
Example:
Entity A has the fields key1 & key2.
I want to map Entity B which should be loaded by the filter b.key1 = a.key1 & b2.key2 = a.key2
Can anyone help?
I got the solution. I just used #ManyToOne and #JoinColumns:
#ManyToOne
#JoinColumns(
{
#JoinColumn(name="key1", referencedColumnName="refKey1"),
#JoinColumn(name="key2", referencedColumnName="refKey2")
}
)
private EntityB entityB;

How do I properly annotate two JPA entities which are in a parent child relationship?

Maybe this is a question with an easy answer ... but I don't get it running. At persist() I get the exception that the referential key in the child table is null (which of course is not allowed by the database). I have a recipe and some steps for preparation.
I'm using EclipseLink 2.4.1
Recipe.java (rcpid is autoset by JPA)
#Entity
public class Recipe {
#Id
long rcpid;
List<Recipestep> recipesteps = new ArrayList<>();
#OneToMany(
cascade=CascadeType.ALL,
fetch=FetchType.EAGER,
mappedBy="recipe",
targetEntity=Recipestep.class )
// This does NOT work. Following line tries to access a join-table !!!
// #JoinColumn(name="rcpid", referencedColumnName="rcpid")
public List<Recipestep> getRecipesteps() { return recipesteps; }
// some more attributes, getters and setters
}
Recipestep.java (rpsid is autoset by JPA)
#Entity
public class Recipestep {
#Id
long rpsid;
Recipe recipe;
#ManyToOne( targetEntity=Recipe.class )
#JoinColumn( name="rcpid" )
public Recipe getRecipe() { return recipe; }
// some more attributes, getters and setters
}
The code above is a valid workaround. However to have clean (and supportable) code, the relationship should be only one-way with a collection in the parent which references all its children.
You have mapped this as a unidirectional one to many, but have two mappings for the recipestep rcpid database column. Try changing the long rcpid to
#ManyTOne
Recipe rcp;
And then remove the joincolumn definition from the oneToMany and make it bidirectional by marking it as mappedby the rcp manyToOne relation. An example is posted here http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Mapping/Relationship_Mappings/Collection_Mappings/OneToMany
Eclipselink will always insert nulls on unidirectional oneToMany relations using a joincolumn when first inserting the target entity, and then update it later when it processes the Recipe entity. Your rcpid mapping in Recipestep is also likely null, which means you have two write able mappings for the same field which is bad especially when they conflict like this.
You are experiencing the default JPA behaviour. Adding an entity to the recipesteps list is not sufficient to create a bidirectional relation.
To solve the issue you need to set the rcpid explicitly on every element in the list.
EDIT: I think the issue is that JPA does not know where to store the id of the Recipe in the Recipestep table. It assumes a name ("recipebo_rcpid"), but your table seems to lack it.
Try adding the column "recipe_id" to the Recipestep table and a mappedBy attribute to the #OneToMany annotation:
#OneToMany(
cascade=CascadeType.ALL,
fetch = FetchType.EAGER,
mappedBy = "recipe" )
You probably do not need the targetEntity attribute in the annotation- the List is typed already.