Im facing a little problem here.
I have two entities: Parent and Child, Parent has a List annotated #OneToMany.
The problem is when I try to insert a new Parent, it crashes when persisting the children, because the Parent Id was not generated yet.
Is that a fix for it?
#Entity
#Table(name = "PRODUTO")
public class Parent extends BaseEntity
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID_PRODUTO")
private Integer produtoId;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "produtoId", orphanRemoval = true)
// #JoinTable(name = "PRODUTO_TAMANHO", joinColumns = #JoinColumn(name = "ID_PRODUTO"))
#OrderBy("preco ASC")
private List<Child> children;
}
#Entity
#IdClass(Child.PrimaryKey.class)
#Table(name = "PRODUTO_TAMANHO")
public class Child extends BaseEntity
{
public static class PrimaryKey extends BaseEntity
{
private static final long serialVersionUID = -2697749220510151526L;
private Integer parentId;
private String tamanho;
//rest of implementation
}
#Id
#Column(name = "ID_PRODUTO")
private Integer parentId;
#Id
#Column(name = "TAMANHO")
private String tamanho;
#ManyToOne
#JoinColumn(name = "ID_PRODUTO", insertable = false, updatable = false)
private Parent parent;
}
I think if I persist firstly the parent, than persist the children would be a bad approach.
Is that a way to persist the children, when persisting Parent?
Thanks!
Guys, the exception that occurs when persisting Parent is:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'ID_PRODUTO' cannot be null
I found a guy facing the same problem: #OneToMany and composite primary keys? (maybe it's better explained)
Here is my insertion code
Parent parent = new Parent();
Child child1 = new Child();
child1.setTamanho("Tamanho 1");
child1.setParent(parent);
Child child2 = new Child();
child2.setTamanho("Tamanho 1");
child2.setParent(parent);
List<Child> children = parent.getChildren();
children.add(child1);
children.add(child2);
save(parent);
//all of this instances, is coming from a view.jsp binded by spring, I can confirm it is exactly like this, with parentId as null
//when updating, it goes perfectly
There are few problems with your entity class.
mappedBy attribute in Parent entity should be set to parent: mappedBy="parent".
In child entity, below field is not required.
#Id
#Column(name = "ID_PRODUTO", nullable = true)
private Integer parentId;
Updated entity is like this.
#Entity
#Table(name = "PRODUTO")
public class Parent extends BaseEntity
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ID_PRODUTO")
private Integer produtoId;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "parent", orphanRemoval = true)
// #JoinTable(name = "PRODUTO_TAMANHO", joinColumns = #JoinColumn(name = "ID_PRODUTO"))
#OrderBy("preco ASC")
private List<Child> children;
}
#Entity
#IdClass(Child.PrimaryKey.class)
#Table(name = "PRODUTO_TAMANHO")
public class Child extends BaseEntity
{
public static class PrimaryKey extends BaseEntity
{
private static final long serialVersionUID = -2697749220510151526L;
private Integer parentId;
private String tamanho;
//rest of implementation
}
/* #Id
#Column(name = "ID_PRODUTO", nullable = true)
private Integer parentId; */ // Not required.
#Id
#Column(name = "TAMANHO")
private String tamanho;
#ManyToOne
#JoinColumn(name = "ID_PRODUTO", insertable = false, updatable = false)
private Parent parent;
}
Also I do not understand child inner class for primary key. Use proper primary as you have used parent.
And while inserting set both parent to child and child to parent. See my blog for more details.Here
Related
I'm having an issue creating a JPQLQuery for all parents whose children have a certain attribute (Boolean active) set to TRUE. All children have to have it set to TRUE, otherwise don't select the parent.
I've found similar issues and answers but none of them written in JPQLQuery and QueryDSL that I need.
Here are my entities (simplified):
Parent entity:
#Table(name = "parent")
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "parent_child",
joinColumns = #JoinColumn(name = "parent_id"),
inverseJoinColumns = #JoinColumn(name = "child_id")
)
private List<Child> children;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(columnDefinition ="country_id")
private Country country;
Child entity:
#Table(name = "child")
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "active")
private Boolean active;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "parent_child",
joinColumns = #JoinColumn(name = "child_id"),
inverseJoinColumns = #JoinColumn(name = "parent_id")
)
private List<Parent> parents;
And here's what I've tried but it doesn't work and I'm not very good at writing these so I'm not even sure why it doesn't work:
JPAQuery<Parent> jpaQuery = new JPAQuery<>(entityManager);
QParent qParent = QParent.parent;
QChild qChild = QChild.child;
Map<Country, List<Parent>> parents = jpaQuery.from(qChild)
.join(qChild.parent, qParent)
.where((QChild.child.active.isFalse()).count().eq(0L))
.transform(GroupBy.groupBy(qParent.country).as(GroupBy.list(qParent)));
the error I get for this is:
java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: expecting CLOSE, found '=' near line 4, column 52
Appreciate any help, thanks!
I have the following "parent class" :
#Entity
#Table(name = "parent_table")
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long parentId;
private String firstName;
private String lastName;
#OneToMany(mappedBy = "Parent", cascade = CascadeType.ALL)
List<Child> children;
}
I also have the following child class :
#Entity
#Table(name = "children")
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long childId;
#ManyToOne
#JoinColumn(name="parent_id")
private Parent parent;
private String name;
}
I am sending up the following request in postman :
{
"firstName": "Test",
"lastName": "Parent",
"children":[{
"name":"jack"
},
{
"name":"jill"
}
]
}
When I ask the parent repository to save the parent, it does, but nothing happens for the child... it doesn't save to the database at all...
For reference, this is my line that saves the parent
parentRepository.save(parent)
(the parent in this case, has the two children inside of it - but they don't get saved to the children table).
I ran your example and it seems to be working correctly, only thing i change was mappedBy property of #OneToMany annotation. It must be lower case.
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
List<Child> children = new ArrayList<>();
Parent
#Entity
#Table(name = "parent_table")
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "parent_id")
private Long parentId;
private String firstName;
private String lastName;
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
List<Child> children = new ArrayList<>();
}
Child
#Entity
#Table(name = "children")
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long childId;
#ManyToOne
#JoinColumn(name="parent_id")
private Parent parent;
private String name;
}
Test
Above code makes next unit case to succeed:
#Test
public void parentRepositoryMustPersistParentAndChildren() {
Parent parent = new Parent("Anakin", "Skywalker");
parent.getChildren().add(new Child("Luke"));
parent.getChildren().add(new Child("Leia"));
Parent saved = parentRepository.save(parent);
Assert.assertNull("Parent does not have and id assigned after persist it", saved.getParentId());
saved.getChildren().forEach((child) ->{
Assert.assertNull("Parent does not have and id assigned after persist it", child.getChildId());
});
}
I'm having problems mapping composite keys in jpa / hibernate. The parent entity and the child entity both have composite primary keys.
I have been able to use #mapsId when the parent entity has a simple key and the child has a composite key.
In the hibernate documentation they use #JoinCoumns in the mapping to demonstrate mapping two composite keys. But in their example its not clear where those column references are defined.
I have the following:
#Embeddable
public class PriceRequestLegKey implements Serializable {
#Column(name = "leg_request_id")
private String requestId;
#Column(name = "display_index")
private int displayIndex;
...
}
#Embeddable
public class AllocationKey implements Serializable {
#Column(name = "leg_request_id")
private String requestId;
#Column(name = "display_index")
private int displayIndex;
#Column(name = "allocation_index")
private int allocationIndex;
...
}
#Entity(name = "PriceRequestLeg")
public class PriceRequestLegModel {
#EmbeddedId
private PriceRequestLegKey legKey;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumns({
#JoinColumn(name = "leg_request_id", referencedColumnName = "leg_request_id"),
#JoinColumn(name = "display_index", referencedColumnName = "display_index")
})
private List<AllocationModel> allocations;
...
}
#Entity(name = "Allocation")
public class AllocationModel {
#EmbeddedId
private AllocationKey allocationKey;
#ManyToOne
#MapsId
#JoinColumns({
#JoinColumn(name = "leg_request_id", referencedColumnName = "leg_request_id"),
#JoinColumn(name = "display_index", referencedColumnName = "display_index")
})
private PriceRequestLegModel leg;
...
}
At runtime when saving it gives the follow exception:
org.springframework.orm.jpa.JpaSystemException: could not get a field value by reflection getter of com.lbg.legato.rfq.data.entity.AllocationKey.displayIndex; nested exception is org.hibernate.PropertyAccessException: could not get a field value by reflection getter of com.lbg.legato.rfq.data.entity.AllocationKey.displayIndex
Which I assume is spurious as there are getters and setters. I also get the same error if I use mappedBy="leg" on the priceRequestLegModel and #MapsId on the AllocationModel. Could anyone point out what I'm doing wrong here?
You should restore the mappedBy="leg" to the PriceRequestLegModel #OneToMany annotation:
#Entity(name = "PriceRequestLeg")
public class PriceRequestLegModel {
#EmbeddedId
private PriceRequestLegKey legKey;
#OneToMany(mappedBy="leg", cascade = CascadeType.ALL)
private List<AllocationModel> allocations;
...
}
Then you should change AllocationKey to reference PriceRequestLegKey:
#Embeddable
public class AllocationKey implements Serializable {
PriceRequestLegKey legKey; // corresponds to PK type of PriceRequestLegModel
#Column(name = "allocation_index")
private int allocationIndex;
...
}
And then set the value of the Allocation.leg #MapsId annotation appropriately:
#Entity(name = "Allocation")
public class AllocationModel {
#EmbeddedId
private AllocationKey allocationKey;
#ManyToOne
#MapsId("legKey")
#JoinColumns({
#JoinColumn(name = "leg_request_id", referencedColumnName = "leg_request_id"),
#JoinColumn(name = "display_index", referencedColumnName = "display_index")
})
private PriceRequestLegModel leg;
...
}
Some examples like this are in the JPA 2.2 spec section 2.4.1.
I'm trying to use Spring Data to perform joined queries but one of my tables has a Composite Key and I'm not sure how to map the entities.
Here is an analogy of the data model:
table: device
pk=model_id
pk=serial_id
...
table: device_settings
pk=device_settings_id
fk=model_id
fk=serial_id
...
Here is an analogy of the code, which doesn't compile due to a "mappedby" attribute that is isn't present.
#Entity
#Table(name = "device_settings")
public class DeviceSettings {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "device_settings_id")
private Long id;
// Pretty sure this is the problem
#OneToMany(targetEntity = Device.class, mappedBy = "deviceKey", cascade = {CascadeType.MERGE}, fetch = FetchType.EAGER)
#JoinColumns({
#JoinColumn(name = "model_id", referencedColumnName = "model_id"),
#JoinColumn(name = "serial_id", referencedColumnName = "serial_id")})
private List<Device> devices;
}
#Entity
#Table(name = "device")
public class Device {
#Id
private DeviceKey deviceKey;
}
...
}
#Embeddable
public class DeviceKey implements Serializable {
private static final long serialVersionUID = -1943684511893963184L;
#Column(name = "model_id")
private Long modelId;
#Column(name = "serial_id")
private Short serialId;
}
Associations marked as mappedBy must not define database mappings like #JoinTable or #JoinColumn
To achieve your scenario you have to define #ManyToOne:
#ManyToOne(cascade = {CascadeType.MERGE}, fetch = FetchType.EAGER)
#JoinColumns({
#JoinColumn(name = "model_id", referencedColumnName = "model_id"),
#JoinColumn(name = "serial_id", referencedColumnName = "serial_id")})
private Device device;
This will end up model_id, serial_id, device_settings_id
or
Define #JoinColumn in Device Entity
Entities:
DeviceSettings :
#Entity
#Table(name = "device_settings")
public class DeviceSettings {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "device_settings_id")
private Long id;
#OneToMany( mappedBy = "deviceSettings", cascade = {CascadeType.MERGE}, fetch = FetchType.EAGER)
private List<Device> devices;
}
Device Entity :
#Entity
#Table(name = "device")
public class Device {
#EmbeddedId
private DeviceKey deviceKey;
#ManyToOne
#JoinColumn(name="device_settings_id")
private DeviceSettings deviceSettings;
//getters and setters
}
Note : you can decide which is the owner of the relationship and put your mappings accorindly either One Device has many device settings or other way around.
I'm having trouble persists the following entities:
#Entity
#Table(name="entityOne")
public class EntityOne implements Serializable {
#Id
#Column(name = "id", nullable = false)
private Integer id;
#OneToMany(fetch = FetchType.LAZY, mappedBy="entityOne")
private List<EntityTwo> entities;
}
#Entity
#Table(name="entityTwo")
public class EntityTwo implements Serializable {
#Id
#Column(name = "id", nullable = false)
private Integer id;
#Inject
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="entityOne", referencedColumnName="id")
private EntityOne entityOne;
}
In EntityOneDAO:
em.merge(entityOne);
And it is only persisted to EntityOne and not the list of EntityTwo ... How do I persist the list ?
Thanks all
You need to take care of both:
transitive persistence (using Cascade)
synchronizing both end of the bi-directional association.
So EntityOne should Cascade Persist and Merge to EntityTwo:
#OneToMany(fetch = FetchType.LAZY, mappedBy="entityOne", cascade = { CascadeType.PERSIST, CascadeType.MERGE})
private List<EntityTwo> entities = new ArrayList<>();
As you can see, you should always initialize your collection classes to avoid unnecessary null checks.
And it's always better to add the following helper child adding utility in your parent classes (e.g. EntityOne)
public void addChild(EntityTwo child) {
if(child != null) {
entities.add(child);
child.setEntityOne(this);
}
}
Then you can simply call:
EntityOne entityOne = new EntityOne();
entityOne.setProperty("Some Value");
EntityTwo entityTwo_1 = new EntityTwo();
entityTwo_1.setName("Something");
EntityTwo entityTwo_2 = new EntityTwo();
entityTwo_2.setName("Something");
entityOne.addChild(entityTwo_1);
entityOne.addChild(entityTwo_2);
entityManager.persist(entityOne);
P.S.
Please remove the #Inject annotation from the EntityTwo class. Entities are not Components.
And persist is much more efficient than merge, when you want to insert new entities.
You should explicitly set each entityTwo objects' entityOne field.
Such that:
entityTwo_1.setEntityOne(entityOne);
entityTwo_2.setEntityOne(entityOne);
entityOne.entities.add(entityTwo_1);
entityOne.entities.add(entityTwo_2);
em.merge(entityOne);
Try this:
public class EntityOne implements Serializable {
#Id
#Column(name = "id", nullable = false)
private Integer id;
#OneToMany(fetch = FetchType.LAZY, mappedBy="entityOne",
cascade = { CascadeType.ALL})
private List<EntityTwo> entities;
}
#Entity
#Table(name="entityTwo")
public class EntityTwo implements Serializable {
#Id
#Column(name = "id", nullable = false)
private Integer id;
#Inject
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="entityOne", referencedColumnName="id")
private EntityOne entityOne;
}
You can read here, about the CascadeType.
edited.