Hibernate - Populate sub entity on insert - java

Ive added 2 hibernate model objects
First table
#Entity
#Table(name = "ACTIVITIES")
public class ActivityMO extends ModelBase {
#Column(name = "CA_ID", nullable = false, insertable = true,updatable = true, length = 22, precision = 0)
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "G1")
#SequenceGenerator(name = "G1", sequenceName = "CSM_ACTIVITIES_SEQ")
private Long id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "activityId", cascade = {CascadeType.ALL})
#Fetch(FetchMode.JOIN)
List<ActivitiesProductsMO> relatedProducts;
...getters / setters
}
The other table is
#Entity
#Table(name = "ACTIVITIES_PRODUCTS")
public class ActivitiesProductsMO {
#Column(name = "CAP_ID")
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "G1")
#SequenceGenerator(name = "G1", sequenceName = "ACTIVITIES_PRODUCTS_SEQ")
private Long id;
#Column(name = "CAP_ACTIVITY_ID")
private Long activityId;
#Column(name = "CAP_PRODUCT_ID")
private Long productId;
...getters/setters
}
The point is to populate each db record for ActivitiesProductsMO.activityId with ActivityMO.id value
I.e.
If I create an activity record with id = 555
I'll get another activity_product record with activityId of 555
How can i get this to work?
Thank you!

Instead of manually trying to map the entitiy relations with long values you should use a bidirectional OneToMany relationship from ActivityMO to ActivitiesProductsMO
change ActivitiesProductsMO to:
#Entity
#Table(name = "ACTIVITIES_PRODUCTS")
public class ActivitiesProductsMO {
// cut unimportant code ...
#ManyToOne
#JoinColumn(name = "CAP_ACTIVITY_ID")
private ActivityMO activityId;
// cut unimportant code ...
}
If you then were to persist an ActivityMO that already has ActivitiesProductsMO entries in its relatedProducts List, the Cascade type should actually take care and create those products while filling out the CAP_ACTIVITY_ID database field with the right value.
Another Possible Solution:
Use a Unidirectional OneToMany:
#Entity
#Table(name = "ACTIVITIES")
public class ActivityMO extends ModelBase {
#OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
#Fetch(FetchMode.JOIN)
#JoinColumn(name = "CAP_ACTIVITY_ID")
List<ActivitiesProductsMO> relatedProducts;
}
And remove the
private Long activityId;
from your ActivitiesProductsMO class.
This should both lead to identical database structure. But in the second case you would no longer have the "backlink" inside java from ActivitiesProductsMO to ActivityMO

Related

hibernate search 6 updating elasticsearch index after entity update

I have a problem using elasticsearch with hibernate search 6. Let's assume we have this setup :
#Entity
#Table(name = "entityA")
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
#Indexed(index = "entityA")
public class EntityA {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
#GenericField
private Long id;
#Column(name = "name")
#KeywordField
private String name;
#OneToOne
#JoinColumn(unique = true)
#Cascade(value = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.SAVE_UPDATE})
#IndexedEmbedded
#IndexingDependency(reindexOnUpdate = ReindexOnUpdate.SHALLOW)
private EntityB entityB;
}
#Entity
#Table(name = "entityB")
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class EntityB {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
#GenericField
private Long id;
#Column(name = "name")
#KeywordField
private String name;
#OneToOne(cascade = {}, fetch = FetchType.EAGER, targetEntity = EntityA.class)
#JoinColumn(name = "id", nullable = false)
#IndexingDependency(reindexOnUpdate = ReindexOnUpdate.DEFAULT)
private EntityA entityA
}
When I first persist EntityA, that being the entity that is indexed, the EntityB is persisted in the elasticsearch index as a child of EntityA. This is ok. The problem appears when I directly edit EntityB and make changes to it, this changes are not propagated to the elasticsearch index. Is something that i am missing?
UPDATE 1
After #yrodiere answers, i made this changes :
#OneToOne
#JoinColumn(unique = true)
#Cascade(value = {CascadeType.MERGE, CascadeType.PERSIST,
CascadeType.SAVE_UPDATE})
#IndexedEmbedded
#AssociationInverseSide(inversePath = #ObjectPath(
#PropertyValue(
propertyName = "entitya" ) ))
private EntityB entityB;
The problem still persist. If i do something like this :
EntityB b = entityBRepository.findById(5051L).get();
b.setProperty("3333");
entityBRepository.save(b);
Regards.
The problem appears when I directly edit EntityB and make changes to it, this changes are not propagated to the elasticsearch index.
You explicitly instructed Hibernate Search to behave exactly that way:
#IndexingDependency(reindexOnUpdate = ReindexOnUpdate.SHALLOW)
private EntityB entityB;
reindexOnUpdate = ReindexOnUpdate.SHALLOW means "reindex EntityA when the entityB property of EntityA changes, but not when a property of EntityB itself (e.g. its name) changes".
See this section of the reference documentation.
I'm guessing you added that to get rid of an exception telling you that Hibernate Search was unable to find the inverse side of the association EntityA.entityB. In your case, it seems you should rather tell Hibernate Search what the inverse side of that association is. Either add a mappedBy to one side of the association (Warning: this will change your DB schema), or use #AssociationInverseSide (see this section of the documentation).

