1

I have a class

@MappedSuperclass
public abstract class MyAbstractProperty <T extends Object>{

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    private Long id;

    @Column(name="defaultValue")
    protected T defaultValue;

//...

}

Which is basis for sub classes like class MyLongProperty extends MyAbstractProperty<Long>{}. This works fine. The problem arises when I want to store Text in the class class MyStringProperty extends MyAbstractProperty<String>{}.

I have learned that hibernate per default stores Strings only with max 255 chars (see: JPA: "Data too long for column" does not change). Therefore my defaultValuewould need an additional annotation like:

@Entity
class MyStringProperty extends MyAbstractProperty<String>{

    @Type(type="org.hibernate.type.StringClobType")
    @Column(name="defaultValue")
    protected String defaultValue; // problem since defaultValue is already defined in MyAbstractProperty<>.
}

How can I ensure this since the defaultvalue is already defined in MyAbstractProperty? I don't think it is ok to move the type annotation to MyAbstractProperty since this might have side effects on other classes like MyLongProperty.

For example when I was setting @Lob in MyAbstractProperty the SQL definition of MyStringProperty included DEFAULTVALUE BLOB(255) instead of DEFAULTVALUE BIGINT.

Additional question: There seem to be different options for defining the column as text data (e.g. @Type(type="org.hibernate.type.StringClobType"), @Lob) Where are the maijor differences in these options. What best to take. (My main database is HyperSQL, but I also target for SQLite, MariaDB and MySQL).

Edit

Based on the recent feedback it seems like the solution might be abstract getters/setter. I have to check what are the implications on this on my code.

I had an alternative - not finished - idea, which might cause less modifictions of my code. Is it possible to define a class

class MyText extendes String; 

such as that all instances of MyText are automatically stored as @Lob? Than I would just have to modify MyAbstractProperty<String> to MyAbstractProperty<MyText>.

BerndGit
  • 1,530
  • 3
  • 18
  • 47
  • i think you need to follow one of inheritance strategies, so please read this article http://www.thejavageek.com/2014/05/14/jpa-single-table-inheritance-example/ – mibrahim.iti Feb 27 '17 at 23:40
  • So mostly you need to use @Inheritance annotation with the strategy you want in your abstract class – mibrahim.iti Feb 27 '17 at 23:45
  • Also i think you should remove defaultValue from MyStringProperty as it will be already in super class and move @Type(type="org.hibernate.type.StringClobType") to MyAbstractProperty – mibrahim.iti Feb 27 '17 at 23:50
  • The last thing, if you are using JPA with Hibernate provider so why you just not use @lob annotation on your string ? please have a look at http://docs.oracle.com/javaee/5/api/javax/persistence/Lob.html – mibrahim.iti Feb 27 '17 at 23:53
  • @mibrahim.iti: moving the annotation to MyAbstractProperty migth have sideeffects on classes like MyLongProperty. I think i have seen somewhere the possibility to alter the specification of columbs, which were defined in super-classes, but i coudn't find this in the hibernate documentation. – BerndGit Feb 28 '17 at 07:01
  • @Lob is also a possibility which i want to investigate. However here i have the same difficulty with placing it in the correct class. – BerndGit Feb 28 '17 at 07:04
  • I don't think you can have `@Column` on a generic (T) variable. Hibernate would have no way of knowing how to map this. Also, since there is one column per variable, you can't have a column that can hold both a String and an Integer for instance. – Tobb Mar 01 '17 at 10:25
  • I would remove the variable from the abstract class, and replace it with an abstract get-method (to give some indication to inheriting classes that they need to define this value mapped to some column) – Tobb Mar 01 '17 at 10:26
  • @Tobb: basically the column-annotation works here since the abstract class has no entity. Tested for Long, Boolean, and String (with the limitation of 255 chars) – BerndGit Mar 01 '17 at 10:29
  • @Tobb: i'll investigste more in the getter/setter approach. – BerndGit Mar 01 '17 at 10:31
  • Ah, it's a mapped superclass.. Then you should be able to redefine the variable in a subclass and add @Lob for a longer String.. – Tobb Mar 01 '17 at 10:52
  • Ok. I was supprised to see that i can do this w/o compile error (just a warning: field hides another field). However i now have a runtime exception which i have to investigate in more detail. Maybe data acess, which uses the superclass doesn't use the correct field now. – BerndGit Mar 01 '17 at 11:04
  • Seems that methods from 'MyAbstractProperty' still use the varable w/o @Lob. I can try to re-implement certain methods in MyStringProperty, but this feels like an antipattern. I have concerns on the robustness of this approach. – BerndGit Mar 01 '17 at 11:10
  • @BerndGit i think no way to override defaultValue property, the only way is to remove it from generic class and put it direct in every subclass, so only id will be in your generic class – mibrahim.iti Mar 02 '17 at 20:09

1 Answers1

3

You could leave only an abstract getter in the superclass, and define the field in subclasses:

@MappedSuperclass
public abstract class MyAbstractProperty<T extends Object>{

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    private Long id;

    public abstract T getDefaultValue();
}

@Entity
class MyStringProperty extends MyAbstractProperty<String>{

    @Type(type="org.hibernate.type.StringClobType")
    @Column(name="defaultValue")
    protected String defaultValue;

    @Override
    public String getDefaultValue() {
        return defaultValue;
    }
}

In order to avoid boilerplate for all other cases which don't require special treatment, you can simply create another abstract class which contains default mapping, and then extend other entities from it:

@MappedSuperclass
public abstract class MyDefaultProperty<T> extends MyAbstractProperty<T extends Object> {

    @Column(name="defaultValue")
    protected T defaultValue;

    @Override
    public T getDefaultValue() {
        return defaultValue;
    }
}
Dragan Bozanovic
  • 23,102
  • 5
  • 43
  • 110
  • Thank you Dragan Bozanovic for your answer. It seems to be possible althrough some refactoring in my code will be required. Kindly check also the edit of my question. Maybe this idea could lead to a simpler solution? – BerndGit Mar 03 '17 at 09:31
  • @BerndGit You can't extend from `String`, it's a final class. And even if you could, the effect would be the same, as you would need to place the `@Lob` annotation on the field of `MyText` type in the containing class. – Dragan Bozanovic Mar 03 '17 at 11:45