One to Many relationship throwing (null value in column "permission_group_id" violates not-null constraint) - spring

Parent.java:
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "pgroup_generator")
#SequenceGenerator(name="pgroup_generator", sequenceName = "pg_seq", allocationSize=50)
#Column(
name = "group_id",
unique = true,
updatable = false,
nullable = false
)
private Long id;
#Column(name="group_name",unique = true)
private String groupName;
#OneToMany(targetEntity=PermissionsEntity.class, mappedBy = "permissionGroup", cascade=CascadeType.ALL, fetch = FetchType.LAZY)
private List<PermissionsEntity> permissions= new ArrayList<>();
public void setPermissions(List<PermissionsEntity> permissions) {
this.permissions = permissions;
for(PermissionsEntity p:permissions) {
p.setPermissionGroup(this);
}
}
child.java:
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name="group_id", referencedColumnName = "group_id", insertable = false, updatable = false)
private PermissionGroupEntity permissionGroup;
Here is the error log:
org.postgresql.util.PSQLException: ERROR: null value in column "permission_group_id" violates not-null constraint
Detail: Failing row contains (11, null, 2020-11-02 10:52:34.849, null, 2020-11-02 10:52:34.849, Allow the user to create findings, create audit or workpaper findings, null, null, null, null, null, null).

Because you have insertable = false on that permissionGroup, it's not being inserted, leaving it null. Remove that setting to leave it the default true.

Related

#JoinColumn "occurs out of order" when upgrading to spring-boot-3 (Hibernate 6 )

I have the following usage in JoinColumns
#Entity
public class EntityOne{
private String action;
private String type;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#NotFound(action = NotFoundAction.IGNORE)
#JoinColumns({
#JoinColumn(name = "action", referencedColumnName = "action_name", updatable = false, insertable = false),
#JoinColumn(name = "type", referencedColumnName = "type_name", updatable = false, insertable = false)
})
private Entitytwo entitytwo;
}
And
#Entity
public class EntityTwo {
#Id
#Column(name = "type_name")
private String typeName;
#Id
#Column(name = "action_name")
private String actionName;
}
This setup causes hibernate error of
Referenced column '" + column.getName()
+ "' mapped by target property '" + property.getName()
+ "' occurs out of order in the list of '#JoinColumn's
If i change the order inside the #JoinColumns it seems to work, but can stop working at the next time the application starts.
The hibernate comments at the begining of the relevant code states:
// Now we need to line up the properties with the columns in the
// same order they were specified by the #JoinColumn annotations
// this is very tricky because a single property might span
// multiple columns.
// TODO: For now we only consider the first property that matched
// each column, but this means we will reject some mappings
// that could be made to work for a different choice of
// properties (it's also not very deterministic)
And on the relevant code itself:
// we have the first column of a new property
orderedProperties.add( property );
if ( property.getColumnSpan() > 1 ) {
if ( !property.getColumns().get(0).equals( column ) ) {
// the columns have to occur in the right order in the property
throw new AnnotationException("Referenced column '" + column.getName()
+ "' mapped by target property '" + property.getName()
+ "' occurs out of order in the list of '#JoinColumn's");
}
currentProperty = property;
lastPropertyColumnIndex = 1;
}
How should i set the #JoinColumn for it to consistently work?
If the action and type attributes of EntityOne are meant to refer to the corresponding attributes of EntityTwo, they are useless and misleading.
The attribute private Entitytwo entitytwo is enough to design the #ManytoOne relation.
Remove these two attributes and if you need to get the action and type value of the entityTwo linked to an entityOne, simply use entityOne.entitytwo.getAction() (or entityOne.entitytwo.getType()).
I just tried the code you posted in Hibernate 6.1, and I observed no error. Even after permuting various things, still no error. So then to make things harder, I added a third column to the FK and tried permuting things. Still no error.
I now have:
#Entity
public class EntityOne {
#Id #GeneratedValue
Long id;
String action;
String type;
int count;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#NotFound(action = NotFoundAction.IGNORE)
#JoinColumns({
#JoinColumn(name = "count", referencedColumnName = "count", updatable = false, insertable = false),
#JoinColumn(name = "action", referencedColumnName = "action_name", updatable = false, insertable = false),
#JoinColumn(name = "type", referencedColumnName = "type_name", updatable = false, insertable = false),
})
EntityTwo entitytwo;
}
#Entity
public class EntityTwo {
#Id
#Column(name = "type_name")
String typeName;
#Id
#Column(name = "count")
int count;
#Id
#Column(name = "action_name")
String actionName;
}
and the test code:
#DomainModel(annotatedClasses = {EntityOne.class, EntityTwo.class})
#SessionFactory
public class BugTest {
#Test
public void test(SessionFactoryScope scope) {
scope.inTransaction( session -> {
EntityOne entityOne = new EntityOne();
entityOne.action = "go";
entityOne.type = "thing";
EntityTwo entityTwo = new EntityTwo();
entityTwo.actionName = "go";
entityTwo.typeName = "thing";
entityOne.entitytwo = entityTwo;
session.persist( entityOne );
} );
}
}
Perhaps there's something you're not telling us? Like, for example, something to do with the #Id of EntityOne which is missing in your original posted code?
Just in case, also tried this variation:
#Entity
public class EntityOne {
#Id
String action;
#Id
String type;
#Id
int count;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#NotFound(action = NotFoundAction.IGNORE)
#JoinColumns({
#JoinColumn(name = "action", referencedColumnName = "action_name", updatable = false, insertable = false),
#JoinColumn(name = "count", referencedColumnName = "count", updatable = false, insertable = false),
#JoinColumn(name = "type", referencedColumnName = "type_name", updatable = false, insertable = false),
})
EntityTwo entitytwo;
}
But still no error.

