I'm having a problem with a constraint in oracle/jpa mapping.
I have these 4 tables
SucursalSolicitanteFornecimento(
Sucursal_ID (NUMBER) PK
Sucursal_Name(VARCHAR)
Sucursal_UF
);
ParamSolicitante(
paramID (NUMBER)
paramName(Varchar)
);
ConfiguracaoParametroSolicitante(
paramConfigID (NUMBER) PK,
paramID FK (from ParamSolicitante),
requestID (From SucursalSolicitanteFornecimento)
);
ConfiguracaoParametroSolicitanteSucursal(
ID (NUMBER) PK,
paramConfigID FK from ConfiguracaoParametroSolicitante,
SucursalSolicitanteFornecimento_ID
SucursalSolicitanteFornecimento_UF - Both From SucursalSolicitanteFornecimento
);
My problem is when I try to insert a new value in ConfiguracaoParametroSolicitanteSucursal I can't have 2 different ParamConfigID with the same SucursalSolicitanteFornecimento_UF and same SucursalSolicitanteFornecimento_ID because of the FK.
But now that's exactly what I need, more than one parameter for that SucursalSolicitante on the same state.
My Java class is mapped this way:
public class ConfiguracaoParametroSolicitanteSucursal{
#Id
#GeneratedValue()
private Integer codigoConfigParamSolctteSuc;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumns( { #JoinColumn(name = "SUCURSAL_ID", referencedColumnName = "SUCURSAL_ID", nullable = false),
#JoinColumn(name = "SUCURSAL_UF", referencedColumnName = "SUCURSAL_UF", nullable = false) })
private SucursalSolicitanteFornecimento sucursalParametro;
#JoinColumn(name = "paramConfigID", referencedColumnName = "paramConfigID", nullable = false)
#ManyToOne(fetch = FetchType.LAZY)
private ConfiguracaoParametroSolicitante configuracaoParametroSolicitante;
}
What can I do to achieve what I need? I was wondering if I have to change #OneToOne column to #ManyToOne and Delete the Constraint on my Database.
Related
My SQL view :-
CREATE OR REPLACE VIEW my_view
AS
SELECT
col1,
col2,
col3,
col4,
col5,
col6
FROM
my_table
My JPA entity :-
#Entity
#Table(name = "my_view")
#Getter
#Setter
public class MyEntity {
#EmbeddedId
private MyPk myPk;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "col1", insertable = false, updatable = false)
private Entity1 entity1 ;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col2", insertable = false, updatable = false)
private Entity2 entity2;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col3", insertable = false, updatable = false)
private Entity3 entity3;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col4", insertable = false, updatable = false)
private Entity4 entity4;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col5", insertable = false, updatable = false)
private Entity5 entity5;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col6", insertable = false, updatable = false)
private Entity6 entity6;
}
My Embedded Id class:-
#NoArgsConstructor
#EqualsAndHashCode
#AllArgsConstructor
#Embeddable
public class MyPk implements Serializable {
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "col1", insertable = false, updatable = false)
private Entity1 entity1 ;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col2", insertable = false, updatable = false)
private Entity2 entity2;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col3", insertable = false, updatable = false)
private Entity3 entity3;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col4", insertable = false, updatable = false)
private Entity4 entity4;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col5", insertable = false, updatable = false)
private Entity5 entity5;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col6", insertable = false, updatable = false)
private Entity6 entity6;
}
My JPA Repository :-
public interface MyEntityRepository extends JpaRepository<MyEntity, MyPk> {
List<MyEntity> findByMyPk_Entity1_Id(Long id);
}
Entity1 has an attribute id
I have tried #IdClass approach for composite key and many other solutions related to composite key but all are giving me some kind of error during runtime
This above approach is giving me no runtime error but giving null elements when invoking findByMyPk_Entity1_Id method but its giving correct count of elements in list
I cant use any kind of sequence from table or any other unique column approach dur to my underlying code. Also I tried using #mapIds approach but its also giving null elements.
For some reason JPA repository is not able to convert into entities but its able to give correct count of entities fetched
Also when using findAll method of repository in debugger its giving me proper entities list with no null elements but when searching through nested property i am not able to get entities
spring-data-jpa:1.9.4 version
hibernate-jpa-2.1-api:1.0.0
java 8
< Its a legacy project :") >
I don't know what you really have mapped in your table/view that you really need all 6 columns to uniquely identify a row. Assuming you do, and assuming they are all references to other tables that you want to have mapped in your entity, there are a number of approaches. The easiest, IMO more flexible approach might be this:
#Entity
#Table(name = "my_view")
public class MyEntity {
#EmbeddedId
private MyPk myPk;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "col1")
#MapsId("entity1")
private Entity1 entity1 ;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col2")
#MapsId("entity1")
private Entity2 entity2;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col3")
#MapsId("entity1")
private Entity3 entity3;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col4")
#MapsId("entity1")
private Entity4 entity4;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col5")
#MapsId("entity1")
private Entity5 entity5;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "col6")
#MapsId("entity1")
private Entity6 entity6;
}
#Embeddable
public class MyPk implements Serializable {
private Integer entity1 ;//use the ID type from Entity1's primary key
private Integer entity2;
private Integer entity3;
private Integer entity4;
private Integer entity5;
private Integer entity6;
}
Mappings in the MyPk embeddable class will be basic mappings, and pick up the column name settings from the entity ManyToOne mappings. Should you try to ever write one out (you can't write to views, but JPA treats everything as a table), you only need to set the appropriate relationships and JPA will extract the fk values to populate your MyEntity.myKey.entityX values for you.
Having both the embedded and the ManyToOne references means you can have the fk values within your entity and accessible without having to fetch the related entity just for the ID value.
My entity user with #SQLDelete
#Data
#Entity
#Table(name = "`user`")
#SQLDelete(sql = "UPDATE `user` SET status = 0 WHERE id = ?")
public class User {
private Integer id;
private String username;
private String password;
#ManyToMany
#JoinTable(
name = "`user_role`",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id"))
private List<Role> roles = new ArrayList<>();
}
When I delete by method userRepository.deleteAllById(ids). My row user_role table also deleted. I only want soft delete a row at user table and no effect to user_role
In my database I have a user who can have multiple email addresses. An email address can have only one user. I have following two tables in my database to handle this.
CREATE TABLE IF NOT EXISTS w4a_user (
id INTEGER NOT NULL AUTO_INCREMENT,
login_id VARCHAR(100) NOT NULL UNIQUE,
first_name VARCHAR(100),
last_name VARCHAR(100),
division INTEGER NOT NULL,
created_date TIMESTAMP NOT NULL,
last_active DATE,
PRIMARY KEY (id),
FOREIGN KEY (login_id) REFERENCES w4a_authentication_data (login_id) ON DELETE RESTRICT,
FOREIGN KEY (division) REFERENCES w4a_division (id) ON DELETE RESTRICT
);
CREATE TABLE IF NOT EXISTS w4a_email_address(
email_address VARCHAR(100) NOT NULL,
user_id INTEGER NOT NULL,
is_confirmed BOOLEAN NOT NULL DEFAULT FALSE,
PRIMARY KEY (email_address),
FOREIGN KEY (user_id) REFERENCES w4a_user (id) ON DELETE CASCADE
);
In my Spring boot application, I have following entity classes to handle this.
User.java
#Entity
#Table(name = "w4a_user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int id;
#Column(name = "first_name")
#Size(max = 100, message = GlobalConstants.ErrorMessageConstants.ERROR_FIRST_NAME_LENGTH_EXCEEDED)
private String firstName;
#Column(name = "last_name")
#Size(max = 100, message = GlobalConstants.ErrorMessageConstants.ERROR_LAST_NAME_LENGTH_EXCEEDED)
private String lastName;
#Column(name = "created_date")
private Date createdDate;
#Column(name = "last_active")
private Date lastActive;
#ManyToOne
#JoinColumn(name = "division", referencedColumnName = "id")
private Division division;
#OneToMany(mappedBy = "userId", cascade = CascadeType.ALL, orphanRemoval = true)
#Size(min = 1)
private List<ContactNumber> contactNumberList;
#OneToMany(mappedBy = "userId", cascade = CascadeType.ALL, orphanRemoval = true)
#Size(min = 1)
private List<EmailAddress> emailAddresses;
.
.
}
EmailAddress.java
#Entity
#Table(name = "w4a_email_address")
public class EmailAddress {
#Id
#Column(name = "email_address")
#Email(message = GlobalConstants.ErrorMessageConstants.ERROR_EMAIL_INCORRECT_FORMAT,
regexp = GlobalConstants.RegexList.EMAIL_REGEX)
#Size(max = 100, message = GlobalConstants.ErrorMessageConstants.ERROR_EMAIL_LENGTH_EXCEEDED)
private String emailAddress;
#ManyToOne
#JoinColumn(name = "user_id", referencedColumnName = "id")
private User userId;
#Column(name = "is_confirmed")
private Boolean isConfirmed;
.
.
}
I use following method to persist entitites to my database.
#PersistenceContext
private EntityManager em;
#Override
public T createEntity(T entity) {
this.em.unwrap(Session.class).save(entity);
return entity;
}
I set email address list in the user entity and perform above method to create a new user.
The issue I have is when adding a user with an email address already used by an existing user. In this case, the database entry for the email address gets updated with the id of the new user. Instead I want to give an error saying the email address is already in use. What is the best way of handling this?
I'm using Spring Boot 2 and PostGres 10. I have created the following entities, Roles and Privileges,
#Data
#Entity
#Table(name = "Roles")
public class Role {
public enum Name {
USER,
ADMIN
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
#Column(unique=true)
#Enumerated(EnumType.STRING)
private Name name;
#ManyToMany
#JoinTable(
name = "roles_privileges",
joinColumns = #JoinColumn(
name = "role_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(
name = "privilege_id", referencedColumnName = "id"))
private Collection<Privilege> privileges;
}
Privilege is
#Data
#Entity
#Table(name = "Privileges")
public class Privilege {
public enum PrivilegeName {
SUPER_ADMIN,
USER
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
#Column(unique=true)
#Enumerated(EnumType.STRING)
private PrivilegeName name;
#ManyToMany(mappedBy = "privileges")
private Collection<Role> roles;
}
Then I have this in my application.properties
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.show-sql=true
spring.datasource.url=jdbc:postgresql://${PG_DB_HOST:localhost}:5432/${PG_DB_NAME}
spring.datasource.username=${PG_DB_USER}
spring.datasource.password=${PG_DB_PASS}
When my tables are generated, the roles_privileges table is generated like so ...
cardmania=# \d roles_privileges ;
Table "public.roles_privileges"
Column | Type | Modifiers
--------------+------+-----------
role_id | uuid | not null
privilege_id | uuid | not null
Foreign-key constraints:
"fk5duhoc7rwt8h06avv41o41cfy" FOREIGN KEY (privilege_id) REFERENCES privileges(id)
"fk629oqwrudgp5u7tewl07ayugj" FOREIGN KEY (role_id) REFERENCES roles(id)
Are there any other annotations or other Java code I can add so that when my join table is created, the role_id/privilege_id has a unique key generated?
To force Hibernate to create a primary key with both columns, you have to change Collection by Set
public class Role {
#ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinTable(
name = "roles_privileges",
joinColumns = #JoinColumn(
name = "role_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(
name = "privilege_id", referencedColumnName = "id"))
private Set<Privilege> privileges;
}
And:
public class Privilege {
#ManyToMany(mappedBy = "privileges")
private Set<Role> roles;
}
You will not see a unique constraint but you will see the following table
create table roles_privileges (
role_id binary not null,
privilege_id binary not null
)
But you can explicitly declare your unique constraint
#ManyToMany
#JoinTable(
name = "roles_privileges",
joinColumns = #JoinColumn(name = "role_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(
name = "privilege_id", referencedColumnName = "id"),
uniqueConstraints = #UniqueConstraint(columnNames = {"role_id",
"privilege_id"},
name = "rolesPrivilegesConstraint")
)
private Collection<Privilege> privileges;
I have an Entity Episode
#Id //The unique id.
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name= "title", unique = false, nullable = false)
private String title;
#Column(name= "description", unique = false, nullable = false)
private String description;
#Column(name= "price", unique = false, nullable = false)
private BigDecimal price;
#OneToOne(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
private Image icon;
#OneToOne(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
private Image episodeNexus;
private String repositoryGeneratedId;
#JsonIgnore
#ManyToOne(cascade = {CascadeType.ALL}, fetch = FetchType.LAZY)
#JoinColumn(name="webtoon_id")
Webtoon webtoon;
Webtoon
#Id //The unique id of the webtoon.
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name= "price", unique = false, nullable = false)
private BigDecimal price;
#OneToOne(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
private Image cover;
#OneToOne(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
private Image icon;
#Column(name= "title", unique = false, nullable = false)
private String title;
#Column(name= "author_name", unique = false, nullable = false)
private String authorName;
#Column(name= "description", unique = false, nullable = false)
private String description;
#Column(name= "language", unique = false, nullable = false)
private String language;
#Column(name= "company_id", unique = false, nullable = false)
private Long companyId;
#OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.LAZY, mappedBy="webtoon", orphanRemoval = false)
#Column(name= "user_review", nullable = true)
private List<Review> userReview = new ArrayList<>();
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy="webtoon", orphanRemoval = false)
#CollectionTable(name= "list_of_episodes")
#Fetch(value = FetchMode.SUBSELECT)
private List<Episode> listOfEpisodes = new ArrayList<>();
#OneToOne(fetch = FetchType.EAGER)
private Category category;
#OneToOne(fetch = FetchType.EAGER)
private SubCategory subCategory;
#OneToOne(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#JoinColumn(name = "rating", unique=true, nullable=false)
private Rating rating = new Rating();
private String repositoryGeneratedId;
and Image
#Id //The unique id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String nexusId;
private String path;
private String name;
private String generatedUniqueId;
private Long size;
When I start my app, I notice that Hibernate create a foreign-key using the id. How is it possible ?
The code generated is the below. This constraint CONSTRAINT fkae0gia7g5anc7p031c00mdf7x FOREIGN KEY (id) must not exist.
CREATE TABLE public.episode
(
id bigint NOT NULL,
description character varying(255) COLLATE pg_catalog."default" NOT NULL,
price numeric(19,2) NOT NULL,
repository_generated_id character varying(255) COLLATE pg_catalog."default",
title character varying(255) COLLATE pg_catalog."default" NOT NULL,
episode_nexus_id bigint,
icon_id bigint,
webtoon_id bigint,
CONSTRAINT episode_pkey PRIMARY KEY (id),
CONSTRAINT fkae0gia7g5anc7p031c00mdf7x FOREIGN KEY (id)
REFERENCES public.episode (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION,
CONSTRAINT fkeom9w8fbdmqm8j9nkq5hqglia FOREIGN KEY (icon_id)
REFERENCES public.image (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION,
CONSTRAINT fksd2jfjxp5puq4cnp4renveldi FOREIGN KEY (episode_nexus_id)
REFERENCES public.image (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION,
CONSTRAINT fkthwbcsb0axcklmd5wfhr650b9 FOREIGN KEY (webtoon_id)
REFERENCES public.webtoon (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION
)
WITH (
OIDS = FALSE
)
Set hibernate.hbm2ddl.auto value to none in your Hibernate configuration.
See Automatic schema generation in Hibernate User Guide for details.