Handling reduntant columns with hibernate/jpa/spring data - postgresql

i'm kinda struggling mapping the following schema with hibernate
table_a (A1_ID,A2_ID) --> PK = (A1_ID, A2_ID)
table_b (A1_ID, A2_ID, B1_ID) --> PK =(A1_ID, A2_ID, B1_ID)
where table_b's A1_ID and A2_ID should be foreingkey referencing respective table_A's columns
There is a one-to-many from TABLE_A to TABLE_B where TABLE_B's primary key is partially shared with TABLE_A's primary key
What I've tried so far
#Data
#Entity
#Table(name = "table_a")
#IdClass(TableA.TableAKey.class)
public class TableA {
#Id
#Column(name = "A1_ID)
private String a1_id;
#Id
#Column(name = "A2_ID)
private String a2_id;
#OneToMany(mappedBy = "tableA",fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<TableB> tableB;
#Data
static class TableAKey implements Serializable {
private String a1_id;
private String a2_id
}
}
**CHILD ENTITY**
#Data
#Entity
#Table(name = "table_b")
#IdClass(TableB.TableBKey.class)
public class TableB {
#Id
#Column(name = "B1_ID)
private String b1_id;
#Id
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumns({
#JoinColumn(name = "a1_id", insertable = false, updatable = false),
#JoinColumn(name = "a2_id", insertable = false, updatable = false)
)}
private TableA tableA;
#Column(name = "A1_ID)
private String a1_id;
#Column(name = "A2_ID)
private String a2_id;
#Data
static class TableAKey implements Serializable {
private String b1_id;
private TableA tableA;
}
}
I was expecting i could be able to do something like this:
TableA tableA = new TableA();
t.setA1_id("a1id");
t.setA2_id("a2id");
TableB tableB = new TableB();
tableB.setB1Id("b1Id");
tableA.setTableB(Arrays.asList(tableB));
tableARepository.save(tableA);
And the code above I was expecting to "magically" perform the following insert at DB
INSERT INTO table_A (A1_ID,A2_ID) VALUES ('a1id',a2id');
INSERT INTO table_B (A1_ID,A2_ID, B1_ID) VALUES ('a1id',a2id','b1id')
but instead i get a "the column index is out of range: n, number of columns n-1".
I also tried with some embeddedId approach, using referenceColumnName but nothing.
Am I doing something wrong in the mapping or in the object creation process?
The problem is a lot similar to the following
https://hibernate.atlassian.net/browse/HHH-14340

Related

Hibernate: getting entity from PostgreSQL view

Let's imagine that we have two entities in the database. We have a simple Server with a unique Id:
PostgreSQL:
CREATE TABLE public.servers (
id bigint NOT NULL,
name character varying(64) NOT NULL
);
ALTER TABLE ONLY public.servers
ADD CONSTRAINT servers_pkey PRIMARY KEY (id);
Class entity:
#Entity
#Table(name = "servers")
public class Server {
#Id
private long id;
private String name;
#OneToMany(mappedBy = "server", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private List<Channel> channels;
public Server() {}
public Server(String name) {
this.name = name;
channels = new ArrayList<>();
}
// Getters and Setters...
}
Each Server can have several Channels that also have a unique id and belong to a server:
PostgreSQL:
CREATE TABLE public.channels (
id bigint NOT NULL,
server_id bigint NOT NULL,
name character varying NOT NULL
);
ALTER TABLE ONLY public.channels
ADD CONSTRAINT channels_pkey PRIMARY KEY (id);
ALTER TABLE ONLY public.channels
ADD CONSTRAINT fkey_channel_server FOREIGN KEY (server_id) REFERENCES public.servers(id);
Class entity:
#Entity
#Table (name = "channels")
public class Channel {
#Id
private long id;
private String name;
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "server_sn")
private Server server;
public Channel() {}
public Channel(Server server, String name) {
this.server = server;
this.name = name;
}
// Getters and Setters...
}
Next, I create a View in PostgreSQL:
CREATE VIEW public.summary AS
SELECT servers.s_snowflake AS server_id, channels.c_snowflake AS channel_id, channels.name FROM servers
JOIN channels ON servers.s_snowflake = channels.server_sn;
I want to get data from this view but I'm facing a problem on how to implement the class. I have tried something like this:
#Embeddable
class SummaryPK implements Serializable {
private long server_id;
private long channel_id;
}
#Entity
#Immutable
#Subselect("SELECT * FROM summary")
public class Summary {
#EmbeddedId
private SummaryPK summaryPK;
#MapsId("server_id")
#ManyToOne(fetch = FetchType.LAZY, targetEntity = Server.class)
#JoinColumn(name = "id", insertable = false, updatable = false)
private Server server;
#MapsId("channel_id")
#ManyToOne(fetch = FetchType.LAZY, targetEntity = Channel.class)
#JoinColumn(name = "id", insertable = false, updatable = false)
private Channel channel;
private String name;
// Getters and Setters
}
In the program, I want to receive a list of Entity, for example, by passing the Server ID. Any idea how to implement a working structure here?
Fixed the problem, there were incorrect id in JoinColumn:
#JoinColumn(name = "server_id", insertable = false, updatable = false)
#JoinColumn(name = "channel_id", insertable = false, updatable = false)

JPA: Composite key mapping with common field as primary key

I have two table with composite keys which shares a common column as part of their composite key. Below is by ER:
Table A: schemaId,aID
Table B: schemaId,bID,aID
Here scheId is contant value.
A and B has OneToMany relation(i.e., A record has multiple B records)
When I form a ManyToOne relation in Table B, I am doing below:
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "aID", referencedColumnName = "aID")
#JoinColumn(name = "schemaId", referencedColumnName = "schemaId")
private A a;
Above works fine(records get inserted), but I require schemaId as part of Entity B as there are other table which share this composite key from Table B.
In order to maintain the filed in entity if I change #JoinColumn with (insert="false" update="false"), value is passing as null for schemaId as it will treat as optional one.
Entity A:
#IdClass(APK.class)
#Entity
public class A
{
#Id
private Integer schemaId;
#Id
private Integer aId;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "a")
private List<B> blist;
...
}
public class APK implements Serializable{
private Integer schemaId;
private Integer aId;
}
Entity B:
#IdClass(BPK.class)
#Entity
public class B
{
#Id
private Integer schemaId;
#Id
private Integer bId;
private Integer aId;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "aID", referencedColumnName = "aID")
#JoinColumn(name = "schemaId", referencedColumnName = "schemaId")
private A a;
}
public class APK implements Serializable{
private Integer schemaId;
private Integer bId;
}

