JPA save the transient instance before flushing - java

I have the following situation: a student belongs to a team, a team can have many students. At registration time the student doesn't know the assigned team, so the team object in student class should be null. How to force the insertion of student with null team object without getting this error:
org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.af.domain.Student.team -> com.af.domain.Team
I tried cascade=CascadeType.ALL but it also ads a new team in the database.
Student class
#Entity
#Table(name = "student")
public class Student implements DomainModel{
#Id
#GeneratedValue
private Integer idStudent;
private String firstname;
private String lastname;
private String username;
private String password;
#ManyToOne
#JoinColumn(name="idTeam", nullable=true)
private Team team;
}
Team class
#Entity
#Table
public class Team implements DomainModel{
#Id
#GeneratedValue
private Integer idTeam;
private String teamName;
#OneToMany(mappedBy="team")
private Set<Student> students;
}
Persistence class
public class GenericDAOImpl<T extends DomainModel> implements GenericDAO<T> {
public void save(T object) {
EntityManager entityManager = entityManagerFactory
.createEntityManager();
entityManager.getTransaction().begin();
try {
entityManager.persist(object);
} catch(NullPointerException ex){
ex.printStackTrace();
}finally {
entityManager.getTransaction().commit();
entityManager.close();
}
}
}

You can use detach option with entity manager.
entityManager.detach --- This will detach that particular parent object from the main entity object which you want to persist or merge.

Related

get request response from springboot handler method is not showing entity class object properly in postman

I am trying to fetch customer details from my database using spring boot handler method by incoming get request from postman. I am using birectional #manytomany relationship between Account and Customer entity classes.
Account class -
#Entity
#Table(name = "Account")
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int accountNumber;
public String accountType;
public int balance;
#ManyToMany(cascade = CascadeType.ALL)
#JsonManagedReference
#JsonIgnore
private List<Customer> customers;
}
//skipped constructors and getters setters
Customer class
#Entity
#Table(name = "Customer")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int customerId;
public String firstName;
public String lastName;
public String email;
#ManyToMany(mappedBy = "customers")
#JsonBackReference
private List<Account> accounts;
}
Handler method in controller class -
// fetching all customers
#GetMapping("/customers")
public List<Customer> getallAccounts() {
return (List<Customer>) this.customerServices.getAllAccounts();
}
CustomerServices class -
#Component
public class CustomerServices {
#Autowired
private CustomerRepository customerRepository;
// get all books
public List<Customer> getAllAccounts() {
System.out.println("Fetching all Customers");
return (List<Customer>) this.customerRepository.findAll();
}
}
And CustomerRepository interface -
public interface CustomerRepository extends CrudRepository<Customer, Integer> {
}
When I am sending a get request to fetch all customers from the database
In response it is not showing List of account class objects that are mapped by ManyToMany relationship in Customer class.
I have no idea what is the error.
Somebody please help here.
You specifically annotated accounts field with #JsonBackReference which means:
Linkage is handled such that the property annotated with this annotation is not serialized;
If you don't serialize the accounts field of Customer, what JSON did you expect to get that would list Account objects when returning a list of Customer objects?
Returning a list of Account objects instead, and expecting sub-lists of Customer objects, will also not happen because of the #JsonIgnore annotation, which pretty much have the same serialization effect as #JsonBackReference
There is no error here. The generated JSON is exactly what you asked the system to generate.

hibernate changes id when saving the object

I've got an object with this parameters
{
"id" : "36461dd3-2bdb-42de-8e3d-b44e28696b1e",
"race" : "HUMAN",
"age" : "18",
"name" : "Alisa"
}
I attempt to save it
List<MainFemaleCharacter> batch = Arrays.asList(sampleMainCharacter());
try (Session session = sessionFactory.openSession()) {
session.beginTransaction();
batch.forEach(session::save);
session.getTransaction().commit();
}
In debug, before saving, it shows id with expected value. But when I retrieve object it shows another id for example dccaf5d0-5c2b-4336-a0f3-ff65f92bf5f1. Why? MainFemaleCharacter class looks like this
#Entity
#Table(name="main_female_character")
#EqualsAndHashCode(callSuper=true)
#ToString(callSuper=true)
public #Data class MainFemaleCharacter extends BasicCharacter {
}
#MappedSuperclass
#EqualsAndHashCode(callSuper=true)
#ToString(callSuper=true)
public #Data class BasicCharacter extends UidNamedObject {
#OneToOne
private Race race;
private int age;
}
#MappedSuperclass
public #Data class UidNamedObject {
#Id
#GeneratedValue
private UUID id;
#Column(unique=true)
private String name;
}
The annotation #GeneratedValue will generate an id automatically. It is the same as the #GeneratedValue(strategy=GenerationType.AUTO) annotation.
GenerationType.AUTO means that the persistence provider chooses a strategy which will restart the values after a server restart in your case.
I recommend you to consider using GenerationType.SEQUENCE.

How to save or update OneToMany Column with value Null in JPA (Using Play Framework)

