JPA with #ManyToOne - java

I have two classes like this
package models;
import java.util.*;
import javax.persistence.*;
import play.db.jpa.*;
#Entity
#Table(name = "commitment_type_value")
public class CommittmentTypeValue extends Model{
#Id
#Column(name = "id", nullable = false)
public Long id;
#Column(name = "value", nullable = true)
public String type;
#ManyToOne
#JoinColumn(name="commitment_type_id")
public CommitementType commitmentType;
public CommittmentTypeValue(){
}
}
-------------
package models;
import java.util.*;
import javax.persistence.*;
import play.db.jpa.*;
/**
*
* #author hatem
*/
#Entity
#Table(name = "commitment_type")
public class CommitementType extends Model{
#Id
#Column(name = "id", nullable = false)
public Long id;
#Column(name = "type", nullable = true)
public String type;
#OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL, mappedBy="commitmentType")
public List<CommittmentTypeValue> commitmentTypeValues;
public CommitementType(){
}
}
when I execute my app, this problem appears :
A JPA error occurred (Unable to build EntityManagerFactory): A Foreign
key refering models.CommitementType from models.CommittmentTypeValue
has the wrong number of column. should be 2
Please, can Any one tell me what 's wrong ?

Please check your foreign key column name it should match the exactly with the name of column.
EDIT
If your problem is still unsolved then please check if your persistance.xml has
<property name="generateDdl" value="true" />
and if it already have then check if you are getting any error in generation of table.
If yes then clear the data in table
or
add drop-and-create-tables option in config file
or
change your code as follows
#ManyToOne(optional=true)
#JoinColumn(name="commitment_type_id", nullable = true)
public CommitementType commitmentType;
Because you might have old data in table which might be stopping the creation of new table.

Reference column name is missing in your many to one join in class CommittmentTypeValue
#ManyToOne
#JoinColumn(name="commitment_type_id" referencedColumnName="id" )
public CommitementType commitmentType;
Also specify target entity
#OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL, targetEntity=CommittmentTypeValue.class, mappedBy="commitmentType")
public List<CommittmentTypeValue> commitmentTypeValues;

The error sounds like you Id in CommitementType is composite, so your foreign key must contain two columns.
Include the code for the CommitementType.

Related

Modifying the generated ID, UUID, removing dashes

Due to compatibility reasons with existing systems, I have to store the UUID type as varchar without dashes. What would be the simplest solution to do this?
I have created a sample class below to demonstrate the question. When the instance of the class is persisted, the UUID is in the DB fine, just with the dashes in the tab1_id column e.g. 583cfe1a-d671-47f9-9cd5-3f1e8f717856. What I would need is: 583cfe1ad67147f99cd53f1e8f717856. So the question is how I could change the generated id after its generation.
I am using spring-boot and hibernate as JPA implementation. Any constructive response is appreciated. Thanks in advance.
#Entity
#Table(name = "tab1")
#XmlRootElement
public class Tab1 implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(generator = "UUID")
#GenericGenerator(
name = "UUID",
strategy = "org.hibernate.id.UUIDGenerator"
)
#Column(name = "tab1_id", updatable = false, nullable = false, columnDefinition = "uuid")
#Type(type = "org.hibernate.type.UUIDCharType")
private UUID tab1Id;
#Column(name = "name")
private String name;
....
You'll have to implement a custom ID generator and your entity will have an ID of String as it's no longer going to be a valid UUID.
Something like this will work
#Id
#GenericGenerator(name = "mygen", strategy = "com.abc.generator.IdGenerator")
#GeneratedValue(generator = "mygen")
private String id;
Your generator will look like this:
package com.abc.generator;
import java.io.Serializable;
import java.util.UUID;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;
public class IdGenerator implements IdentifierGenerator {
#Override
public Serializable generate(SharedSessionContractImplementor sharedSessionContractImplementor, Object o)
throws HibernateException {
return UUID.randomUUID().toString().replace("-", "");
}
}
Do you need it persisted on database? If that is the case I guess it wouldn't be possible. But if you want to retrieve it without the dashes just do the getUUID method of your entity class return a string without the dashes.

Spring Boot JPA - Define a One to Many relationship