Spring Boot 2, Spring 5 JPA: Dealing with multiple OneToMany JoinTables

Not sure the best approach to implementing the CrudRepository for an Entity that has multiple
#OneToMany associations with a #JoinTable
#Entity
#Table(name = "contact", uniqueConstraints = {#UniqueConstraint(columnNames ={"first_name","last_name"})})
#SuppressWarnings("PersistenceUnitPresent")
public class Contact extends Auditable implements Serializable {
#Id
#Column(name = "contact_id")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "contact_generator")
#SequenceGenerator(name = "contact_generator", sequenceName = "contact_seq", allocationSize = 50)
private Long contactId;
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "last_name", nullable = false)
private String lastName;
#Column(name = "middle_name", nullable = true)
private String middleName;
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#JoinTable(
name = "contact_phone"
)
private List<Phone> phoneNumbers = new ArrayList<>();
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#JoinTable(name = "contact_email")
private List<EmailAddress> emailAddresses = new ArrayList<>();
public interface ContactRepo extends CrudRepository<Contact, Long> {
List<Contact> findByLastNameContainingIgnoreCase(String lastName);
}
I have the FetchType.LAZY so I don't get the MultipleBagFetchException from 2 cartesian products.
So I know I need to split the 2 joins up which is where I am stuck as to the best solution.
Put in a custom repo and customImpl class that has can access the EntityManager and code out the 2 joins?
I am not crazy and letting Java take care of the cartesian via a Set, nor the one having FetchType.EAGER and dealing with the other with another query??
Generates:
create table contact (
contact_id bigint not null,
create_tm timestamp not null,
created_by varchar(255) not null,
updated_tm timestamp not null,
updated_by varchar(255) not null,
first_name varchar(255) not null,
last_name varchar(255) not null,
middle_name varchar(255),
primary key (contact_id)
)
create table email_address (
email_id bigint not null,
email_addr varchar(255) not null,
email_type varchar(255),
primary_addr boolean default false,
primary key (email_id)
)
create table contact_email (
Contact_contact_id bigint not null,
emailAddresses_email_id bigint not null
)
create table phone (
phone_id bigint not null,
phone_nbr varchar(255) not null,
phone_type varchar(255),
primary_ph boolean default false,
primary key (phone_id)
)
create table contact_phone (
Contact_contact_id bigint not null,
phoneNumbers_phone_id bigint not null
)
The strange think is my JpaDataTests worked find. The find all and findByLastNameContainingIgnoreCase return the phone numbers and email addresses.
However, The Service does not.
#Autowired
private ContactRepo contactRepo;
#Override
public List<Contact> findAllContacts() throws GcaServiceException {
try {
Iterable<Contact> iter = contactRepo.findAll();
return IteratorUtils.toList(iter.iterator());
} catch(DataAccessException e) {
throw new GcaServiceException(e.getMessage());
}
}
#Override
public List<Contact> findByLastName(String lastName) throws GcaServiceException {
try {
return contactRepo.findByLastNameContainingIgnoreCase(lastName);
} catch (DataAccessException e) {
throw new GcaServiceException(e.getMessage());
}
}
[
{
"createTm": "2021-01-11T16:27:19.995011",
"createdBy": "UncleMoose",
"updatedBy": "UncleMoose",
"updateTm": "2021-01-11T16:27:19.995011",
"contactId": 1,
"firstName": "Bruce",
"lastName": "Randall",
"middleName": null,
"phoneNumbers": [],
"emailAddresses": []
},
{
"createTm": "2021-01-11T16:27:19.996009",
"createdBy": "UncleMoose",
"updatedBy": "UncleMoose",
"updateTm": "2021-01-11T16:27:19.996009",
"contactId": 51,
"firstName": "Boss",
"lastName": "Randall",
"middleName": null,
"phoneNumbers": [],
"emailAddresses": []
}
]
Part of the mystery of DataJpaTest vs manual integration testing differences was I decided to look at a map and make sure I wasn't hiking down the wrong Google trail. I turned the H2 console on and found the Join Tables empty even though the insert occurred? However, I notice I was getting different Join Table column names between live and automated testing.
Solution was to explicitly name the Join Table columns. It appears Spring has handled the MultipleBagFetchException issues with multiple OneToMany JoinTable attributes in an Entity.
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#JoinTable(
name = "contact_phone"
,joinColumns = #JoinColumn(name = "contact_id")
,inverseJoinColumns = #JoinColumn(name = "phone_id")
)
private List<Phone> phoneNumbers = new ArrayList<>();
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#JoinTable(
name = "contact_email"
,joinColumns = #JoinColumn(name = "contact_id")
,inverseJoinColumns = #JoinColumn(name = "email_id")
)
private List<EmailAddress> emailAddresses = new ArrayList<>();

