Generated Key looks wrong when ManyToOne and OneToMany - postgresql

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.

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)

ConstraintViolationException could not execute statement; SQL [n/a]; constraint [id]

I have two entities, fileVersion and fileEnvironment, which have a many to many relationship. I'm using a junction table, modeled by fileDeployment entity.
The junction entity:
#Data
#Builder(toBuilder = true)
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(
name = "file_deployment"
)
public class FileDeploymentEntity {
#EmbeddedId
private FileDeploymentKey id;
#ToString.Exclude
#ManyToOne
#MapsId("fileVersionId")
#JoinColumn(name = "fileVersionId")
private FileVersionEntity fileVersion;
#ToString.Exclude
#ManyToOne
#MapsId("fileEnvironmentId")
#JoinColumn(name = "fileEnvironmentId")
private FileEnvironmentEntity fileEnvironment;
}
It's composite key:
#Embeddable
#NoArgsConstructor
#AllArgsConstructor
#Data
#Builder(toBuilder = true)
public class FileDeploymentKey implements Serializable {
#Column
private UUID fileVersionId;
#Column
private UUID fileEnvironmentId;
}
Its JPA repository:
#Repository
public interface FileDeploymentEntityRepository extends
JpaRepository<FileDeploymentEntity, FileDeploymentKey>,
JpaSpecificationExecutor<FileDeploymentEntity> {
}
The two entities for which the junction entity is capturing the many-to-many relationship for:
#Data
#Builder(toBuilder = true)
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(
name = "file_environment"
)
public class FileEnvironmentEntity {
#Id
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "uuid2")
private UUID id;
#ToString.Exclude
#OneToMany(mappedBy = "fileEnvironment")
private List<FileDeploymentEntity> fileDeployments;
}
FileVersion is the other
#Data
#Builder(toBuilder = true)
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(
name = "file_version"
)
public class FileVersionEntity {
#Id
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "uuid2")
private UUID id;
#ToString.Exclude
#OneToMany(mappedBy = "fileVersion")
private List<FileDeploymentEntity> fileDeployments;
}
The following code executes fine:
var fileDeploymentEntity = FileDeploymentEntity.builder()
.id(FileDeploymentKey.builder()
.fileVersionId(existingFileVersion.get().getId())
.fileEnvironmentId(existingFileEnvironment.get().getId())
.build())
.deploymentTime(
Instant.now(clock))
.fileEnvironment(existingFileEnvironment.get())
.fileVersion(existingFileVersion.get())
.build();
var result = fileDeploymentEntityRepository.save(fileDeploymentEntity);
But when eventually fileDeploymentEntityRepository.flush() is called I get the following exception:
could not execute statement; SQL [n/a]; constraint [id]
org.hibernate.exception.ConstraintViolationException: could not
execute statement
org.postgresql.util.PSQLException: ERROR: null value in column "id"
violates not-null constraint Detail: Failing row contains
(7670fec3-3766-4c69-9598-d4e89b5d1845,
b9f6819e-af89-4270-a7b9-ccbd47f62c39, 2019-10-15 20:29:10.384987,
null, null, null, null).
If I also call save for the 2 entities it doesn't change the result:
fileVersionEntityRepository
.save(existingFileVersion.get().addFileDeployment(fileDeploymentEntity));
fileEnvironmentEntityRepository
.save(existingFileEnvironment.get().addFileDeployment(fileDeploymentEntity));
Any help would be greatly appreciated!
For me the issue was that I named another entity with the same table name by accident which caused the schema that was generated to be very different from what I thought it was.
Take away lesson:
1) Check the schema that is generated when in doubt.
var con = dataSource.getConnection();
var databaseMetaData = con.getMetaData();
ResultSet resultSet = databaseMetaData.getTables(null, null, null, new String[]{"TABLE"});
System.out.println("Printing TABLE_TYPE \"TABLE\" ");
System.out.println("----------------------------------");
while(resultSet.next())
{
//Print
System.out.println(resultSet.getString("TABLE_NAME"));
}
ResultSet columns = databaseMetaData.getColumns(null,null, "study", null);
while(columns.next())
{
String columnName = columns.getString("COLUMN_NAME");
//Printing results
System.out.println(columnName);
}

How to reference a Composite Secondary Key