JPA: Reference column in the child entity is null when using unidirectional #OneToMany

I have two entity classes.
Order.java
#Entity
#Table(name = "order_table")
public class Order implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "order_id", referencedColumnName = "id", nullable = false, insertable=false, updatable=false)
private Set<Item> items;
// getters & setters & toString
Item.java
#Entity
#Table(name = "item")
public class Item implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "order_id", nullable = false)
private Long orderId;
// getters & setters && toString
I created a test class like this:
#Test
public void createOrderWithItems() {
Item item = new Item();
item.setName("Iron Man");
Order order = new Order();
order.setName("Toy");
order.getItems().add(item);
Order created = service.createOrder(order);
Order orderById = service.getOrderById(order.getId());
System.out.println("Created Order: " + orderById);
Item itemById = service.getItemById(item.getId());
System.out.println("Created item: " + itemById);
Assert.notNull(created.getId(), "Order ID is Null");
}
Test is green but if you check output, you'll see that orderId field in the Item class is null.
Created Order: Order{id=1, name='Toy', items=[Item{id=2, name='Iron Man', orderId=null}]}
Created item: Item{id=2, name='Iron Man', orderId=null}
Does JPA not update this column in the db automatically? Is this column is redundant? If so, how can I retrieve this information from test code?
You need to set orderId explicitly.
item.setOrderId(order.getId());
order.getItems().add(item);
You can create a method addItem(Item item) in your Order class and hide this logic within it.
Cascading will create an entry in db but it won't initialize field. JPA annotations just indicate to JPA provider how to perform mapping between entity and table.
Moreover, check your annotations. #JoinColumn should be used in the entity which owns the relationship (the corresponding table has column as a foreign key). Check the top answer for this question for detailed explanations: What's the difference between #JoinColumn and mappedBy when using a JPA #OneToMany association

Hibernate MappedBy for Multiple columns

I am using Postgresql for my database and it contains a table called user and a table called friendship, which has 2 foreign keys userA_id and userB_id. I know how to use mappedBy to check for friendships based on userA_id but I am not sure how to check for userB_id. Is there a way to tell hibernate to check a user ID from user table with both of columns on friendship table?
EDIT: Here is the code I currently have.
#Entity
#Table(name = "users")
public class UserDB implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "userid", nullable=false)
public int userID; //not null
#OneToMany (targetEntity = FriendshipDB.class, mappedBy = "userA_ID", cascade = CascadeType.ALL, fetch=FetchType.EAGER)
//#OneToMany (targetEntity = FriendshipDB.class, mappedBy = "userB_ID", cascade = CascadeType.ALL, fetch=FetchType.EAGER)
public List<FriendshipDB> friends = new ArrayList<>();
}
#Entity
#Table(name = "friendships")
public class FriendshipDB implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "friendshipid", nullable = false)
private int friendshipID; //not null
#ManyToOne
#JoinColumn(name="usera_id")
private UserDB userA_ID; //not null
#ManyToOne
#JoinColumn(name = "userB_id")
private UserDB userB_ID;
}
I think this is very specific mapping but the only solution I know is to go with 2 association like this:
#OneToMany(mappedBy = "user1")
private Collection<User> usersByFirst;
#OneToMany(mappedBy = "user2")
private Collection<User> usersBySecond;

Hiibernate: #OrderColumn in #OneToMany causes an UPDATE after INSERT

