If I have Three Tables, 1 user can work into Multiple projects and the same is true that one project can have multiple users. To Achive this I have three tables
1) User ---> UserEntity
2) Project ---> ProjectEntity
3) UserProjectMapping --> Join Table
Now If I try to create a bidirectional mapping and use Maven to get the JSON response using Hibernate Since I will be defining a Join in UserEntity using Join Table. On one of the entity, I will have to define #JsonbTransient, else it will go into Circular Loops and I will get Stackover flow exception.
org.eclipse.yasson.internal.serializer.AbstractDateTimeSerializer.serialize(AbstractDateTimeSerializer.java:52)
org.eclipse.yasson.internal.serializer.AbstractContainerSerializer.serializerCaptor(AbstractContainerSerializer.java:95)
org.eclipse.yasson.internal.serializer.ObjectSerializer.marshallProperty(ObjectSerializer.java:92)
org.eclipse.yasson.internal.serializer.ObjectSerializer.serializeInternal(ObjectSerializer.java:61)
#ManyToMany(fetch = FetchType.EAGER,cascade= CascadeType.ALL)
#JoinTable(name= "UserProjectMapping ", joinColumns = #JoinColumn(name = "User_id"),
inverseJoinColumns = #JoinColumn(name = "Project_ID") )
List<ProjectEntity> ProjectList = new ArrayList<ProjectEntity>();
On Project
#ManyToMany( cascade= CascadeType.ALL, mappedBy= "ProjectList")
List<UserEntity> UserEntityList = new ArrayList<UserEntity>();
But after defining #JsonbTransient on getters I will not get the corresponding response. So let's say I define #JsonbTransient on getter of UserEntityList in ProjectEntity, I will not be getting the response of the User list when I am calling the ProjectEntity?
SO is there any way I cam make bidirectional mapping work on JSON, or is it safe to conclude JSON HIbernate don't support complete bidirectional mapping?
Related
Hello i have 2 tables with relation ManyToMany but i am getting this error after if fetch from the api:
Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException:
My Classes are ManyToMany as example:
Class A:
#ManyToMany( fetch = FetchType.LAZY , cascade = [
CascadeType.PERSIST,
CascadeType.MERGE
])
#JoinTable(name = "material_formula_material_set",
joinColumns = [JoinColumn(name = "material_formula_id", referencedColumnName = "id")],
inverseJoinColumns = [JoinColumn(name = "material_set_id", referencedColumnName = "id")]
)
val materialSet: Set<MaterialSet>?,
And Class B:
#ManyToMany(mappedBy = "materialSet")
val materialFormulas: Set<MaterialFormula>?,
I also tried to use #JsonManagedReference and #JsonBackReference and JsonIgnore to one of them but still not working :\
You need to annotate a reference to your parent class in your child class with #JsonIgnore annotation. Since it is many to many relationship it might be not that obvious to see who is parent and who is the child. But in any case the infinite recursion occurs because class A references B and B in turn references A. So, lets say in B you will need to annotate reference to A with #JsonIgnore and it will solve the problem
Is it possible to map hibernate entities without using another table that is mapping them ?
When I create #OneToMany and #ManyToOne relation between 2 entities, hibernate always creates another table in the database to map the relation, I would like to map 2 entities directly using column in the mapped entity like this:
"InstalledApp" entity:
#OneToMany (fetch = FetchType.EAGER, targetEntity=InstalledAppPort.class, mappedBy = "id")
private List<InstalledAppPort> ports;
"InstalledAppPort" entity:
#ManyToOne (targetEntity=InstalledApp.class, fetch=FetchType.LAZY)
#JoinColumn(name = "iappId")
private InstalledApp iapp;
When using the code above, the list is always empty and I do not know why. Technically this mapping should work but it is not. No exception thrown.
Solved by using:
#OneToMany (fetch = FetchType.EAGER, targetEntity=InstalledAppPort.class)
#JoinColumn(name = "iappId", referencedColumnName = "id")
private List<InstalledAppPort> ports;
I'm trying to use spring-data-jpa against a mysql 5.7 database to find entities using a regular expression. My native query on a jpaRepository method is producing errors.
I'm replacing an old custom-built c++ server used for licensing with Spring. I cannot change the database structure nor the api.
I'm using spring-boot-starter-data-jpa:2.1.4.RELEASE, which users hibernate-core:5.3.9.Final and spring-data-jpa:2.1.6:RELEASE.
My api implements the following endpoint: licenses/search/{fieldName}:{regex}/{limit}/{offset}
for example: licenses/search/edition.name:"^Edition X$"/1/0
My DBLicense entity has a #OneToMany relationship with DBEdition.
At first I tried writing a query method in a LicenseRepository, as described here:
#Repository
public interface LicenseRepository extends JpaRepository<DBLicense, Long> {
...
List<DBLicense> findByEditions_NameRegex(String searchStr, Pageable pageRequest);
...
}
But I kept receiving the following error: unsupported keyword regex (1): [matchesregex, matches, regex]. The documentation indicates that regex might not be supported, and to check store-specific docs, which I could not find. Other answers led me to try the #Query annotation.
Because JPQL does not support regex, I opted to use the native query:
#Repository
public interface LicenseRepository extends JpaRepository<DBLicense, Long> {
...
#Query(value = "select l.* from licenses as l join licenseeditions as le on l.LicenseID=le.LicenseID join editions as e on le.EditionID=e.EditionID where e.Name regexp :searchStr limit :offset, :limit", nativeQuery = true)
List<DBLicense> findByEditions_NameRegex(#Param("searchStr") String searchStr, #Param("offset") Integer offset, #Param("limit") Integer limit);
...
}
and I receive the following error:
2019-07-18 11:46:50.145 WARN 24524 --- [nio-8080-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: S0022
2019-07-18 11:46:50.146 ERROR 24524 --- [nio-8080-exec-2] o.h.engine.jdbc.spi.SqlExceptionHelper : Column 'ParentID' not found.
My DBLicense class:
#Entity
#Table(name = "licenses")
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class DBLicense {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "LicenseID")
...
#ManyToOne
#JoinTable(name = "licensekinships", joinColumns = #JoinColumn(name = "ChildID", referencedColumnName = "LicenseID"), inverseJoinColumns = #JoinColumn(name = "ParentID", referencedColumnName = "LicenseID"))
private DBLicense parentLicense;
...
#OneToMany
#JoinTable(name = "licenseeditions", joinColumns = #JoinColumn(name = "LicenseID", referencedColumnName = "LicenseID"), inverseJoinColumns = #JoinColumn(name = "EditionID", referencedColumnName = "EditionID"))
#Setter(AccessLevel.NONE)
#Builder.Default
private List<DBEdition> editions = new ArrayList<DBEdition>();
}
The query executes successfully in mysql (I checked the log), and an error is thrown sometime after it returns inside Spring.
Notice that none of the tables referenced in my #Query (i.e. licenses,licenseeditions,editions) contain a 'ParentID' column. 'ParentID' is found on licensekinships, which is the relationship table of the many to one relationship between licenses and licenses.
Does my native query need to account for all the other relationship annotations on DBLicense? That's problematic because there are a LOT (the built-in LicenseRepository findById method executes no less than 59 queries!).
If you're using the hibernate/javax.persistence relationship annotations on your entities (i.e. #OneToOne, #OneToMany, #ManyToOne, #ManyToMany), and you attempt to use native queries, then you may have the same issue as presented in the question post.
This will happen especially if you have a complex schema wherein one entity shares a relationship with another, which in turn has further relationships, and you're trying to return one of those entities from a native query. To fix this error, you will need to provide enough information in your native query for spring-data-jpa to resolve the relationships which are present in your entities.
For example, consider the following class objects:
#Entity
#Table(name = "entity_a")
public class EntityA {
#Column
private int entityA_field
...
#ManyToOne
private EntityB entityB
}
and
#Entity
#Table(name = "entity_b")
public class EntityB {
#Column
private int entityB_field
...
#ManyToOne
private EntityC entityC
}
the JpaRepository built-in findById method for a EntityA id might execute multiple database queries. Using a sql database, for example:
select a.*, b.* from entity_a as a left outer join entity_b as b on a.id = b.id;
select b.*, c.* from entity_b as b left outer join entity_c as c on b.id = c.id;
you will need to mimic that first query's joins and columns. Luckily, you can see the pseudo-sql generated by spring-data-jpa by turning on logging:
find and open your application.properties (or application.yaml) file. Usually is located in "src/main/resources".
Add the following line if not present: spring.jpa.show-sql=true, then save.
Make a repository for the entity returned by your native query. For example, if your native query returns an EntityA, then your repository might look like:
#Repository
public interface MyRepository extends JpaRepository<EntityA, Long> {}
Call your repository findById method (from a controller, or a test) and check the console output. A number of queries will be logged to the console. Your native query needs to provide the same columns and implement the same joins as the first of these queries.
I have one of the weird issues that make you growl. I'm having (also weird) Spring/Hibernate application, that is intended to manage database in following way (i've simplified some things, so don't be confused that source code mentions slightly different tables/columns):
active_proxy_view table:
id | entity
<uuid> | <string containing json>
archive_proxy_view table:
id | entity
<uuid> | <string containing json>
track_reference table:
ref_type | ref_id | track_domain | track_type | track_id |
'proxy' | <uuid> | 'example.com' | 'customer' | '123' |
Keeping two tables is mandatory - i need to have both all-time-history/statistical queries and business-value queries only for things that being active right now, so i need to keep set for active proxies tight. track_reference table is used for searches so i could do queries like that:
SELECT p.id, p.entity FROM archive_proxy_view AS p
INNER JOIN track_reference AS t1 ON
t1.ref_id = p.id AND
t1.ref_type = 'proxy' AND
t1.track_domain = 'example.com' AND
t1.track_type = 'customer' AND
t1.track_id = '123'
INNER JOIN track_reference AS t2 ON
t2.ref_id = p.id AND
t2.ref_type = 'proxy' AND
t2.track_domain = 'example.com' AND
t2.track_type = 'campaign' AND
t2.track_id = 'halloween-2017'
(it may be not 100% correct, i haven't raw sql experience in a while)
And here's the problem:
Both active_proxy_view and archive_proxy_view entities are inherited from one class that specifies #OneToMany relationship on track_reference entity; #ManyToOne usage is not really possible, because there are many entities tied to tracking reference
track_reference is managed separately (and this is mandatory too)
I need to manage views separately from track_reference table, but whenever i tell Hibernate to remove entity from active_proxy_view table, it takes away track_reference entities as well. Even if i play with cascade annotation value, which is blank by default (and as i understand, it means that child records should not be deleted with parent). There is possibility that i've missed something, though.
I also failed to hack the whole thing using custom #SQLDeleteAll, i still can see regular deletes in general log:
55 Query delete from tracking_reference where referenced_entity_id='13c6b55c-f9b7-4de7-8bd4-958d487e461c' and referenced_entity_type='proxy' and tracked_entity_type='agent'
55 Query delete from tracking_reference where referenced_entity_id='13c6b55c-f9b7-4de7-8bd4-958d487e461c' and referenced_entity_type='proxy' and tracked_entity_type='lead'
55 Query delete from tracking_reference where referenced_entity_id='13c6b55c-f9b7-4de7-8bd4-958d487e461c' and referenced_entity_type='proxy' and tracked_entity_type='source'
53 Query DELETE FROM `tracking_reference` WHERE `referenced_entity_type` = 'proxy' AND referenced_entity_id = '13c6b55c-f9b7-4de7-8bd4-958d487e461c' AND 1 = 0
I'm using Hibernate 5.2.3.Final through Spring 4.3.2.RELEASE / Spring Data JPA 1.10.2.RELEASE
TL; DR
So, the question is: how do i prevent Hibernate from deleting associated entities when parent is deleted?
The source code for entities looks like this:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class ProxyViewEntryTemplate {
#Id
#NotNull
#Column(nullable = false)
private String id;
#NotNull
#Column
private String entity;
// some other columns
#OneToMany
#JoinColumn(name = TrackRef.REFERENCE_ID_COLUMN_NAME) // 'reference_entity_id`
#Where(clause = ProxyView.TRACK_WHERE_JOIN_CLAUSE) // `referenced_entity_type` = 'proxy'
#SQLDeleteAll(sql = ProxyView.TRACK_DELETE_ALL_QUERY) // DELETE FROM `tracking_reference` WHERE `referenced_entity_type` = 'proxy' AND referenced_entity_id = ? AND 1 = 0
private Collection<TrackingReference> track = new ArrayList<>();
// setters, getters, hashCode, equals
}
#Entity
#Table(name = "active_proxy")
public class ActiveProxyViewEntry extends ProxyViewEntryTemplate {}
#Entity
#Table(name = "tracking_reference")
#IdClass(TrackingReferenceId.class)
public class TrackingReference {
#Id
#Column(name = "tracked_entity_type", nullable = false)
#NotNull
private String trackedType;
#Id
#Column(name = "tracked_entity_domain", nullable = false)
private String trackedDomain;
#Id
#Column(name = "tracked_entity_id", nullable = false)
private String trackedId;
#Id
#Column(name = "referenced_entity_type", nullable = false)
#NotNull
private String referencedType;
#Id
#Column(name = "referenced_entity_id", nullable = false)
#NotNull
private String referencedId;
// setters, getters, hashCode, equals
}
The whole thing is managed through Spring JPA Repositories:
#NoRepositoryBean
public interface SuperRepository<E, ID extends Serializable> extends PagingAndSortingRepository<E, ID>,
JpaSpecificationExecutor<E> {
}
public interface ActiveProxyViewRepository extends SuperRepository<ActiveProxyViewEntry, String> {}
// the call for deletion
public CompletableFuture<Void> delete(ID id) {
...
descriptor.getRepository().delete(descriptor.getIdentifierConverter().convert(id));
...
}
// which is equal to
...
ActiveProxyViewRepository repository = descriptor.getRepository();
String uuidAsString = descriptor.getIdentifierConverter().convert(id);
repository.delete(uuidAsString);
...
If you remove the #JoinColumn, you shouldn't have this problem.
If you need to keep the #JoinColumn, you need to remove the foreign-key constraint requirement that gets automatically applied by the persistence provider by changing the annotation to:
#JoinColumn(
name = "yourName"
foreignKey = #Foreignkey(value = ConstraintMode.NO_CONSTRAINT)
)
You should then be able to delete the view entity without forcing the tracking references to be removed.
It turned out to be a typical shoot yourself in the foot scenario.
Tracking references were updated in a rather sophisticated way:
Build collection of references to be stored in database (C1)
Load all present references (C2)
Store C1
Delete all references that are present in C2 but not referenced in C1 (using collection.removeAll)
And it turned out that my .equals method has been written terribly wrong, returning false in nearly in each case. Because of that, usually every reference was deleted from database (the queries you can see in the log in question), so it was my fault.
After i've fixed that, only #SQLDeleteAll query was run - for reasons not known to me, it still acted like if cascade option was set. I managed to get rid of it using #OneToMany(updatable = false, insertable = false); it seems like a dirty hack, but i don't have enough time to dig it through.
I haven't tested it thoroughly yet, but, i hope, that solves the problem.
I am trying to map two entities User(userid,name,password,address) and Role(roleId,roleName).
I am trying to do bidirectional OneToMany mapping between User and Role.
My User entity:
#OneToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
#JoinTable(
name="UserRole",
joinColumns = #JoinColumn(name="USER_ID"),
inverseJoinColumns = #JoinColumn(name="ROLE_ID")
)
public Set<Role> role;
My Role entity:
#ManyToOne(cascade = CascadeType.ALL)
#JoinTable(
name="UserRole",
joinColumns= #JoinColumn(name="ROLE_ID")
)
private User user;
Now the corresponding intermediate table created in the database has following attributes.
userrole-> attributes( user_userId,ROLE_ID,USER_ID )
Now when i add set of items to a user. ROLE_ID and USER_ID of userrole table gets populated but user_userId remain null. What is the purpose of user_useerId.Should i manully make it primary key in the table user_userId?
UPDATE:
i did the following editing in Role entity
#ManyToOne(cascade = CascadeType.ALL)
#JoinTable(
name="UserRole",
joinColumns = #JoinColumn(name = "ROLE_ID"),
inverseJoinColumns = #JoinColumn(name="USER_ID")
)
private User user;
And now when i checked the table in the database, the "userrole" table is perfectly ok and contains only (USER_ID,ROLE_ID)
I want to know why shouldn't i map two entities though this way?
In User entity you have declared #OneToMany with Role and also given details about the Join table using #JoinTable.
So if you need bidirectional between User and Role entities, then adding user property in Role entity with #ManyToOne declaration is sufficient, so no need of having #JoinTable once again.
Now coming to user_userId - in your Role entity you have declared the #JoinTable annotation and this annotation needs two column names for Join Table.
#JoinTable(
name="JoinTableName",
joinColumns = #JoinColumn(name="ID_NAME_FOR_THIS_ENTITY"),
inverseJoinColumns = #JoinColumn(name="ID_NAME_FOR_THE_MAPPING_ENTITY")
)
but you have provided one with name ROLE_ID and ignore the second name which is inverseJoinColumns that points to User entity.
#JoinTable(
name="UserRole",
joinColumns= #JoinColumn(name="ROLE_ID")
)
so hibernate should make a guess about the column name for inverseJoinColumns, so it creates a column with name as combination of the entity name in lower case (which is user in your case) then the identifier in that entity separated by underscore (which is userId I guess based on column name). So finally the column name becomes user_userId.
Update:
If you need bidirectional relationship then you need to declare your entities like this:
#OneToMany(mappedBy="user", cascade=CascadeType.ALL,fetch=FetchType.EAGER)
public Set<Role> role;
Here you are telling to hibernate that User entity has one-to-many relationship with Role entity and the relationship is bi-directional and the Role entity has a property called user.
Now in your Role entity you will give details about the JoinTable like this:
#ManyToOne(cascade = CascadeType.ALL)
#JoinTable(
name="UserRole",
joinColumns = #JoinColumn(name="ROLE_ID"),
inverseJoinColumns = #JoinColumn(name="USER_ID")
)
private User user;
The property name user in Role entity should match with the mappedBy attribute that you have declared in your User entity for the #OneToMany annotation. Adding the user field to Role entity makes the relationship bi-directional.
A (typical) bidirectional mapping has one side that is maintaining the relationship and the other side follows this relation ship.
This mean when you modifiy the maintaining side of the relation ship and save this change, then it gets stored in the database. While when you modifiy the following side, then nothing gets changed in the database. (it gets only updated when you load the entity).
A mapping would looks like:
#Entity
public class User {
...
#OneToMany(mappedBy="user") //mappedBy makes this side to the following side
public Set<Role> role;
...
}
#Entity
public class Role {
...
#ManyToOne
//#JoinTable... when you need it
private User user;
...
}
But this mapping is strange: because it mean that every User can have serveral roles, but each role belongs to exactly one user. -- If this is not intended, then switch ManyToOne and OneToMany or move over to ManyToMany