JPA EmbeddedId is null after persisting - jpa

Let's say I have 2 JPA entites
#Entity
public class MyComplexEntity implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
#GeneratedValue(strategy = GenerationType.IDENTITY)
private MyComplexEntityId id;
private String text;
public MyComplexEntity() {
}
public MyComplexEntity(String text) {
this.text = text;
}
public MyComplexEntityId getId() {
return id;
}
public void setId(MyComplexEntityId id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
#Override
public String toString() {
return "MyComplexEntity{" + "id=" + id + ", text=" + text + '}';
}
}
and
#Embeddable
public class MyComplexEntityId {
#Column
private long id;
public MyComplexEntityId(long id) {
this.id = id;
}
public MyComplexEntityId() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#Override
public String toString() {
return "MyComplexEntityId{" + "id=" + id + '}';
}
}
Now I'm trying to persist a new MyComplexEntity object like this
MyComplexEntity entity = new MyComplexEntity("something");
em.persist(entity);
System.out.println(entity);
and what I get is this:
MyComplexEntity{id=null, text=something}
Why is the id null there? If I do the same thing with a primitive primary key type, the id is correctly set after persisting. I've also tried the following things:
Calling em.flush(); after the persist: does nothing
Calling em.refresh(entity); after the persist: Exception telling me that the entity is no longer in the database (which is wrong)
Trying em.merge() instead of em.persist(): Same result, as expected
Calling em.contains(entity); returns true, so the entity is actually attached
Putting the em.persist(); in a separate transaction by executing it in a separate method using #TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
There has to be some kind of problem with persist(); when using an #EmbeddedId, but I just don't get it.

You cannot put GeneratedValue(strategy = GenerationType.IDENTITY) on a complex mapping. It needs to go within the embeddedId class, and you need to instantiate a MyComplexEntityId instance for your new Entity. You will also need to flush to the database to ensure that values are assigned as identity values are assigned by the insert.

Related

POST REST request including a foreign key OnToMany Mapping