I have the following POJOs for my db schema.
#Entity
#Table(name = "Quotes")
public class QuoteRequest
{
public QuoteRequest(){}
#Id
#Column(name = "quote_request_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long QuoteRequestId;
#OneToMany(mappedBy = "quoteRequest", cascade = CascadeType.ALL)
#OrderColumn(name = "accidents_id")
private Accidents[] accidents;
// Getters and setters
}
#Entity
#Table(name = "Accidents")
public class Accidents
{
public Accidents()
{
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "accidents_id")
private long AccidentId;
#Column(name = "amount", nullable = false)
private Float amount;
#ManyToOne(optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "quote_request_id", nullable = false)
private QuoteRequest quoteRequest;
// Getters and setters
}
Because I'm using an array to store Accidents[] Hibernate is requiring me to add #OrderColumn. Adding this causes an update to be generated after insert that zeros out my accidents_id. The only way I found around that is to change Accidents[] to Set.
How can I keep Accidentsas an array and not have Hibernate force this 2nd update after the insert?

JPA #ManyToMany ordering failure using #OrderBy

When JPA tries to select AdmUser entity I have sql error:
ERROR: column locations1_.name does not exist.
Is there anything wrong with my entities? My AdmUser entity:
#Entity
#Table(name = "ADM_USERS")
#SequenceGenerator(name = "ADM_USER_SEQ", sequenceName = "ADM_USER_SEQ", allocationSize = 1)
public class AdmUser implements EntityInt, Serializable {
private static final long serialVersionUID = 786L;
#Id
#Column(nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ADM_USER_SEQ")
private Long id;
(...)
#ManyToMany(cascade = CascadeType.MERGE, fetch = FetchType.EAGER)
#JoinTable(name = "loc_locations_adm_users", joinColumns = #JoinColumn(name = "id_user", referencedColumnName="id"),
inverseJoinColumns = #JoinColumn(name = "id_location"))
#OrderBy("name")
private Set<LocLocation> locations;
(...)
}
My LocLocation Entity:
#Entity
#Table(name = "loc_locations", schema = "public")
#SequenceGenerator(name = "LOC_LOCATIONS_SEQ", sequenceName = "LOC_LOCATIONS_SEQ", allocationSize = 1)
public class LocLocation implements EntityInt, java.io.Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "id", unique = true, nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "LOC_LOCATIONS_SEQ")
private Long id;
#Column(nullable = false, unique = true, length = 200)
private String name;
(...)
#ManyToMany(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY, mappedBy="locations")
private List<AdmUser> users;
}
And now - when JPA tries to select AdmUser entity I have sql error. The query generated by JPA looks that:
select
admuser0_.id as id1_2_0_,
admuser0_.actived as actived2_2_0_,
admuser0_.admin as admin3_2_0_,
admuser0_.allow_ip as allow_ip4_2_0_,
admuser0_.created as created5_2_0_,
admuser0_.deleted as deleted6_2_0_,
admuser0_.id_domain as id_doma16_2_0_,
admuser0_.email as email7_2_0_,
admuser0_.language as language8_2_0_,
admuser0_.login as login9_2_0_,
admuser0_.name as name10_2_0_,
admuser0_.passwd as passwd11_2_0_,
admuser0_.phone as phone12_2_0_,
admuser0_.picture as picture13_2_0_,
admuser0_.surname as surname14_2_0_,
admuser0_.theme as theme15_2_0_,
locations1_.id_user as id_user1_2_1_,
loclocatio2_.id as id_locat2_6_1_,
loclocatio2_.id as id1_17_2_,
loclocatio2_.description as descript2_17_2_,
loclocatio2_.name as name3_17_2_
from
public.ADM_USERS admuser0_
left outer join
public.loc_locations_adm_users locations1_
on admuser0_.id=locations1_.id_user
left outer join
public.loc_locations loclocatio2_
on locations1_.id_location=loclocatio2_.id
where
admuser0_.id=1
order by
locations1_.name
The order by points to locations1_.name, but should be loclocatio2_.name. Have I anything wrong with my entities?
You have a Set on one side for that field. Consequently there is no "ordering" (other than what hashCode() gves). Use a List if you want ordering (this is Java, nothing to do with JPA really).
You also seem to be missing a "mappedBy" on the non-owner side of that M-N.
The #OrderBy works fine with ManyToMany. Also with the structure I provided in my question. The problem was my query and JPA didn't managed with it. Sorry.

Categories