Java Persistence Query Language JOIN - java

I have two tables :
A(bigint id, ...)
B(bigint id, varchar name, bigint id_A)
and now I want get all rows from A which exists in B (and those rows in B have name eg Andy)
Plase help me create dynamic query
class A
#Entity
#Table(name = "A", schema = "mySchema")
#Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
public class A{
#Id
private Long id;
}
class B
#Entity
#Table(name = "B",
schema = "mySchema",
uniqueConstraints = { #UniqueConstraint(columnNames = {
"some_id", "id_A" }) })
public class B{
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "Seq")
#SequenceGenerator(name = "Seq", sequenceName = "mySchema.mySeq")
private Long id;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_A", nullable = false)
private A a;
#Column(name = "id_A", updatable = false, insertable = false)
private Long IdA;
}

There are several weird parts. e.g. this:
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_A", nullable = false)
private A a;
#Column(name = "id_A", updatable = false, insertable = false)
private Long IdA;
With the #JoinColumn annotation you are telling the JPA provider that it should use the specified column for internal mapping, but with the IdA field, you are trying to manage the column yourself. Which is it going to be?

Related

Java Spring: ManytoOne EmbeddedId in child class with additional Id - getting null error on child specific PK

I was able to get this ManyToOne relationship with a composite pk working for GET requests but I am having issues with saving new entries.
I have a parent entity with an embedded id.
#Embeddable
public class AnnualServiceHistoryPK implements Serializable {
#Column(name = "year", columnDefinition = "int(4)")
Integer year;
#Column(name = "month", columnDefinition = "char(3)")
String month;
#Column(name = "route", columnDefinition = "varchar(32)")
String route;
This is placed in the parent entity:
#Entity(name = "AnnualServiceHistory")
#Table(name = "annual_service_history")
public class AnnualServiceHistory extends Auditable<String> implements Serializable
{
#EmbeddedId
AnnualServiceHistoryPK annualServiceHistoryPK;
... other variables
#OneToMany(mappedBy = "annualServiceHistory", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
List<AnnualServiceHistoryNonMachine> annualServiceHistoryNonMachineList;
I have the child entity which maps the embedded id from the parent and adds and additional local PK.
public class AnnualServiceHistoryNonMachine extends Auditable<String> implements Serializable {
#Id
#MapsId
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "year", referencedColumnName = "year")
#JoinColumn(name = "month", referencedColumnName = "month")
#JoinColumn(name = "route", referencedColumnName = "route")
private AnnualServiceHistory annualServiceHistory;
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "toy_inventory", referencedColumnName = "id")
ToyInventory toyInventory;
... other variables
My issue is, when I save the parent with a list of children for some reason during the cascading processes, when it is generating the child key it is only creating the key from the MapsId parts and complains that the toyInventory key is null. I'm not sure why it loses this ID because prior to performing the repository save the child class has the toyInventory key in the object.
This is the error I am receiving:
"java.sql.SQLIntegrityConstraintViolationException: Column 'toy_inventory' cannot be null"
Solution found. - https://medium.com/#bhagyajayashani/composite-key-handling-using-idclass-annotation-in-spring-boot-java-26f40bbd38a2
Changed from using #embeddedId to using #classId. Then created a separate join object, and created join based off individual variables not the embeddedId.
#Embeddable
public class AnnualServiceHistoryPK implements Serializable {
Integer year;
String month;
String route;
public AnnualServiceHistoryPK() {
}
#Entity(name = "AnnualServiceHistory")
#Table(name = "annual_service_history")
#IdClass(AnnualServiceHistoryPK.class)
public class AnnualServiceHistory extends Auditable<String> implements Serializable {
#Id
#Column(name = "year", columnDefinition = "int(4)")
Integer year;
#Id
#Column(name = "month", columnDefinition = "char(3)")
String month;
#Id
#Column(name = "route", columnDefinition = "varchar(32)")
String route;
... other variables ...
#OneToMany(mappedBy = "annualServiceHistory", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
Set<AnnualServiceHistoryNonMachine> annualServiceHistoryNonMachineList;
#Embeddable
public class AnnualServiceHistoryNonMachinePK implements Serializable {
Integer year;
String month;
String route;
ToyInventory toyInventory;
public AnnualServiceHistoryNonMachinePK() {
}
#Entity(name = "AnnualServiceHistoryNonMachine")
#Table(name = "annual_service_history_non_machine")
#IdClass(AnnualServiceHistoryNonMachinePK.class)
public class AnnualServiceHistoryNonMachine extends Auditable<String> implements Serializable {
#Id
#Column(name = "year", columnDefinition = "int(4)")
Integer year;
#Id
#Column(name = "month", columnDefinition = "char(3)")
String month;
#Id
#Column(name = "route", columnDefinition = "varchar(32)")
String route;
... other variables ...
#ManyToOne
#JoinColumns({
#JoinColumn(name = "year", referencedColumnName = "year", insertable = false, updatable = false),
#JoinColumn(name = "month", referencedColumnName = "month", insertable = false, updatable = false),
#JoinColumn(name = "route", referencedColumnName = "route", insertable = false, updatable = false)
})
#JsonIgnore
private AnnualServiceHistory annualServiceHistory;

Error relationship persistence using Spring Data JPA in a many to one

I have the following code for many to many or many to one relationship persistence using Spring JPA.
This is my repository test https://github.com/Truebu/testJpa.git
This class has three one-to-many relationships, but none work well
#Entity(name = "routine_assignament")
#Table(name = "routine_assignament")
public class RoutineAssignament {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", updatable = false)
private Long id;
#Column(name = "date_start",nullable = true,columnDefinition = "DATE")
private Date date_start = new Date();
#Column(name = "date_end",nullable = true,columnDefinition = "DATE")
private Date date_end;
#ManyToOne
#JoinColumn(name = "id_user")
private User user;
#ManyToOne
#JoinColumn(name = "id_routine")
private Routine routine;
#OneToMany(mappedBy = "routine_assignament")
private Set<Score> scores = new HashSet<>();
#OneToMany(mappedBy = "routine_assignament")
private Set<Statistic> statistics = new HashSet<>();
#OneToMany(mappedBy = "routine_assignament")
private Set<KeepRoutine> keepRoutines = new HashSet<>();
The other classes
#Entity(name = "score")
#Table(name = "score")
public class Score {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", updatable = false)
private Long id;
#Column(name = "commentary",nullable = false,columnDefinition = "TEXT", unique = true)
private String commentary;
#Column(name = "assessment",nullable = false,columnDefinition = "INT", unique = true)
private String assessment;
#ManyToOne
#JoinColumn(name = "id_routine_assignament")
private RoutineAssignament routineAssignament;
}
#Entity(name = "statistic")
#Table(name = "statistic")
public class Statistic {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", updatable = false)
private Long id;
#Column(name = "time",nullable = false,columnDefinition = "TEXT", unique = true)
private String time;
#ManyToOne
#JoinColumn(name = "id_routine_assignament")
private RoutineAssignament routineAssignament;
}
and
#Entity(name = "keep_routine")
#Table(name = "keep_routine")
public class KeepRoutine {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", updatable = false)
private Long id;
#ManyToOne
#JoinColumn(name = "id_routine_assignament")
private RoutineAssignament routineAssignament;
}
The entity relationship diagram is this:
My mistake is that it doesn't detect these relationships correctly.
When I run it it generates this:
Failed to initialize JPA EntityManagerFactory: mappedBy reference an unknown target entity property: com.example.demo.model.entities.KeepRoutine.routine_assignament in com.example.demo.model.entities.RoutineAssignament.keepRoutines
This error is reproduced with all three classes (KeepRoutine, Statistic and Score), I don't know why
Your OneToMany mapping is not appropriate. You need to use routineAssignament the property name instead of the table name routine_assignament as shown below. This property name is defined in the ManyToOne relationship.
#OneToMany(mappedBy = "routineAssignament")
private Set<Score> scores = new HashSet<>();
#OneToMany(mappedBy = "routineAssignament")
private Set<Statistic> statistics = new HashSet<>();
#OneToMany(mappedBy = "routineAssignament")
private Set<KeepRoutine> keepRoutines = new HashSet<>();

Ignore #Formula in one case

I have simple entity with field countUsing which specify count used in other table. It is subquery with annotation Formula.
And i would like in one case ignore execute query in formula but others subquery must invoke.
My entity:
#Entity
#Table(name = "roles")
public class Role {
#Id
#GeneratedValue(generator = "roles_id_seq", strategy = GenerationType.SEQUENCE)
#SequenceGenerator(name = "roles_id_seq", sequenceName = "roles_id_seq", allocationSize = 1)
#Column(name = "id")
private Long id;
#Column(name = "\"NAME\"")
private String name;
#Convert(converter = LocalDateTimeAttributeConverter.class)
private LocalDateTime insertDate;
#Convert(converter = LocalDateTimeAttributeConverter.class)
private LocalDateTime updateDate;
#Formula("(SELECT COUNT(*) FROM user_roles us WHERE us.id_role = id)")
private Integer countUsing;
}
How can i achive the target, i think about flag in any annotation for field countUsing.
Annotation Formula has got only value property.
I understand that you want use FetchType.LAZY on this attribute.
Therefore, you can`t use LAZY directly in a #Formula attribute in your class.
The solution for this is create an wrapper Class, with a oneToOne relationship to your class that`s contain your formula attribute, like this:
#Entity
#Table(name = "roles")
public class Role{
... /*other attributes*/
#OneToOne(fetch = FetchType.LAZY, mappedBy = "role")
private RoleCounting roleCouting;
}
#Entity
#Table(name = "roles")
public class RoleCounting{
#Id
#Column(name="id", insertable = false, updatable = false)
private Long id;
#OneToOne(fetch = FetchType.LAZY)
#MapsId
#JoinColumn(name = "id", insertable = false, updatable = false)
private Role role;
#Formula("(SELECT COUNT(*) FROM user_roles us WHERE us.id_role = id)")
private Integer countUsing;
}
That way, the subquery will only be executed when you invoke:
role.getRoleCounting().getCountUsing();

Hibernate with SqlServer performance drop on select

I have a performance issue with the following Hibernate TypedQuery:
select generatedAlias0 from MyClass1 as generatedAlias0
where generatedAlias0.class2.uid in (:param0, ..., :paramN)
Following the actual implementation this results in the following query:
select myclass1_.id, myclass1_.other, ... # 16 fields in total, no special big ones
from myschema.dbo.TblMyClass1 myclass1_ cross join myschema.dbo.TblMyClass2 myclass2_
where myclass1_.myclass2Id=myclass2.Id and (myclass2_.uid in('value1', ... 'valueN'))
Where N each time stands for 384 items.
When I execute this query in Toad it only takes about 150ms, but executed from code it takes almost a minute!
Class mappings
#Entity
#Table(name = "TblMyClass1", catalog = "myschema", schema = "dbo")
public class MyClass1 implements Serializable {
private static final long serialVersionUID = 8208493383744288872L;
#Id
#Column(name = "Id")
#GeneratedValue(strategy = GenerationType.AUTO)
protected Integer id;
#NotNull
#ManyToOne
#JoinColumn(name = "myclass2Id", referencedColumnName = "Id", nullable = false)
private MyClass2 class2 = null;
#NotNull
#ManyToOne
#JoinColumn(name = "myclass3Id", referencedColumnName = "Id", nullable = false)
private MyClass3 class3 = null;
#NotNull
#ManyToOne
#JoinColumn(name = "myClass4Id", referencedColumnName = "Id", nullable = false)
private MyClass4 myClass4;
#ManyToOne
#JoinColumn(name = "myClass5Uid", referencedColumnName = "UID", nullable = true)
private MyClass5 resultType;
#Column(name = "string2", nullable = true)
private String string2;
// other column fields and getters and setters ...
}
#Entity
#Table(name = "TblMyClass2", schema = "myschema", catalog = "dbo")
public final class MyCLass2 implements Serializable {
private static final long serialVersionUID = 4660579327140751989L;
#Id
#Column(name = "Id")
#GeneratedValue(strategy = GenerationType.AUTO)
protected Integer id;
#Column(name = "uid", nullable = false, updatable = false, unique = true)
private String uid;
// ... other columns fields and getters and setters
}
I only provided the mappings for the two classes in the slow query, since the follow-up queries of hibernate which fill all other linked entities all perform very good.
Using Java VisualVm I find that the following method takes 99% of the time:
com.microsoft.sqlserver.jdbc.TDSChannel.read()
I am running SQL Server 2008 and using sqljdbc4-2.0 and hibernate4.2.1 with spring3.2.9 (for transaction management)
Any help would be greatly appreciated !

JPA Many-to-Many Join table entity with compound key "null id generated "

This are my entities:
public class Account extends AbstractEntity<Long> {
#Id
#SequenceGenerator(name = "accountSequence", sequenceName = "SQ_ACCOUNTS", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "accountSequence")
#Column(name = "ACC_ID", nullable = false)
private Long id;
...
}
public class Integration extends AbstractEntity<Long> {
#Id
#SequenceGenerator(name = "integrationSequence", sequenceName="SQ_INTEGRATIONS", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "integrationSequence")
#Column(name = "INT_ID", nullable = false)
private Long id;
...
public void addIntegration(Integration integration) {
IntegrationAccount association = new IntegrationAccount();
// This does not help
//association.setIntAccountsPK(new IntAccountsPK(integration.getId(), this.getId()));
association.setAccount(this);
association.setIntegration(integration);
this.integrationAccounts.add(association);
integration.getIntAccountsCollection().add(association);
}
}
And this is entity for join table
#Entity
#Table(name = "INT_ACCOUNTS")
public class IntegrationAccount {
#EmbeddedId
protected IntAccountsPK intAccountsPK;
#JoinColumn(name = "ACC_ID", referencedColumnName = "ACC_ID", insertable = false, updatable = false)
#ManyToOne
private Account account;
#JoinColumn(name = "INT_ID", referencedColumnName = "INT_ID", insertable = false, updatable = false)
#ManyToOne
private Integration integration;
...
}
#Embeddable
public class IntAccountsPK implements Serializable {
#Column(name = "INT_ID", nullable = false)
private Long intId;
#Column(name = "ACC_ID", nullable = false)
private Long accId;
...
}
And when i do:
account.addIntegrations(integrations.getTarget());
account.setCustomer(customer);
accountService.save(account);
I got this in my log
Caused by: org.hibernate.id.IdentifierGenerationException: null id generated for:class com.dhl.dcc.domain.IntegrationAccount
I dont have many knowledge about this kind of mapping, can you please tell me how to improve this mapping (entity for join table have to be preserved) and how to save account with related integrations? Thanks.
I know this question has already been marked as solved but I disagree with the accepted answer. This answer modifies the datamodel by adding a useless column (the new id) in the table INT_ACCOUNTS. There is another way to solve this problem in Hibernate without modifying the datamodel :
#Entity
#Table(name = "INT_ACCOUNTS")
public class IntegrationAccount implements Serializable {
#Id
#ManyToOne
#JoinColumn(name = "INT_ID_FK")
private Integration integration;
#Id
#ManyToOne
#JoinColumn(name = "ACC_ID_FK")
private Account account;
}
#Entity
#Table(name = "INTEGRATIONS")
public class Integration {
#Id
#SequenceGenerator(name = "integrationSequence", sequenceName = "SQ_INTEGRATIONS", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "integrationSequence")
#Column(name = "INT_ID")
private Long id;
}
#Entity
#Table(name = "ACCOUNTS")
public class Account {
#Id
#SequenceGenerator(name = "accountSequence", sequenceName = "SQ_ACCOUNTS", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "accountSequence")
#Column(name = "ACC_ID")
private Long id;
}
You could create a ID field for your IntegrationAccount and then create a unique constraint for your two fields.
#Entity
#Table(name = "INT_ACCOUNTS",
uniqueConstraints=#UniqueConstraint(columnNames={"ACC_ID", "INT_ID"}))
public class IntegrationAccount {
#Id
private Long id;
#JoinColumn(name = "ACC_ID", referencedColumnName = "ACC_ID", insertable = false, updatable = false)
#ManyToOne
private Account account;
#JoinColumn(name = "INT_ID", referencedColumnName = "INT_ID", insertable = false, updatable = false)
#ManyToOne
private Integration integration;
...
}
Works like a charm!

Categories