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(...)
Related
I'm experimenting with mapstruct and follow this tutorial:
mapstruct tut
I have this entity:
#Entity
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_company")
#SequenceGenerator(name = "seq_company", allocationSize = 1)
#Column(nullable = false, updatable = false)
private Long id;
private String name;
private String shortName;
public Company() {
}
public Company(Long id, String name, String shortName) {
this.id = id;
this.name = name;
this.shortName = shortName;
}
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;
}
public String getShortName() {
return shortName;
}
public void setShortName(String shortName) {
this.shortName = shortName;
}
}
And this is the simple dto:
public class CompanyDto {
#JsonProperty("id")
private Long id;
#JsonProperty("name")
private String name;
#JsonProperty("shortName")
private String shortName;
}
And here is the mapper interface:
#Mapper(componentModel = "spring")
public interface CompanyMapper {
CompanyDto companyToCompanyDto(Company company);
Company companyDtoToCompany(CompanyDto companyDto);
List<CompanyDto> companiesToCompanyDtos(List<Company> companies);
}
I certanly oversee something, because there is no setters in the generated implementation, f. e.:
#Override
public Company companyDtoToCompany(CompanyDto companyDto) {
if ( companyDto == null ) {
return null;
}
Company company = new Company();
return company;
}
What goes here wrong?
I've noticed that your CompanyDto class has private fields but no getters or setters. There is no standard way to access the fields in that class. You might need to add those in order to map in or out of that class.
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).
I have this code (just multiply the number of attributes by ALOT):
#Entity
#Table(name = "MY_TABLE")
#Immutable
public class MyEntity {
#Id
#Column(name = "ID")
private String id;
public static final String PROPERTYNAME_ID = "id";
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
/* used by JPA, needs to have same getters/setters as MyEntity */
public class MyEntityCriteria {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
Is there a way to share the attributes, getters and setters between those class?? ('extends' doesn't work as it also shares annotations...)
Probably a silly question but I don't get it.
So I've got a class like:
#Entity
#Table(name="colour"
,catalog="car_store"
)
public class Colour implements java.io.Serializable {
private Byte id;
private String name;
private Set<Car> cars = new HashSet<Car>(0);
public Colour() {
}
public Colour(String name) {
this.name = name;
}
public Colour(String name, Set<Car> cars) {
this.name = name;
this.cars = cars;
}
#Id #GeneratedValue(strategy=IDENTITY)
#Column(name="id", unique=true, nullable=false)
public Byte getId() {
return this.id;
}
public void setId(Byte id) {
this.id = id;
}
#Column(name="name", nullable=false)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(fetch=FetchType.LAZY, mappedBy="colour")
public Set<Car> getCars() {
return this.cars;
}
public void setCars(Set<Car> cars) {
this.cars = cars;
}
}
So if I'm trying to insert like:
Session session = HibernateUtil.getSessionFactory().openSession();
Colour newColour = new Colour();
newColour.setName("Deep Sea Blue");
session.save(newColour);
session.getTransaction().commit();
it should not automaticly generate new id value during this? Because it doesn't.
What am I doing wrong?
Thanks.
In the documentation provided type of the property is Long. In the example provided by you Byte is used. I assume there can be problems with this. Try to change it to Long.
Try to remove
#Column(name="id", unique=true, nullable=false)
from
public Byte getId()
It looks redundant.
And leave only:
#Id #GeneratedValue(strategy=IDENTITY)
public Byte getId() {
return this.id;
}
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;
}
}