I'm currently developing a system using PLAY framework and JPA. My problem is that I can't save an EMPLOYEE if Department_id (which is connected to another Entity class) is Null or has no value.
Here is my Employees Entity Class
#Entity
public class Employees{
#Id
public int employee_id;
public String first_name;
public String last_name;
public String email;
public String phone_number;
public java.sql.Date hire_date;
public String salary;
public String commission_pct;
#ManyToOne
#JoinColumn(name="department_id",nullable = true)
private Departments department_id;
#ManyToOne
#JoinColumn(name="job_id")
private Jobs job_id;
#ManyToOne
#JoinColumn(name="manager_id",nullable = true)
private Employees manager_id;
#OneToMany(mappedBy = "manager_id")
Set<Employees> emps = new HashSet<Employees>();
}
Here is my Departments Entity Class.
#Entity
public class Departments {
#Id
private String department_id;
private String department_name;
private int manager_id;
#OneToMany(mappedBy="department_id")
private Set<Employees> emps = new HashSet<Employees>();
}
Here is the error
Caused by: org.hibernate.TransientObjectException: object references an unsaved
transient instance - save the transient instance before flushing: modelsDomain.E
mployees.department_id -> modelsDomain.Departments
at org.hibernate.engine.CascadingAction$9.noCascade(CascadingAction.java
:387) ~[hibernate-core-3.6.9.Final.jar:3.6.9.Final]
at org.hibernate.engine.Cascade.cascade(Cascade.java:172) ~[hibernate-co
re-3.6.9.Final.jar:3.6.9.Final]
at org.hibernate.event.def.AbstractFlushingEventListener.cascadeOnFlush(
AbstractFlushingEventListener.java:154) ~[hibernate-core-3.6.9.Final.jar:3.6.9.F
inal]
at org.hibernate.event.def.AbstractFlushingEventListener.prepareEntityFl
ushes(AbstractFlushingEventListener.java:145) ~[hibernate-core-3.6.9.Final.jar:3
.6.9.Final]
at org.hibernate.event.def.AbstractFlushingEventListener.flushEverything
ToExecutions(AbstractFlushingEventListener.java:88) ~[hibernate-core-3.6.9.Final
.jar:3.6.9.Final]
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlus
hEventListener.java:50) ~[hibernate-core-3.6.9.Final.jar:3.6.9.Final]

I can't get cascade to work on an entity,Hibernate