i'm trying to build a simple full stack tic-tac-toe application, but i've some problem defing a one-to-many relationship.
here the context, we have a main table Games:
CREATE TABLE `tictactoe`.`games` (
`idgames` INT NOT NULL AUTO_INCREMENT,
`gridConf` VARCHAR(45) NOT NULL,
`playerNum` VARCHAR(45) NOT NULL,
PRIMARY KEY (`idgames`));
and a secondary table, Gamesteps:
CREATE TABLE `tictactoe`.`gamesteps1` (
`idgamesteps` INT NOT NULL AUTO_INCREMENT,
`idgame` INT NOT NULL,
`step` VARCHAR(45) NOT NULL,
PRIMARY KEY (`idgamesteps`));
i'd like to define a relation One (Game) to Many (Gamesteps),
where the combo Gamesteps.idgamesteps and Gamesteps.games is primary key on the Gamesteps table.
i have two doubts:
1- how should i define my db tables? are my queries rights or do i need an explict foreing key?
2 -JPA annotations:
here my entities:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Entity
public class Games {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer idgames;
private String gridConf;
private Integer playerNum;
#OneToMany(mappedBy = "idgames")
private Set<GameSteps> gameSteps;
//all setter/getters
secondary table:
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class GameSteps {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer idGameSteps;
#ManyToOne
#JoinColumn(name = "idgames", referencedColumnName = "idgames")
private Integer idgame;
private String step;
//all setter/getter
i'm trying to archive is the possibility to do a Save/findAll operation throught my CRUDRepository interface on the main entity (Games) and do an automatic Save/findAll on all the others subEntities (gamesteps).
for me is not clear where i should use the #OneToMany or #ManyToOne annotations.
For now i'm only getting exceptions.
if needs others info just ask, any type of help / documentation will be really approciated!
Thanks!
Following #Smutje comment i've change to:
public class GameSteps {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer idGameSteps;
#ManyToOne
#JoinColumn(name = "idgames", referencedColumnName = "idgames")
private **Games** idgame;
and
#OneToMany(mappedBy = "**idgame**")
private Set<GameSteps> gameSteps;
but got:
java.sql.SQLSyntaxErrorException: Unknown column 'idgames' in 'field list'
If you want Gamesteps.idgamesteps and Gamesteps.games to be primary key on the Gamesteps table. You have to use the tags #EmbeddedId and #Embeddable, your composite key has to be another class.
The primary key fields are defined in an embeddable class. The entity contains a single primary key field that is annotated with #EmbeddedId and contains an instance of that embeddable class.
Something like this
import javax.persistence.*;
#Entity
public class GameSteps {
#EmbeddedId
private GameStepsPK pk;
private String step;
// getters and setters
GameStepsPK would be the composite key
import java.io.Serializable;
import javax.persistence.*;
#Embeddable
public class ExploitationEvaluationPK implements Serializable {
private static final long serialVersionUID = 1L;
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer idGameSteps;
#ManyToOne
#JoinColumn(name = "idgames", referencedColumnName = "idgames")
private Games idgames;
You can also read about this here
I hope this helps

Nullable #ManyToOne not accepting null value [duplicate]

This question already has an answer here:
Set parent target to null if source is null in MapStruct
(1 answer)
Closed 4 years ago.
I have a many-to-one relationship that I want to be nullable.
Here's the parent:
#Entity
#Table(name = "C_CUSTOMER")
class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToMany (fetch = FetchType.LAZY, mappedBy="user", cascade = CascadeType.REMOVE)
private List<Profile> profiles;
}
And the child:
#Entity
#Table(name = "C_PROFILE")
class Profile {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne(optional = true)
#JoinColumn(nullable = true, name = "user_id", referencedColumnName = "id", insertable = false, updatable = false)
private User user;
}
When I try to save a profile without a userId, the following error is threw:
org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : fr.entity.Profile.user -> fr.entity.User
If the field userId has a value, then it works smoothly.
I tried a bunch of answers from other questions on SO, but so far nothing has worked. The easiest way would be to ditch the relation in the entity files and use Integer customerId, but that's not really satisfactory for me, 'cause it would mean that the cascade deletion wouldn't work anymore.
Thanks in advance.
EDIT
The problem comes from a entity <-> dto mapping. I added an answer about it, will probably edit if I come across a workaround.
Tried your code on my machine. Below are my entity classes:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import lombok.Data;
#Entity
#Table(name = "C_PROFILE")
#Data
public class Profile {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
#ManyToOne(optional = true)
#JoinColumn(nullable = true, name = "user_id", referencedColumnName = "id", insertable = false, updatable = false)
private User user;
}
User class:
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import lombok.Data;
#Entity
#Table(name = "C_CUSTOMER")
#Data
class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String email;
#OneToMany (fetch = FetchType.LAZY, mappedBy="user", cascade = CascadeType.REMOVE)
private List<Profile> profiles;
}
Repository:
import org.springframework.data.jpa.repository.JpaRepository;
import io.polarisdev.returns.model.Profile;
public interface ProfileRepository extends JpaRepository<Profile, String> {
}
Code to save the Profile without user:
Profile p = new Profile();
p.setName("Name");
profileRepository.save(p);
So, after some digging inside the generated files: there's no solution to it (for now). The problem comes from a mapping between entity <-> dto, using mapstruct: having not mentioned this step, no wonder it wasn't working like it should for me... Well, the profile mapper contains this:
protected User profileToUser(Profile profile) {
if ( profile == null ) {
return null;
}
User user = new User();
User.setId(profile.getUserId());
return user;
}
Meaning I need to reassign a null value before saving the Profile. There is apparently a ticket about this on the github, so wait and see. Seems like there's some suggestions too to make it work, so if I come across one that work, i'll edit this answer (unless someone answer with a solution before).
EDIT
After a few researches on the mapstruct git and SO, I came across a solution that work, though it's not really elegant.
#AfterMapping
public Profile doAfterMapping(#MappingTarget Profile entity) {
if (entity != null && entity.getUser().getId() == null) {
entity.setUser(null);
}
return entity;
}
So... I'll put the question in duplicate. Thanks a bunch to the ones that helped me (though I wouldn't have need to post if I had been more vigilant).

JPA 2 Hibernate mapping with composite key in primary key using #IdClass with 3 tier structure

This question is very similar to: JPA (Hibernate, EclipseLink) mapping: why doesn't this code work (chain of 2 relationships using JPA 2.0, #EmbeddedId composite PK-FK)?
Actually my only (from meaningful that I spotted) difference is that I use #IdClass and that I most probably won't be able to switch to a different provider than hibernate.
but anyway here is the code (removed parts that where unimportant):
PermissionContextType.java:
#Entity
#IdClass(PermissionContextTypePk.class)
public class PermissionContextType{
#Id
private String id;
#Id
#JoinColumn (name = "PROJECT", referencedColumnName = "ID")
#ManyToOne ()
private Project project;
public static class PermissionContextTypePk implements Serializable{
public String project;
public String id;
// ... eq and hashCode here ...
}
}
PermissionContext.java:
#Entity
#IdClass(PermissionContextPk.class)
public class PermissionContext{
#Id
private String id;
#Id
#JoinColumns ({
#JoinColumn (name = "PROJECT", referencedColumnName = "PROJECT"),
#JoinColumn (name = "PERMISSIONCONTEXTTYPE", referencedColumnName = "ID")
})
#ManyToOne
private PermissionContextType permissionContextType;
public static class PermissionContextPk implements Serializable{
public String id;
public PermissionContextTypePk permissionContextType;
// ... eq and hashCode here ...
}
}
Permission.java:
#Entity
#IdClass(PermissionPk.class)
public class Permission{
#Id
private String id;
#Id
#JoinColumns ({
#JoinColumn (name = "PROJECT", referencedColumnName = "PROJECT"),
#JoinColumn (name = "PERMISSIONCONTEXTTYPE", referencedColumnName = "PERMISSIONCONTEXTTYPE"),
#JoinColumn (name = "PERMISSIONCONTEXT", referencedColumnName = "ID")
})
#ManyToOne
private PermissionContext permissionContext;
public static class PermissionPk implements Serializable{
public String id;
public PermissionContextPk permissionContext;
// ... eq and hashCode here ...
}
}
and what I get is:
org.hibernate.AssertionFailure: Unexpected nested component on the referenced entity when mapping a #MapsId: PermissionContext
Caused by: org.hibernate.AssertionFailure: org.hibernate.AssertionFailure: Unexpected nested component on the referenced entity when mapping a #MapsId: PermissionContext
does anybody know if this is a hibernate bug and I should post it on their issue tracking system (and pray that I would be able to update to given hibernate version) or is there something fundamentally wrong with my way of binding the entities?
I've checked it with the hibernate implementation on EAP 6.1 (4.2.0) as well as on wildfly (don't really know which one.)
Ok, so this is what I found so far :
Thanks fr my friend : https://hibernate.atlassian.net/browse/HHH-5764 which most probably is the reason for this behaviour.
And I found a workaround :
Permission.java:
#Entity
#IdClass(PermissionPk.class)
public class Permission{
#Id
private String id;
// for the next 3 fields there are no public acessors, so the public API of the class was not changed !
#Id
#Column(name = "PROJECT")
private String projectId;
#Id
#Column(name = "PERMISSIONCONTEXTTYPE")
private String permissionContextTypeId;
#Id
#Column(name = "PERMISSIONCONTEXT")
private String permissionContextId;
#JoinColumns ({
#JoinColumn (name = "PROJECT", referencedColumnName = "PROJECT", updatable = false, insertable = false),
#JoinColumn (name = "PERMISSIONCONTEXTTYPE", referencedColumnName = "PERMISSIONCONTEXTTYPE", updatable = false, insertable = false),
#JoinColumn (name = "PERMISSIONCONTEXT", referencedColumnName = "ID", updatable = false, insertable = false)
})
#ManyToOne
private PermissionContext permissionContext;
public static class PermissionPk implements Serializable{
// previously they where private as well, but removed public constructor for the sake of simplicity of the question - so no changes where necesary in public API of the class !
private String id;
private String projectId;
private String permissionContextTypeId;
private String permissionContextId;
public PermissionPk () {}
public PermissionPk (String aId, PermissionContextPk aPermissionContext) {
this.id = aId;
permissionContextId = aPermissionContext.id;
permissionContextTypeId = aPermissionContext.permissionContextType.id;
projectId = aPermissionContext.permissionContextType.project;
}
... eq and hashCode here ...
}
}
The good thing about this workaround is that it does not change the public API of the class in any way
(the only change was that I needed to make fields in Pk's of context and contexttype visible to the PermissionPk - they where private before with only a public constructor [but again simplified for the question]), nor did it change the jpql queries, and at the same time workaround is scalable (to any tier amount - as long as every even pk does not contain another pk), so if the bug will be resolved it will be easy to remove the workaround.
I would still gladly accept any comments on either my workaround or the question in itself.
Today I found another workaround :)
You can omit #IdClass entirely and use hibernate specific ability to create composite keys on the fly as apparently it is not affected by this bug.
The drawback here is that:
it is entirely Hibernate specific not covered by JPA at all.
you cannot do em.find(ClassName.class,new ClassPk(args...)) as there is no ClassPk at all.
But if you could use anything else than hibernate you could just as well use something without this bug - so probably 1 is not a problem really. and there is a possibility that you don't really need the em.find for this entity (or can live with creating it thru session or jpql query).