The below examples show what I tried to reference an entity by a unique combination of columns that is not its primary key.
I want to do it that way because the referencing table contains all the necessary constituents of the referenced "Composite Secondary Key" but it does not contain the Primary Key.
How can I achieve this? (If possible without being Hibernate specific)
Base example
Suppose an entity has a primary key as well as a composite secondary key:
#Entity
#Table(name = "EXAMPLE_DATA",
uniqueConstraints = {
#UniqueConstraint(columnNames = {
"COMPOSITE_KEY_PART_1",
"COMPOSITE_KEY_PART_2"})
})
public class ExampleData {
#Id
#Column
private Long exampleDataId;
#Column(name = "COMPOSITE_KEY_PART_1")
private String compositeKeyPart1;
#Column(name = "COMPOSITE_KEY_PART_2")
private String compositeKeyPart2;
}
I would like to reference it from another table by its composite secondary key:
#Entity
#Table(name = "EXAMPLE")
public class Example {
#Id
#Column
private Long exampleId;
#Column(name = "COMPOSITE_KEY_PART_1")
private String compositeKeyPart1; // of ExampleData
#Column(name = "COMPOSITE_KEY_PART_2")
private String compositeKeyPart2; // of ExampleData
#MayToOne
#JoinColumn(name = "COMPOSITE_KEY_PART_1", insertable = false, updatable = false)
#JoinColumn(name = "COMPOSITE_KEY_PART_2", insertable = false, updatable = false)
private ExampleData exampleData;
}
However, this leads to
org.hibernate.AnnotationException:
A Foreign key refering com.example.ExampleData from com.example.Example has the wrong number of column. should be 1
Making a separate #Embeddable composite key
I tried making the secondary key embeddable
#Embeddable
public class CompositeKey implements Serializable {
#Column(name = "COMPOSITE_KEY_PART_1")
private String compositeKeyPart1;
#Column(name = "COMPOSITE_KEY_PART_2")
private String compositeKeyPart2;
}
and using it as an embedded object:
#Entity
#Table(name = "EXAMPLE_DATA",
uniqueConstraints = {
#UniqueConstraint(columnNames = {
"COMPOSITE_KEY_PART_1",
"COMPOSITE_KEY_PART_2"})
})
public class ExampleData {
#Id
#Column
private Long exampleDataId;
#Embedded
private CompositeKey compositeKey;
}
but that leads to the same exception:
org.hibernate.AnnotationException:
A Foreign key refering com.example.ExampleData from com.example.Example has the wrong number of column. should be 1
Using #EmbeddedId
Using #EmbeddedId instead of just #Embedded leads to issues with multiple keys
org.hibernate.AnnotationException:
com.example.CompositeKey must not have #Id properties when used as an #EmbeddedId: com.example.ExampleData.compositeKey
Having only a single key works but is undesirable
The only way I can actually make it work is by removing the primary key and making the composite key the primary key (but I don't want that)
#Entity
#Table(name = "EXAMPLE_DATA")
public class ExampleData {
// #Id // removing this primary key is undesirable
#Column
private Long exampleDataId;
#EmbeddedId // This now becomes the primary key
private CompositeKey compositeKey;
}

Spring jpa duplication of record insertion exception on unique attributes

First time personService.addPerson(person); works fine when similar record inserted again it cast this exception:
"com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 'Patient' for key 'Role'
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_112]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_112]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_112]
at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_112]"
I want to keep unique rows in database on the basis of email, role in person table (email) and in personrole table (role).
--Tables are
CREATE TABLE PersonRole(Role_ID INT PRIMARY KEY AUTO_INCREMENT,Role VARCHAR(50) unique);
And
CREATE TABLE Person(Person_ID INT PRIMARY KEY AUTO_INCREMENT,Email VARCHAR(50) unique);
--Person entity
#Entity
public class Person implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(unique = true, nullable = false)
private int person_ID;
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "Role_ID")
private Personrole personrole;
.............
--personrole entity
#Entity
public class Personrole implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(unique = true, nullable = false)
private int role_ID;
#Column(length = 50)
private String role;
#OneToMany(mappedBy = "personrole",cascade = CascadeType.ALL)
private List<Person> persons;
..............................
--here is my column
#Controller
public class UserController {
#RequestMapping(value = "/signup", method = RequestMethod.POST)
public String signUp(#Valid Person person, BindingResult result, ModelMap model) {
if (result.hasErrors()) {
model.addAttribute("message", result.getAllErrors() + "\n DOB is: " + person.getDob());
return "fail";
}
Date date = new Date();
person.setRegister_Date(date);
personService.addPerson(person);
---Thanks in advance---

composite primary key which contains a foreign key

I have an entity called UserWithRoles:
#Entity
#Getter
#Setter
public class UserWithRoles implements Serializable
{
#Id
#GeneratedValue( strategy = GenerationType.AUTO )
private int id;
private String name;
private String password;
#OneToMany( mappedBy = "user" )
private List<UserRole> roles;
}
A UserRole entity:
#Entity
#Getter
#Setter
#IdClass( UserRolePK.class )
#Inheritance( strategy = InheritanceType.JOINED )
#DiscriminatorColumn( name = "roleType", discriminatorType = DiscriminatorType.STRING, length = 10 )
abstract public class UserRole implements Serializable
{
#Id
// It should be mapped as a foreign PK by user.id (user field declared below)
private int userID;
#Id
private String roleType;
#ManyToOne
#JoinColumn( name="user_id", referencedColumnName = "id" )
private UserWithRoles user;
}
The primary key class UserRolePK:
#Data
public class UserRolePK implements Serializable
{
private int userID;
private String roleType;
}
I want to create a composite PK to UserRole: UserWithRoles.id + UserRole.roleType
How can I map it to the database? Should I use the UserWithRoles type in the PK class instead of the ID? Is it a good idea at all? Or I just should use normal PK to UserRole? The relation would be something like that between the ClientOrder and ClientOrdetItem entities: (ClientOrder.id + ClientOrderItem.num)
You are using Derived Identity.
You need to change UserRole to look like this:
#Entity
#Getter
#Setter
#IdClass( UserRolePK.class )
#Inheritance( strategy = InheritanceType.JOINED )
#DiscriminatorColumn( name = "roleType", discriminatorType = DiscriminatorType.STRING, length = 10 )
abstract public class UserRole implements Serializable
{
#Id
private String roleType;
#Id
#ManyToOne
#JoinColumn( name="user_id", referencedColumnName = "id" )
private UserWithRoles user;
}
That is, get rid of the userID field and add an #Id annotation to the user field.
And change UserRolePK to look like this:
#Data
public class UserRolePK implements Serializable
{
private int user;
private String roleType;
}
That is, change the name of the userID field to user, to match the name of the #Id field in UserRole (but its type must still match the type of the UserWithRoles PK field, id).
Derived identity is discussed in JPA 2.1 spec, section 2.4.1.