I have a entity that has two Many-To-One relationships, and one of them cascades on save just fine and the other one returns:
Exception in thread "main" org.hibernate.TransientObjectException: object references an
unsaved transient instance - save the transient instance before flushing : dto.publicSchema.Pessoas
Here is the code of the entity that works:
#Entity
#Table(name="`Doc_tipo`", schema="public")
public class Doc_tipo implements Serializable {
private static final long serialVersionUID = 1859372890916956036L;
#Id
#Column(nullable=false)
private int tp_doc;
#Column(nullable=false,columnDefinition="CHAR(255)")
private String descricao;
#Column(nullable=false,columnDefinition="CHAR(255)")
private String tp_emissor;
//getters and setters
}
And here is the code of the entity that will not allow cascade:
#Entity
#Table(name="`Pessoas`", schema="public")
public class Pessoas implements Serializable {
private static final long serialVersionUID = 8292302132119274975L;
#Id #GeneratedValue
#Column(nullable=false,columnDefinition="serial NOT NULL")
private int seq_pessoa;
static Date padrao_dt_criacao = new Date();
#Column(nullable=false, columnDefinition="date NOT NULL")
private Date dt_criacao = padrao_dt_criacao;
#Column(columnDefinition="CHAR(255)")
private String nome;
#Column(columnDefinition="CHAR(1) NULL")
private char tp_pessoa;
#Column(columnDefinition="CHAR(255)")
private String fantasia;
#Column(columnDefinition="VARCHAR(25)")
private String idioma;
#Column(columnDefinition="VARCHAR(25)")
private String login;
#Column(columnDefinition="VARCHAR(25)")
private String senha;
static Date padrao_dt_i = new Date();
#Column(nullable=false, columnDefinition="date NOT NULL")
private Date dt_i = padrao_dt_i;
//Pessoa está ativa para o sitema se este campo estiver em branco
#Column(columnDefinition="date")
private Date dt_f;
#Column(columnDefinition="oid")
private int foto;
//getters and setters
}
And here is the class that has relationships Many-To-One with the two above
but cascade will only work on the first one:
#Entity
#Table(name="`Documentos`", schema="public")
public class Documentos implements Serializable {
private static final long serialVersionUID = -4874330647877687810L;
#Id
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="seq_pessoa",columnDefinition="integer",referencedColumnName="seq_pessoa",nullable=false)
private Pessoas seq_pessoa;
#Id #GeneratedValue
#Column(nullable=false,columnDefinition="serial NOT NULL")
private int cd_doc;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="tp_doc",referencedColumnName="tp_doc",nullable=false)
private Doc_tipo tp_doc;
#Column(nullable=false)
private int tp_emissor;
#Column(nullable=false,columnDefinition="CHAR(2) NOT NULL DEFAULT 'DF'::bpchar")
private String tp_emissor_uf="DF";
#Column(columnDefinition="CHAR(5)")
private String alfa_doc;
#Column(nullable=false,columnDefinition="CHAR(20)")
private String nr_doc;
//Data de validade do documento
#Column(columnDefinition="date")
private Date dt_f_valid;
#Transient
transient static Date padrao_dt_i = new Date();
#Column(columnDefinition="date DEFAULT now()")
private Date dt_i = padrao_dt_i;
#Column(columnDefinition="date")
private Date dt_f;
//getters and setters
}
When I go to save a Documentos object hibernate inserts the Doc_tipo in to its table
as its supposed to, and instead of inserting the Pessoa object as well throws me that exception.
Here is the class that manipulates the session(it's just for tests):
public class Hibernate {
public static SessionFactory getSessionFactory() {
SessionFactory sessionFactory = null;
try {
Configuration configuration = new Configuration();
configuration.configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(
configuration.getProperties()).buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
} catch (HibernateException hbe) {
hbe.printStackTrace();
}
return sessionFactory;
}
public static void main(String[] args) {
SessionFactory sessionFactory = getSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
Pessoas a = new Pessoas();
a.setDt_criacao(new Date());
a.setDt_f(new Date());
a.setDt_i(new Date());
a.setFantasia("teste");
a.setFoto(12);
a.setIdioma("aa");
a.setLogin("aa");
a.setNome("aa");
a.setSenha("a");
a.setTp_pessoa('H');
Doc_tipo b = new Doc_tipo();
b.setDescricao("aa");
b.setTp_doc(5);
b.setTp_emissor("aaa");
Documentos c = new Documentos();
c.setAlfa_doc("aaa");
c.setDt_f(new Date());
c.setDt_f_valid(new Date());
c.setDt_i(new Date());
c.setNr_doc("aa");
c.setSeq_pessoa(a);
c.setTp_doc(b);
c.setTp_emissor(1);
c.setTp_emissor_uf("aa");
//session.save(a);
session.save(c);
session.getTransaction().commit();
session.close();
}
}
If i remove the comment on the save() Pessoas object, everything works fine, but i shouldn't have to do that, the same exception happens when i try to cascade save in another
entity that has a Many-To-One relationship with Pessoas too.
Documentos references Pessoas with primary key (Pessoas.seq_pessoa) which is only created after it has been saved/flushed to database as Pessoas uses serial. So there is nothing to reference with before save/persist actually happens.
Also, you shouldn't use multiple #Id annotations to define composite identity unless you are also defining #IdClass that holds aforementioned #Id fields. Or you could you #EmbeddedId annotation as well. JPA supports two different approaches for compound PKs. In each case there must be a PK class that includes the fields.
A) Multiple #Id fields/attributes on the entity. Names and types of fields in the entity must match those in the PK class. Must also have an #IdClass annotation on the class. Ex:
public class EmpPK {
int id;
String name;
...
}
#Entity
#IdClass(EmpPK.class)
public class Employee {
#Id int id;
#Id String name;
...
}
B) Embed an attribute of the PK class in the entity. In this case the attribute is marked with #EmbeddedId and the PK class must be annotated with #Embeddable. Ex:
#Embeddable
public class EmpPK {
int id;
String name;
...
}
#Entity
public class Employee {
#EmbeddedId EmpPK empId;
...
}

JPA Bi-directional Remove

In OpenJPA, I try to remove an entity with a bi-directional mapping to another entity. I did "find" and then "remove" but I have got an exception of "Encountered deleted object". Can someone provide me a working example?
#Entity
#Table(name="Order")
public class Order implements Serializable {
#EmbeddedId
private OrderPK pk;
...
#OneToOne(cascade=CascadeType.ALL, mappedBy="order")
private Invoice invoice;
}
#Entity
#Table(name="Invoice")
public class Invoice implements Serializable {
#EmbeddedId
private InvoicePK pk;
...
#OneToOne
#PrimaryKeyJoinColumn
private Order order;
}
#Embeddable
public class OrderPK implements Serializable {
private String id;
private Date date;
...
}
#Embeddable
public class InvoicePK implements Serializable {
private String id;
private Date date;
...
}
First, I add them in a single transaction and commit:
Order order = new Order(...);
order.set...
Invoice invoice = new Invoice(...);
invoice.set...
order.setInvoice(invoice);
invoice.setOrder(order);
em.persist(order);
Then when I try to remove the order, I expect the invoice will be gone too:
Order order = em.find(Order.class, orderPK); em.remove(order);
but I have an exception instead saying:
Encountered deleted object "org.apache.openjpa.enhance.Order$pcsubclass-
Order-OrderPK#92882281" in persistent field "Invoice.order" of managed
object "Invoice$pcsubclass-InvoicePK#92882281" during flush.

Categories