i'm new to Springboot. I'm trying to implement a simple REST api using :
-Springboot, JPA & rest along with hibernate
I have a 2 tables database, Notebook that contains 1 to many notes
I already setup the 2 tables and relationships. I also created a NotebookRepository and NoteRepository to get basic CRUD operations via the springboot rest. The Database connection and relationships are functionning
but i don't know how to add a new note (it has a notebook_id foreign key which msut NOT be NULL) and everytime i tryto post something along these lines
{
"title:"abc",
"text":"whatever",
"notebook":{
"id":2
}
}
i get this error :
Caused by: java.sql.SQLIntegrityConstraintViolationException: Column 'notebook_id' cannot be null
#Entity
#Table(name="notebook")
public class NoteBook {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="name")
private String name;
#OneToMany(mappedBy="notebook", cascade=CascadeType.ALL)
List<Note> notes;
public NoteBook() {
}
public NoteBook(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Note> getNotes() {
return notes;
}
public void setNotes(List<Note> notes) {
this.notes = notes;
}
public void addNote(Note note) {
if(notes == null) {
notes = new ArrayList<>();
}
note.setNotebook(this);
notes.add(note);
}
#Entity
#Table(name="note")
public class Note {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="title")
private String title;
#Column(name="text")
private String text;
#ManyToOne(cascade={CascadeType.MERGE, CascadeType.DETACH, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinColumn(name="notebook_id")
private NoteBook notebook;
public Note() {
}
public Note(String title, String text) {
this.title = title;
this.text = text;
}
#RepositoryRestResource(collectionResourceRel = "note", path = "notes")
public interface NoteRepository extends JpaRepository<Note, Integer>{
//No code...
}
#RepositoryRestResource(collectionResourceRel = "notebook", path = "notebooks")
public interface NotebookRepository extends JpaRepository<NoteBook, Integer>{
}
The problem is that the class Note doesn't have a constructor with NoteBook parameter to pass the created NoteBook object to, so the solution is to add this constructor:
public Note(String title, String text, NoteBook noteBook) {
this.title = title;
this.text = text;
this.noteBook = noteBook;
}
and it's enough to send the JSON object as you do, but just be aware of case-sensitivity:
{ "title:"abc", "text":"whatever", "noteBook":{ "id":2 } }
I think you need to add referencedColumnName = "id" for JoinColumn annotation for notebook field in Note class.
Maybe you have problem with IDENTITY generation type. See this problem with null pointer

Spring boot CrudRepository save - exception is org.hibernate.type.SerializationException: could not serialize

Not sure why I have an issue here, but when I save with a CrudRepository with these objects, I get the SerializationException (with no further information). Can someone take a look at my objects and offer me some insight into why they can't serialize? My pom.xml is attached last as well in case that helps somehow. I'm using a Postgres database.
EDIT: The database and now - tables are created, but objects are not creating rows.
The actual CrudRepository interface:
public interface AccountRepository extends CrudRepository<ZanyDishAccount, String> {}
ZanyDishAccount entity:
#Entity
public class ZanyDishAccount {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id; // internal id of the customer account for a Zany Dish subscription
private String status;
#OneToOne(cascade=CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "company_id")
private Company company;
#OneToOne(cascade=CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "order_id")
private Order order;
public ZanyDishAccount() {}
public ZanyDishAccount(Company company, Order order) {
this.company = company;
this.order = order;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
#Override
public String toString()
{
return "ClassPojo [id = "+id+ ", company = " + company + ", status = " + status + "]";
}
}
Company entity:
#Entity
public class Company {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
Long id;
private String phoneNumber;
private String website;
private String name;
private String uuid;
private String country;
public Company() {}
public Company(String phoneNumber, String website, String name, String uuid, String country) {
this.phoneNumber = phoneNumber;
this.website = website;
this.uuid = uuid;
this.country = country;
}
public String getPhoneNumber ()
{
return phoneNumber;
}
public void setPhoneNumber (String phoneNumber)
{
this.phoneNumber = phoneNumber;
}
public String getWebsite ()
{
return website;
}
public void setWebsite (String website)
{
this.website = website;
}
public String getName ()
{
return name;
}
public void setName (String name)
{
this.name = name;
}
public String getUuid ()
{
return uuid;
}
public void setUuid (String uuid)
{
this.uuid = uuid;
}
public String getCountry ()
{
return country;
}
public void setCountry (String country)
{
this.country = country;
}
#Override
public String toString()
{
return "ClassPojo [phoneNumber = "+phoneNumber+", website = "+website+", name = "+name+", uuid = "+uuid+", country = "+country+"]";
}
}
Order entity:
#Entity
#Table(name = "_order")
public class Order {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
Long id;
private String pricingDuration;
private Items[] items;
private String editionCode;
public Order() {}
public Order(String pricingDuration, Items[] items, String editionCode) {
this.pricingDuration = pricingDuration;
this.items = items;
this.editionCode = editionCode;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getPricingDuration ()
{
return pricingDuration;
}
public void setPricingDuration (String pricingDuration)
{
this.pricingDuration = pricingDuration;
}
public Items[] getItems ()
{
return items;
}
public void setItems (Items[] items)
{
this.items = items;
}
public String getEditionCode ()
{
return editionCode;
}
public void setEditionCode (String editionCode)
{
this.editionCode = editionCode;
}
#Override
public String toString()
{
return "ClassPojo [pricingDuration = "+pricingDuration+", items = "+items+", editionCode = "+editionCode+"]";
}
}
Thanks for your help!
Mike
Hm, this seems multi-faceted. Let's see if I can help at all. Last thing first...
No tables being created automatically.
I would take a look at this section in Spring's docs for the most basic approach: Initialize a database using Hibernate. For example, spring.jpa.hibernate.ddl-auto: create-drop will drop and re-create tables each time the application runs. Simple and easy for initial dev work. More robust would be leveraging something like Flyway or Liquibase.
Serialization issue
So without logs, and the fact that you have no tables created, the lack of a persistence layer would be the assumed culprit. That said, when you have tables and data, if you do not have a repository for all of the related tables, you'll end up with a StackOverflow error (the serialization becomes circular). For that, you can use #JsonBackReference (child) and #JsonManagedReference (parent). I have been successful using only #JsonBackReference for the child.
Items[]
I'm not sure what Item.class looks like, but that looks like an offensive configuration that I missed the first round.
Change private Items[] items; to private List<Item> items = new ArrayList<Item>();. Annotate with #ElementCollection.
Annotate Item.class with #Embeddable.

How to make a relation of same table in JPA?

Need make a relationship between same table. Example: the object "category" have subcategories and the subcategorie have other subcategorie.
In MySQL make a column and point to primary key of same table, but, howto make in JPA?
My code is:
#Entity
#Table(name = "objects")
public class JObject {
private long id;
private String name;
private JObject parentJObject;
private Set<JObject> jObjects;
public JObject(){
}
public JObject(long id){
this.id = id;
}
public JObject(String name){
this.name = name;
}
public JObject(String name, JObject parentJObject){
this.name = name;
this.parentJObject = parentJObject;
}
#Null
#JoinColumn(name="parent_object_id", referencedColumnName="id")
#ManyToOne(cascade=CascadeType.ALL)
public JObject getParentJObject() {
return parentJObject;
}
public void setParentJObject(JObject parentJObject) {
this.parentJObject = parentJObject;
}
#Null
#OneToMany(mappedBy = "parentJObject", cascade = CascadeType.ALL)
public Set<JObject> getJObjects() {
return jObjects;
}
public void setJObjects(Set<JObject> jObjects) {
this.jObjects = jObjects;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#NotNull
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
And the making objects:
JObject jObjectcategories = new JObject("Demo 1");
Set categoriesJObjects = new HashSet<JObject>(){{
add(new JObject("Demo 1.1", jObjectcategories));
}};
jObjectcategories.setJObjects(categoriesJObjects);
jObjectDao.save(new HashSet<JObject>() {{
add(jObjectcategories);
}});
But does not works. The log says:
List of constraint violations:[ ConstraintViolationImpl{interpolatedMessage='It has to be null', propertyPath=JObjects, rootBeanClass=class a.b.c.models.JObject, messageTemplate='{javax.validation.constraints.Null.message}'} ]
You need to be consistent in where you place your JPA annotations: either all on fields, or all on getters. But not mixed as you're doing.
The OneToOne should be a ManyToOne according to your description, since several objects share the same parent.
And the cascade ALL doesn't make sense: you don't want to delete a parent when a child is deleted.

Google Objectify v4 fetch by id

I'm trying to fetch an entity in App Engine with Objectify v4 but it doesn't work.
My #Entity: Translation.class
The #Id of the #Entity I want to fetch: 301L
My #Entity:
#Entity
public class Translation {
#Id
private Long id;
private String text;
public String getText() {
return text;
}
public Long getId() {
return id;
}
public void setText(String text) {
this.text = text;
}
}
The request that doesn't word:
Translation translation =ObjectifyService.ofy().load().type(Translation.class).id(301L).get(); // translation is null
But if I do:
Translation translation = ObjectifyService.ofy().load().type(Translation.class).first().get(); // translation is not null
Then:
System.out.println(translation.getId()); // translation id equal 301
So the fetch by id doesn't seem to work.
Where is the problem?
Since your entity has a #Parent field, in order to get it by id, you need to execute:
Translation translation = ObjectifyService.ofy().load().type(Thing.class).parent(par).id(301).get();
For more information take a look at Objectify Basic Operations - Loading
Hope this helps!
for #stickfigure, this is my real #Entity (PartOfSpeechGroup is obviously an #Entity too).
#Entity
public class Translation implements IsSerializable {
#Id
private Long id;
private String text;
#Parent
private Key<PartOfSpeechGroup> partOfSpeechEntryKey;
public Translation() {}
public Translation(Key<PartOfSpeechGroup> partOfSpeechEntryKey) {
this.partOfSpeechEntryKey = partOfSpeechEntryKey;
}
public String getText() {
return text;
}
public Long getId() {
return id;
}
public Key<PartOfSpeechGroup> getPartOfSpeechEntryKey() {
return partOfSpeechEntryKey;
}
public void setText(String text) {
this.text = text;
}
}

Copy Entity ID at persist time

I want to copy the entity's UUID, generated at run time to another field.
The entity id is generated via the code described bellow:
package eclipselink.example;
public class UUIDSequence extends Sequence implements SessionCustomizer {
public UUIDSequence() {
super();
}
public UUIDSequence(String name) {
super(name);
}
#Override
public Object getGeneratedValue(Accessor accessor,
AbstractSession writeSession, String seqName) {
return UUID.randomUUID().toString().toUpperCase();
}
...
public void customize(Session session) throws Exception {
UUIDSequence sequence = new UUIDSequence("system-uuid");
session.getLogin().addSequence(sequence);
}
}
Persitence.xml:
property name="eclipselink.session.customizer" value="eclipselink.example.UUIDSequence"
The entity:
public abstract class MyEntity{
private String id;
private String idCopy;
#Id
#Basic(optional = false)
#GeneratedValue(generator="system-uuid")
#XmlElement(name = "ID")
public String getId() {
return id;
}
}
How can I instruct JPA (Eclipse-link) to copy the UUID generated at runtime to idCopy field as well?
I'm not 100% sure this will work (I don't know if EclipseLink calls the setter or assigns the field directly), but give this a try:
public abstract class MyEntity{
private String id;
private String idCopy;
#Id
#Basic(optional = false)
#GeneratedValue(generator="system-uuid")
#XmlElement(name = "ID")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
this.idCopy = id;
// or
// this.setIdCopy(id);
}
}