Hibernate: getting entity from PostgreSQL view - postgresql

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)

Related

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;
}

Handling reduntant columns with hibernate/jpa/spring data

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

Generated Key looks wrong when ManyToOne and OneToMany

I have two entities with a relationship, UserEntity:
#Embeddable
public class UserId extends EntityId implements Serializable {
#Column( length = CCEntity.ID_MAX_SIZE, name = ccIdCN )
private String ccId;
#Column( length = NAME_MAX_SIZE, name = userIdCN )
private String userId;
...
}
#Entity
#Table(name = TableNames.CC_Users)
public class UserEntity {
#EmbeddedId
private UserId id;
...
#OneToMany(targetEntity = ProfileEntity.class, mappedBy = "user", fetch = FetchType.EAGER, cascade = { CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.MERGE })
private List<ProfileEntity> profiles;
And the ProfileEntity:
#Embeddable
public class ProfileId extends EntityId implements Serializable {
#Column( length = CCEntity.ID_MAX_SIZE, name = ccIdCN )
private String ccId;
#Column( length = NAME_MAX_SIZE, name = profileIdCN )
private String profileId;
....
}
#Entity
#Table(name = TableNames.CC_Profile)
public class ProfileEntity {
#EmbeddedId
protected ProfileId id;
...
#ManyToOne
#JoinColumns(value = {
#JoinColumn( nullable = true, name = Columns.referenceIdCN, referencedColumnName = UserId.userIdCN ),
#JoinColumn( nullable = true, name = Columns.ccIdOfReferenceCN, referencedColumnName = UserId.ccIdCN ),
})
private UserEntity user;
When JPA creates the tables it generates the following:
Table CC_USER with primary key: cc_id, user_id. That is correct.
Table CC_PROFILE with primary key: cc_id, user_id, profile_id. Here I don't understand why JPA adds the user_id column as primary key. The table also has the columns: reference_id and cc_id_of_reference_id set as nullable.
I want property user of the ProfileEntity is optional, or nullable. If I try to add an entity with the user as null, I get :
Internal Exception: org.postgresql.util.PSQLException: ERROR: null
value in column "user_id" violates not-null constraint
thanks for any help
Finally I found the issue. I have another Entity with same TableName as ProfileEntity, but a different id. That id contains the user_id column that wasn't expected.

cascade persist results in null column value for a ManyToMany entity

Owner:
#Entity
public class Strategy implements Serializable {
#Id
#GeneratedValue
private Long id;
#ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST})
#JoinTable(name = "StrategyHost", joinColumns = {#JoinColumn(name = "strategyId")}, inverseJoinColumns = {#JoinColumn(name = "hostId")})
private Set<Host> hostName;
}
Related entity:
#Entity
public class Host {
#Id
#GeneratedValue
private Long id;
#Column(unique = true)
private String name;
#ManyToMany(mappedBy = "hostName")
private List<Strategy> strategies;
public Host(String name) {
this.name = name;
}
}
Test:
#Test
#Transactional(propagation = Propagation.NOT_SUPPORTED)
public void testStrategyWithHosts() {
Strategy s = new Strategy();
Set<Host> hosts= new HashSet<>();
hosts.add(Host.builder().name("aaa").build());
hosts.add(Host.builder().name("bbb").build());
s.setHostName(hosts);
Strategy saved= strategyDao.save(s);
Set<Host> hostName = saved.getHostName();
}
debug shows the persisted saved object having Host:
Where are name values? However, if I add merge in cascade type array, name are valued. Why insert (not update managed entities) operation for related entities must have merge cascade type? Although log shows nothing suspicious:
insert into strategy...
insert into host...
insert into host...
update strategy ...
insert into strategy_host ...
insert into strategy_host ...

JPA persist foreign key restriction violation

I have a problem with persisting new objects to the database. I'm using Eclipselink and Postgresql. When I'm trying to add a new Merchandise, cascade adds also Price object.
Price.java
#Entity
#Table(name = "Prices")
public class Price implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(updatable = false)
private int id;
#Column(updatable = false, nullable = false)
private float value;
#Column(updatable = false, nullable = false)
private Date startDate;
#Column(updatable = false, nullable = true)
private Date endDate;
#ManyToOne(targetEntity=Merchandise.class,cascade=CascadeType.ALL)
#JoinColumn(name="id",nullable = false,updatable = false,insertable=false)
private Merchandise merchandiseId;
#Column(updatable = false, nullable = true)
private float valueBulk;
#Column(updatable = false, nullable = true)
private float valueRetail;
#Column(updatable = false, nullable = true)
private float makeupPercent;
#Column(updatable = false, nullable = true)
private float makeupForce;
Merchandise.java
#Entity
#Table(name="Merchandises")
public class Merchandise implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(updatable = false)
private int id;
#Column(updatable = false)
private int externalId;
#Column(unique = false, nullable = false, updatable = true)
private String name;
#Column(unique = false, nullable = true, updatable = true)
private int available;
#Column(unique = false, nullable = true, updatable = true)
private String description;
#OneToMany(targetEntity = Price.class, mappedBy = "merchandiseId",cascade = CascadeType.PERSIST)
private List<Price> prices;
#OneToMany(targetEntity = MerchandiseCategory.class, mappedBy = "merchandiseId",cascade = CascadeType.PERSIST)
private List<MerchandiseCategory> merchandiseCategories;
#OneToMany(targetEntity = MerchandiseOrder.class, mappedBy = "merchandiseId",cascade=CascadeType.PERSIST)
private List<MerchandiseOrder> merchandiseOrders;
#OneToMany(targetEntity = MerchandiseDiscount.class, mappedBy = "merchandiseId",cascade=CascadeType.PERSIST) //#61
private List<MerchandiseDiscount> discounts; //#61
my code in facede looks like this:
Dao dao = new DAO();//begins transaction etc.
Merchandise m = new Merchandise;
m.set..//setting all needed fields
List<Price> list = new ArrayList<Price>();
Price p = new Price();
p.set..//setting all needed fields
p.setMerchandiseID(m);
list.add(p);
m.setPrices(list);
dao.addMerchandise(m);//persisting
This gets mean error (in Polish base so I'll try to translate it): foreign key restriction violation - Key (id)=(356) is not present in merchandises.
I think this is problem with generating id, id fiels is always replaced by generated id, and i think they are different, but in this case should be the same
Your Price class uses its "id" field as the as the foreign key to Merchandise. You have also marked the ID attribute mapping to be generated, so it will always be different than what is in Merchandise. Did you intend Price to use its foreign key to Merchandise as the primary key? If so, you need to remove the #GeneratedValue from Price and either
1) need to persist Merchandise first, flush so that its primary key is assigned, and then set the value into p.setId() as well as calling p.setMerchandiseID(m).
or
2) use JPA 2.0's derived ID capability and mark Price's merchandiseId attribute as either #Id (and remove the int id as it isn't needed), or use #MapsId. #Mapsid will allow JPA to set the id value from Merchandise automatically when it is assigned a value.
Some info: http://wiki.eclipse.org/EclipseLink/Examples/JPA/2.0/DerivedIdentifiers
http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/mappedbyid
If you meant for Price to have its own independent primary key, you need to add a foreign key in the price table:
#ManyToOne(targetEntity=Merchandise.class,cascade=CascadeType.ALL)
#JoinColumn(name="fk_merchandiseid",nullable = false)
private Merchandise merchandiseId;