JPA Many to Many cascade problem - java

If I create a Customer and Controller, then associate my Controller with a customer it saves fine.
If I then remove my controller it doesn't remove the relationship between them.
This causes an EntityNotFoundException when I load the Customer.
javax.persistence.EntityNotFoundException: Unable to find Controller with id 22
I'd like to know how to map this so that when a Controller is deleted the relationship is also deleted.
Database Tables
customer
controller
customer_controllers - mapping table.
The Controller's id is not getting removed from the customer_controllers mapping table.
#Entity
public class Customer implements Serializable{
private Integer id;
private Set<Controller> controllers;
#Id
#GeneratedValue
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#ManyToMany(cascade={CascadeType.ALL})
public Set<Controller> getControllers()
{
return controllers;
}
public void setControllers(Set<Controller> controllers)
{
this.controllers = controllers;
}
}
#Entity
public class Controller implements Serializable{
private Integer id;
private String name;
private String abbreviation;
#Id
#GeneratedValue
public Integer getId()
{
return id;
}
public void setId(Integer id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getAbbreviation()
{
return abbreviation;
}
public void setAbbreviation(String abbreviation)
{
this.abbreviation = abbreviation;
}
}

If you have a ManyToMany then you should map Controller to Customer with a
#ManyToMany(mappedBy="controllers")
or the other way around, depending on which side is the owning side.
As you have it now the relation is not fully defined and it will fail on events like "Cascade".

Have you checked the javadoc for #ManyToMany?
It includes the above example mappings.

you need to make the relationship bidirectional, so that the controller object is aware of its relationship to the customer. Yhis means that when the controller is deleted the record in the join table is also deleted.
This isn't the exact mapping but it gives you the idea.
#Entity
public class Controller implements Serializable{
private Integer id;
private String name;
private String abbreviation;
private Set<Customer> customers;
#Id
#GeneratedValue
public Integer getId()
{
return id;
}
public void setId(Integer id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getAbbreviation()
{
return abbreviation;
}
public void setAbbreviation(String abbreviation)
{
this.abbreviation = abbreviation;
}
#ManyToMany(cascade={CascadeType.ALL})
public Set<Customer> getCustomers()
{
return customers;
}
public void setCustomers(Set<Customers> customers)
{
this.customers= customers;
}
}

Related

How does Hibernate fetch data in a OneToMany relationship under the hood?

I am developing an ORM library similar to Hibernate. Now I'm stuck with the OneToMany relationship. I'd like to know how to fetch automatically data from database when getter of the one side is called and how Hibernate does it under the hood.
Many side
public class Film {
private int id;
private String name;
#JoinColumn(name="producer_id")
private Producer producer;
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 Producer getProducer() {
return producer;
}
public void setProducer(Producer producer) {
this.producer = producer;
}
}
One Side
public class Producer {
#Id
private int id;
private String name;
#OneToMany(mappedBy="producer")
private Set<Film> films;
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
// When called, it executes: SELECT ... FROM Film where producer_id = ?
public Set<Film> getFilms() {
return films;
}
}
In other words, I want to fill films inside Producer only when getFilms() is called.
Hibernate uses proxies of entity classes instead of real entity class using bytebuddy by generating addintional code at runtime.
BTW, I am just curious why are you developing your own ORM when you can use hibernate itself? It's the best ORM out there covering almost all kind of use cases and different optimization techniques.

Ref.get returning null

I am learning google app engine with datastore for my next project. I have made a sample app for the same.
Here are the code for entities:
#Entity
public class Quote {
#Id
private Long id;
#Parent #Load
private Ref<Author> author;
public Quote() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Author getAuther() {
return author.get();
}
public void setAuther(Author author) {
this.author = Ref.create(author);
}
}
#Entity
public class Author {
#Id
private Long id;
String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
and I am inserting a Quote using this API
#ApiMethod(
name = "insert",
path = "quote",
httpMethod = ApiMethod.HttpMethod.POST)
public Quote insert(Quote quote) {
ofy().save().entity(quote).now();
return ofy().load().entity(quote).now();
}
When I try to insert a new quote, I get my author.get() as null. I am stuck in this problem from a long time and I am not able to continue learning.
Thanks.
I was not inserting Auther before inserting Quote. You can either hide it within the Entity model or you can do it separately in an API call.

JPA: difference between setting #Id on fied and on getter

I use EclipseLink and I get very strange results. Please, consider the following code:
This code works:
#Entity
#Table(name = "someTable")
public class SomeClass{
#Id// PAY ATTENTION TO THIS LINE
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#Column (name = "somecol")// PAY ATTENTION TO THIS LINE
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
This code also works:
#Entity
#Table(name = "someTable")
public class SomeClass{
#Id// PAY ATTENTION TO THIS LINE
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
//#Column (name = "somecol")// PAY ATTENTION TO THIS LINE
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
This code also works:
#Entity
#Table(name = "someTable")
public class SomeClass{
private String id;
#Id// PAY ATTENTION TO THIS LINE
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
//#Column (name = "somecol")// PAY ATTENTION TO THIS LINE
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
This code DOESN'T work:
#Entity
#Table(name = "someTable")
public class SomeClass{
private String id;
#Id // PAY ATTENTION TO THIS LINE
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#Column (name = "somecol")// PAY ATTENTION TO THIS LINE
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
I get the following exception:
Exception Description: Entity class [class SomeClass] has no primary key specified. It should define either an #Id, #EmbeddedId or an #IdClass. If you have defined PK using any of these annotations then make sure that you do not have mixed access-type (both fields and properties annotated) in your entity class hierarchy.
at org.eclipse.persistence.exceptions.ValidationException.noPrimaryKeyAnnotationsFound(ValidationException.java:1425)
at org.eclipse.persistence.internal.jpa.metadata.accessors.classes.EntityAccessor.validatePrimaryKey(EntityAccessor.java:1542)
at org.eclipse.persistence.internal.jpa.metadata.accessors.classes.EntityAccessor.processMappingAccessors(EntityAccessor.java:1249)
at org.eclipse.persistence.internal.jpa.metadata.accessors.classes.EntityAccessor.process(EntityAccessor.java:699)
at org.eclipse.persistence.internal.jpa.metadata.MetadataProject.processStage2(MetadataProject.java:1808)
at org.eclipse.persistence.internal.jpa.metadata.MetadataProcessor.processORMMetadata(MetadataProcessor.java:573)
at org.eclipse.persistence.internal.jpa.deployment.PersistenceUnitProcessor.processORMetadata(PersistenceUnitProcessor.java:607)
at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.predeploy(EntityManagerSetupImpl.java:1948)
at org.eclipse.persistence.internal.jpa.deployment.JPAInitializer.callPredeploy(JPAInitializer.java:100)
at org.eclipse.persistence.jpa.PersistenceProvider.createEntityManagerFactoryImpl(PersistenceProvider.java:104)
at org.eclipse.persistence.jpa.PersistenceProvider.createEntityManagerFactory(PersistenceProvider.java:188)
at org.eclipse.gemini.jpa.ProviderWrapper.createEntityManagerFactory(ProviderWrapper.java:128)
at org.eclipse.gemini.jpa.proxy.EMFServiceProxyHandler.createEMF(EMFServiceProxyHandler.java:151)
at org.eclipse.gemini.jpa.proxy.EMFServiceProxyHandler.syncGetEMFAndSetIfAbsent(EMFServiceProxyHandler.java:127)
at org.eclipse.gemini.jpa.proxy.EMFServiceProxyHandler.invoke(EMFServiceProxyHandler.java:73)
at com.sun.proxy.$Proxy8.createEntityManager(Unknown Source)
Why doesn't last code work? How to explain it?
That's because there is something like #Access which you must specify on a entity and field level if you would like to use the mixed mode. There are two values AccessType.PROPERTY and AccesType.FIELD.
The default access type is defined by where you put your identifier annotation (#Id). If you put it on the field - it will be AccessType.FIELD, if you put it on the getter - it will be AccessType.PROPERTY. - edited, not defined by JPA.
If you want to annotate not fields but properties (still having #Id on field) you must define a getter and annotate it as AccessType.PROPERTY. (or vice versa for #Id on getter).

Hibernate - one-to-one mapping not working

I followed a tutorial about one-to-one mapping in Hibernate but I always end up with persistent class not known exception.
The basic scenario is, that I have an order with reference to a customer and car. I would like to save this order together with the referenced entities into database.
Order.java:
public class Order implements Serializable {
private static final long serialVersionUID = 234543634;
private String id;
private Car car;
private Customer customer;
private Date date;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
order.hbm.xml:
<hibernate-mapping>
<class name="wa2.entities.Order" table="ORDER">
<id column="ID" name="id" type="java.lang.String" />
<property column="DATE" name="date" type="java.util.Date" />
<one-to-one name="car" class="wa2.entities.Car"
cascade="save-update"></one-to-one>
<one-to-one name="customer" class="wa2.entities.Customer"
cascade="save-update"></one-to-one>
</class>
</hibernate-mapping>
Any suggestion what am I missing here?
EDIT: car (it is abstract class but there are two more classes inheriting it)
public abstract class Car implements Serializable {
private static final long serialVersionUID = 8513623981763963637L;
private String name;
private String id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
Customer.java:
public class Customer implements Serializable {
private static final long serialVersionUID = 864235654;
private String name;
private String surname;
private String adress;
private String id;
private Company company;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getAdress() {
return adress;
}
public void setAdress(String adress) {
this.adress = adress;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
Persistent class not known means that hibernate mapping does not know about the class you're trying to persist.
You should check the configuration (xml mappings in your case) and make sure that every class is mapped.
It's not enough to reference the class in <one-to-one> mapping. You should also map it as entity.
Please use annotation istead, it will be easier.
If not you need to List ALL the classes in your xml.
You need to defien iheritance strategy for the Cars.
Annotations are much simplier:
You need #Entity on top of your classes (apart from Car, see below).
You need #Id on id field.
For the inheritance, you have two option.
You can have Car annotatated as #MappedSuperclass which would mean it is not managed by JPA but its fields will be in inherited classes.
Or, more appropriate for your case, have Car anotated with #Entity and define inheritance strategy:
#Inheritance
#DiscriminatorColumn(...)

How to map the simplevalue type of Set collection using annotation

Could anyone please explain me how to use Simple value type of Set, by using annotation mapping without using one to many relationship. I have shown example in this below code. In this code I have person name and person petnames, here the person petname is the simple value type of the Set. I want map to the person name in the one table in the one table and petnames in the other table.
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
private Set<String> petname;
public Set<String> getPetname() {
return petname;
}
public void setPetname(Set<String> petname) {
this.petname = petname;
}
public boolean addPetNames(String a) {
return petname.add(a);
}
The JPA annotation works like this: (quite self explaining I think)
#ElementCollection
#CollectionTable(
name="PET_NAMES",
joinColumns=#JoinColumn(name="PERSON_ID")
)
#Column(name="PET_NAME")
private Set<String> petname;

Categories