How filter entity B in join jpa

I have an Entity_A that has OneToMany relationship with Entity_B.
Entity_B has a flag which indicates whether it is active.
How can I select join entity A and only entity B if it is active?
JPA Query:Documention
SELECT DISTINCT entA FROM Entity_A entA JOIN FETCH entA.entityBSet as entB WHERE entB.active = true
Update
- try #where
#Entity
public class Entity_A {
#Id
#Column(name = "ID")
private Integer id;
#OneToMany(mappedBy = "entityA")
#Where("active = true")
private Set<Entity_B> entityBSet;
}
#Entity
public class Entity_B {
#Id
#Column(name = "ID")
private Integer id;
#Id
#Column(name = "ACTIVE")
private Integer active;
#ManyToOne
private Entity_A entityA;
}

JPA NamedQuery with Join returning empty list

I've been struggling with a namedquery for a few days. The named query has an inner join to a 2nd table. One added complexity is that the primary key on the 2nd table is a composite key. I have the two tables simplified here:
Table: aname
nameIdx number(9),
firstName varchar2(40),
lastName varchar2(40),
primary key is nameIdx
Table: aname_role
nameIdx number(9), --foreign key to name table
nameType char(2),
inactiveFlag char(1)
composite primary key is on nameIdx and nameType
I am trying to emulate the following sql query in JPQL:
select * from aname n
left join aname_role nr on n.nameidx=nr.nameidx
where nr.nametype='5'
and nr.inactiveflag='N';
This query works as expected in Oracle returning many records. In Java I have these JPA entities:
#Entity
#Table(name="ANAME")
#NamedQueries({
#NamedQuery(name = "AName.findActiveSalesPersons", query = "SELECT a FROM AName a LEFT JOIN a.aNameRoleList r WHERE r.inactiveflag='N' and r.ANameRolePK.nametype='5' ")})
public class AName implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Column(name = "NAMEIDX")
private Integer nameidx;
#Column(name = "FIRSTNAME")
private String firstname;
#Column(name = "LASTNAME")
private String lastname;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "aName")
private List<ANameRole> aNameRoleList;
//getters and setters here
and
#Entity
#Table(name = "ANAME_ROLE")
public class ANameRole implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
protected ANameRolePK aNameRolePK;
#Basic(optional = false)
#NotNull
#Column(name = "INACTIVEFLAG")
private Character inactiveflag;
#JoinColumn(name = "NAMEIDX", referencedColumnName = "NAMEIDX", insertable = false, updatable = false)
#ManyToOne(optional = false)
private AName aName;
//getters and setters here
There is also a primary key class ANameRolePK
#Embeddable
public class ANameRolePK implements Serializable {
#Basic(optional = false)
#NotNull
#Column(name = "NAMEIDX")
private int nameidx;
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 2)
#Column(name = "NAMETYPE")
private String nametype;
//getters and setters here
With this setup, including the named query specified in the AName entity above, the following returns an empty result list:
em.createNamedQuery("AName.findActiveSalesPersons").getResultList();
Can anyone point me to what I am doing wrong in this named query?
SELECT a FROM AName a LEFT JOIN a.aNameRoleList r WHERE r.inactiveflag='N' and r.aNameRolePK.nametype='5'
Thanks,
Steve
By default, at least using Hibernate, the default fetch type is Lazy, so you need to do a join fetch instead of a join. Also, you should have select distinct. Try:
SELECT distinct a FROM AName a LEFT JOIN fetch a.aNameRoleList r WHERE r.inactiveflag='N' and r.aNameRolePK.nametype='5'
References: Default fetch type for one-to-one, many-to-one and one-to-many in Hibernate
After more testing, I realized the join was working, but not the "r.aNameRolePK.nametype='5'". But if I changed that to "r.aNameRolePK.nameidx=1" it works. So, it was just the nametype field, which we have defined as a char(2) in the database. The problem is with the spaces in a char field and it is discussed here: Java NamedQuery String Problem. It looks like the recommended way to resolve this is to implement an EclipseLink SessionCustomizer. For testing I changed the named query to
SELECT a
FROM AName a LEFT JOIN a.aNameRoleList r
WHERE r.inactiveflag='N' and trim(trailing from r.aNameRolePK.nametype)=5
This returns the expected records.