Hibernate creates self relation on a table using the primary key

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.

Oracle - JPA Alter Constraint

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.

null id generated for composite PK

I have the following tables and the following relationship table too: , which has a composite PK as follow:
UserRole.java
#RooJavaBean
#RooJpaEntity(identifierType = UserRolePK.class, versionField = "", table = "UserRole", schema = "dbo")
#RooDbManaged(automaticallyDelete = true)
#RooToString(excludeFields = { "idApplication", "idRole", "idUserName" })
public class UserRole {
}
UserRole_Roo_DbManaged.aj
#ManyToOne
#JoinColumn(name = "IdApplication", referencedColumnName = "IdApplication", nullable = false, insertable = false, updatable = false)
private Application UserRole.idApplication;
#ManyToOne
#JoinColumn(name = "IdRole", referencedColumnName = "IdRole", nullable = false, insertable = false, updatable = false)
private Role UserRole.idRole;
#ManyToOne
#JoinColumn(name = "IdUserName", referencedColumnName = "IdUserName", nullable = false, insertable = false, updatable = false)
private Users UserRole.idUserName;
But also exist a PK table:
#RooIdentifier(dbManaged = true)
public final class UserRolePK {}
And its identifier class (UserRolePK_Roo_Identifier.aj)
privileged aspect UserRolePK_Roo_Identifier {
declare #type: UserRolePK: #Embeddable;
#Column(name = "IdRole", nullable = false)
private Long UserRolePK.idRole;
#Column(name = "IdUserName", nullable = false, length = 16)
private String UserRolePK.idUserName;
#Column(name = "IdApplication", nullable = false)
private Long UserRolePK.idApplication;
The way how I'm setting the service objec to save is:
UserRole userRole= new UserRole();
userRole.setIdApplication(app);
userRole.setIdRole(invited);
userRole.setIdUserName(user);
appService.saveURole(userRole);
app has been set and saved before (same transaction), as well as invited and user objects.
Since user (from Users table with composite PK: IdUserName which is a String ), is defined as follow, otherwise doesnt work.
#RooJavaBean
#RooJpaEntity(versionField = "", table = "Users", schema = "dbo")
#RooDbManaged(automaticallyDelete = true)
#RooToString(excludeFields = { "quotations", "taxes", "userRoles", "idCompany", "idPreferredLanguage" })
public class Users {
#Id
//#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "IdUserName", length = 16, insertable = true, updatable = true)
private String idUserName;
}
So, the error that I'm getting is:
org.springframework.orm.jpa.JpaSystemException: org.hibernate.id.IdentifierGenerationException: null id generated for:class com.domain.UserRole; nested exception is javax.persistence.PersistenceException: org.hibernate.id.IdentifierGenerationException: null id generated for:class com.domain.UserRole
Try this:
public class UserRole {
#PrePersist
private void prePersiste() {
if (getId() == null) {
UserRolePK pk = new UserRolePK();
pk.setIdApplication(getIdApplication());
pk.setIdRole(getIdRole);
pk.setIdUserName(getIdUserName());
setId(pk);
}
}
}
Roo is generating the fields on UserRole entity and its id embedded class, but is not the same thing (UserRole.idRole is not the same than UserRole.id.idRole). In your example, you fill the UserRole fields, but not the id fields. This code makes it for you before entity is persisted.
Good luck!
In my case if the follow example tries to be persisted in DB, then similar Exception mentioned above is thrown:
EntityExample e = new EntityExample();
repositoryExample.save(e);
//throw ex
This is caused due to missing id field values which needs to be set something like that:
EntityExample e = new EntityExample();
e.setId(new EmbeddedIdExample(1, 2, 3));
repositoryExample.save(e);

Resources