JPA and a novice's issue with relationship-mapping

I am trying to get the following type of mapping to work
Table event has the following columns:
id (PK)
prodgroup
errandtype
table errandtype : errandtype
table prodgroup: prodgroup
I have corresponding JPA classes
#Entity
#Table(name="event")
public class MyEvent {
#Id
int id;
// what mapping should go here?
Prodgroup prodgroup;
// what mapping should go here?
ErrandType errandtype;
}
#Entity
public class Prodgroup {
#Id
private String prodgroup;
}
#Entity
public class ErrandType {
#Id
private String errandtype;
}
Ok so questions are marked as comments in the code but I'll try to be explicit anyway.
In the above example I want my Prodgroup and ErrandType fields in the MyEvent class to be set to corresponding Prodgroup and Errandtype instances
I have tried #OneToOne relationships with #joincolumns and with mappedby attribute, but I just can't get it working and I've lost all sense of logical approach. My grasp of JPA entity mapping is clearly weak.
So can anyone bring some clarity?
It should be:
#Entity
#Table(name="event")
public class MyEvent {
#Id
int id;
// what mapping should go here?
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "prodgroup_id", insertable = true, updatable = true)
Prodgroup prodgroup;
// what mapping should go here?
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "errandtype_id", insertable = true, updatable = true)
ErrandType errandtype;
}
#Entity
public class Prodgroup {
#Id
private String prodgroup;
}
#Entity
public class ErrandType {
#Id
private String errandtype;
}
FetchType Eager means the object will be always loaded (would be "Lazy" by default if not specified).
CascadeType.ALL means mearge/persist/remove will be also done to linked tables.
Sebastian
Your table columns event.prodgroup and event.errandtype are foreign keys to respective tables (prodgroup, errandtype). So you need #ManyToOne association (because many events may share one prodgroup or errantype).

Categories

Resources