Exception while persisting JPA object in DB having one to many relation

hi have two tables in picture table a and table b as follows :
#Entity
#Table(name = "A")
public class A implements Serializable {
#Id
#SequenceGenerator(name = "JOURNAL_CATEGORY_ID_GENERATOR", allocationSize = 1, sequenceName = "clm_jounal_category_config_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "JOURNAL_CATEGORY_ID_GENERATOR")
#Column(name = "CLAIM_ID")
private String claimId;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "country")
private List<ClaimDTLS> claimDetails;
}
B Primary Key:
#Embeddable
public class BPK implements Serializable {
#Column(name = "code")
private String code;
#Column(name = "CLAIM_ID")
private String claimId;
}
B Entity:
#Entity
#Table(name = "B")
public class B implements Serializable {
#EmbeddedId
protected BPK bpk;
#Column(name = "name")
private String name;
#MapsId("country_code")
#JoinColumn(name = "claimId", referencedColumnName = "claimId", insertable = false, updatable = false)
#ManyToOne
private A a;
}
when i try to persist object of A type in Db the value of table b claim id is not set and is intialized with zero.
Also primary key of table A is generated with a oracle sequence.
any help will be welcomed.
thanks in advance
Sequence values are numbers and when JPA use them as a generator it call the setter method of the entity PK. Now, you defined your PK as a string while you use a sequence and so no matching setter can be found. Change the type of you PK to be Long and things shall work