I have an abstract superclass entity which is the following:
#Entity
public abstract class Notification {
#id
private int id;
private Stringname;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
}
Then, I have two class which extends this abstract class
#Entity
public class smsNotifcation extends Notification {
#id
private String name;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
}
#Entity
public class emailNotifcation extends Notification {
#id
private String name;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
}
When I use persistence.xml to auto-create the tables in the PostgreSQL, it only creates the table for the Notification class. I am unable to understand why it wont create tables for the other two. If it is not supposed to create them, then how would I map the information. By mapping information, I meant that how will the name attribute from Notification class know that to which class this name belongs to i.e the name in abstract class's database belongs to smsNotification or emailNotification.
Related
I have an absract class that describes a general functionality of children classes.
When I initialize a child class I want to set a specific enum class as a member on the parent abstract class.
How can I do that?
Example:
AbstractFunctionality.java
public abstract class AbstractFunctionality {
protected String Name;
protected String Surname;
// specific enum class
public AbstractFunctionality(String Name, String Surname){
this.Name = Name;
this.Surname = Surname;
}
}
Child1.java
public class Child1 extends AbstractFunctionality {
public Child1(){
super("Jane","Austen");
}
}
How can I specify that I want the public enum Writers in my Child1 class?
The simpler approach is just add the enum type as the type of the field parameter of the abstract class:
public abstract class AbstractFunctionality {
protected String Name;
protected String Surname;
Writers writers;
public AbstractFunctionality(String Name, String Surname, Writers writers){
this.Name = Name;
this.Surname = Surname;
this.writers = writers;
}
}
the subclass:
public class Child1 extends AbstractFunctionality {
public Child1(){
super("Jane","Austen", Writers.SOME_FIELD);
}
}
Alternatively, you can make your enum Writers implement a more general interface let us say IWriters
public abstract class AbstractFunctionality {
protected String Name;
protected String Surname;
protected IWriters writers;
public AbstractFunctionality(String Name, String Surname, IWriters writers){
this.Name = Name;
this.Surname = Surname;
this.writers = writers;
}
}
The interface:
public interface IWriters {
...
}
the enum:
public enum Writers implements IWriters{
...
}
The benefit of this approach is that you can have different enums types implementing the same interface, and therefore they can also be used on the abstract class.
Maybe you can set generic in abstract class
public abstract class AbstractFunctionality<T extends Enum<T>> {
protected String Name;
protected String Surname;
T specificEnum
public AbstractFunctionality(String Name, String Surname,T specificEnum){
this.Name = Name;
this.Surname = Surname;
this.specificEnum=specificEnum;
}
In the sub class
public class Child1 extends AbstractFunctionality<Writers> {
public Child1(){
super("Jane","Austen",Writers.POEM);
}
}
}
I have such situation:
There are a few groups of POJO classes, each with one parent. Each group serves to be working with some recording of data - to the XML with the help of JAXB and to the database with the help of Hibernate. A simplified version of my working code is:
public static abstract class Habit{
String habitName;
/* constructors */
/* getter & setter */
}
#Entity
#Access(AccessType.PROPERTY)
#Table(name = "habit")
public static class DBHabit extends Habit{
#Id
#GeneratedValue
private int id;
#Column(name = "habit_name")
public String getHabitName() {
return habitName;
}
public void setHabitName(String habitName) {
this.habitName = habitName;
}
}
#XmlAccessorType(XmlAccessType.PROPERTY)
#XmlRootElement(name = "habit")
public static class XmlHabit extends Habit{
#XmlElement
public String getHabitName() {
return habitName;
}
public void setHabitName(String habitName) {
this.habitName = habitName;
}
}
public static abstract class Person{
int age;
String name;
List<Habit> habits;
/* Constructor */
/* Getters & Setters */
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Habit> getHabits() {
return habits;
}
public void setHabits(List<Habit> habits) {
this.habits = habits;
}
}
#Entity
#Access(AccessType.PROPERTY)
#Table(name = "person")
public static class DBPerson extends Person{
#Id
#GeneratedValue
private int id;
#Column(name = "age")
#Override
public int getAge() {
return age;
}
#Override
public void setAge(int age) {
this.age = age;
}
#Column(name = "name")
#Override
public String getName() {
return name;
}
#Override
public void setName(String name) {
this.name = name;
}
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL, targetEntity = DBHabit.class)
#Override
public List<Habit> getHabits() {
return habits;
}
#Override
public void setHabits(List<Habit> friends) {
this.habits = habits;
}
}
#XmlAccessorType(XmlAccessType.PROPERTY)
#XmlType(name = "", propOrder = {
"age",
"name",
"habits"
})
#XmlRootElement(name = "state")
public static class XmlPerson extends Person{
#Override
#XmlElement(required = true)
public int getAge() {
return super.getAge();
}
#Override
public void setAge(int age) {
super.setAge(age);
}
#Override
#XmlElement(required = true)
public String getName() {
return super.getName();
}
#Override
public void setName(String name) {
super.setName(name);
}
#Override
#XmlElement(required = true)
#XmlElementWrapper(name = "friends")
public List<Habit> getHabits() {
return super.getHabits();
}
#Override
public void setHabits(List<Habit> habits) {
super.setHabits(habits);
}
}
I have problems with working with Hibernate. Thought I wrote that I expect the recording of DBHabit.class in targetEntity, hibernate does not record any information about habits, this table is always empty.
Please give me a hint, what can I make in such situation or advice, how I can make a similar abstract system like this for the writing in different ways.
Thank you for attention!
A couple of strange thing i noticed in your mapping:
a #OneToOne mapping, backed by a List<Habit>. Why not directly Habit ?
You annotate class with #Access(AccessType.PROPERTY) but then you put the mapping annotation on accessor methods (you're issue might come from there).
Instead you could try to annotate the parent abstract class as #MappedSuperclass. This way, you do not have to redefine the accessor. Or if you do not wish to put hibernate annotations in the parent class, remove the #Access(AccessType.PROPERTY) annotation.
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 a class which is identical to enum. The only difference is that I create the class so that I can dynamically create the enums. What I want is to override the cast operation of enum so that I can give the enum instance to a method where it gets the class instance.
Example:
public enum SpecialEnum {
FIRST("First"), SECOND("Second");
private String name;
SpecialEnum(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class SpecialClass {
private String name;
public SpecialClass(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public void displayName(SpecialClass specialClass) {
System.out.println(specialClass.getName());
}
Lets say that the SpecialClass instances are coming from a server where I display them. But I also want to use my pre-defined Special enum classes as well. I know that I could create static instances of SpecialClass and use them but not that it looks messy, also using enums are beneficial to my occasion as well. Is there a way to override casting operation of the enum class of a work around maybe?
Extract an interface:
public interface Named {
public String getName();
}
public class SpecialClass implements Named {
private String name;
public SpecialClass(String name) {
this.name = name;
}
#Override
public String getName() {
return name;
}
}
public enum SpecialEnum implements Named {
FIRST("First"), SECOND("Second");
private String name;
SpecialEnum(String name) {
this.name = name;
}
#Override
public String getName() {
return name;
}
}
public void displayName(Named specialClass) {
System.out.println(specialClass.getName());
}
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;
}
}