4

I have a class that is suitable for a builder pattern, there are many params and I'd rather not use a ton of telescopic constructors.

My problem is that this class is a JPA entity and that is very new to me.

Having private final data members is throwing an error as I they are not initialized in the constructor and as far as I'm aware, JPA requires an empty protected constructor.

Can anyone help please? An example would be fantastic, I've included a basic example of the code below but it's very generic. I've omitted many of the accessors and data members to save space/time.

  @Entity//(name= "TABLE_NAME") //name of the entity / table name
public class Bean implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id //primary key
    @GeneratedValue
    Long id;
    private final DateTime date;
    private final String title;
    private final String intro;


    //used by jpa
    protected Bean(){}

    private Bean(Bean Builder beanBuilder){
        this.date = beanBuilder;
        this.title = beanBuilder;

        this.intro = beanBuilder;
    }

    public DateTime getDate() {
        return date;
    }

    public String getTitle() {
        return title;
    }

    public static class BeanBuilder Builder{

        private final DateTime date;
        private final String title; 

        //private optional 

        public BeanBuilder(DateTime date, String title) {
            this.date = date;
            this.title = title;
        }

        public BeanBuilder intro(String intro){
            this.intro = intro;
            return this;
        }

        public BeanBuilder solution(String solution){
            this.intro = solution;
            return this;
        }

        public Bean buildBean(){
            return new Bean(this);
        }

    }

}
HII
  • 3,420
  • 1
  • 14
  • 35
null
  • 3,469
  • 7
  • 41
  • 90
  • You should do some reading about `final`. Maybe http://stackoverflow.com/questions/15655012/how-final-keyword-works or https://en.wikipedia.org/wiki/Final_(Java) –  Aug 07 '15 at 19:37
  • I don't want the object to be mutable... – null Aug 07 '15 at 19:39
  • I wouldn't have a constructor that accepted the builder. This way, the builder is **not** responsible for building the object. The purpose of that class is merely as a container (map with hard coded attributes so to speak!). – Martin Andersson Aug 07 '15 at 20:17
  • @MartinAndersson - looks like I failed at copy/pasting the code in. the constructor should take a builder and then attributes are set to the values of the builder. I can then have something like build.x("valueofx").builder.y(val) and then the object under question will have those values set but in the case where y is not set it'll be null which is also fine. At least that's how I read it...which could be very wrong :) – null Aug 07 '15 at 20:49
  • @Steve Green Making a class immutable is as simple as not providing mutation behaviors on that object and keeping all fields private. True, you could accidentally mutate a field from inside the class, but from your clients point of view, the class is immutable. – plalx Aug 07 '15 at 20:54

5 Answers5

2

Member fields marked as final must have a value assigned during construction and this value is final (i.e. cannot change). As a consequence, all declared constructors must assign a value to all final fields.

This explain your compiler error.

From the JLS:

A blank final instance variable must be definitely assigned at the end of every constructor of the class in which it is declared, or a compile-time error occurs (§8.8, §16.9).

  • Do you know of a way where I can maintain the immutability of the class AND still use it for JPA? As far as I know JPA needs an empty constructor. But, it can't be as simple as that...? – null Aug 07 '15 at 19:46
  • 1
    Hibernate provide an `@Immutable` annotation, see http://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html/ch12.html –  Aug 07 '15 at 19:48
2

I'm not sure what you meant for that, but having immutable objects is not a great idea when working in Hibernate (not to say you cannot do it, or you shouldn't).

Think about it, because Hibernate/JPA defines "states" for objects they are meant to be mutable; otherwise you would have a static database, or something like insert-once-and-never-modify database.

The immutable concept is a very known (nowadays) concept borrowed mainly from Functional Programming that doesn't really apply in the same way to OOP. And if you are working with Hibernate you shouldn't have immutable objects...at least till today's date.

UPDATE

If you want to have what they call read-only entities, you can use the @Immutable annotation from Hibernate itself. Pay close attention to collections as entity members.

Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
x80486
  • 6,627
  • 5
  • 52
  • 111
  • I see what you're saying but in this case I really don't want them to be modified once inserted into the db. I'm using JPA/Hibernate as an alternative to SQL simply to get some exposure, it may not have been the best tool for the job. :) – null Aug 07 '15 at 19:51
  • 1
    @SteveGreen if you really know that whatever you are going to insert in the database is not going to change, then you can go that way and use some annotation named `Immutable` (or something like that...I can't really remember), but it's like having a read-only entity – x80486 Aug 07 '15 at 19:55
  • thank you. I'm getting the impression from you guys that what I'm trying to do it a bad idea :) I was trying to create a clean way of generating different website pages by using the builder. One type may have 2 images, another 4 for example. My preference is having one tsp to handle them depending on what the type is but maybe there's a better way :) – null Aug 07 '15 at 20:00
  • @ɐuıɥɔɐɯ "Think about it, because Hibernate/JPA defines "states" for objects they are meant to be mutable". That's far from true... in Domain-Driven Design immutable objects (Value Objects) are extremely common and are even usually preferred over entities when possible. From the domain perspective, as long as a class doesn't offer mutation behaviors to it's clients and is fully encapsulated, that class or concept is immutable from the client perspective. There is no need for an `@Immutable` annotation at all, although it may boost performances (not sure). – plalx Aug 07 '15 at 21:08
  • @plalx I don't want to start a neverending thread here, but you are talking about something COMPLETELY different, which is the main point when comparing a VO and any Hibernate entity. VOs are objects that contains attributes but have no conceptual "identity" (don't confuse "indentity" in this context) – x80486 Aug 08 '15 at 01:50
  • I do not see how your comment is relevant to mine? The point is that objects do not have to use final members to be immutable from the client's perpective and an example of that is most VOs. – plalx Aug 08 '15 at 02:32
  • Also, append-only models where by definition every entry has to be immutable is quite common. – plalx Aug 08 '15 at 02:38
2

Not sure why you want to do that. Maybe it is better to define the member variable as @Column(name = "id", nullable = false, updatable = false) for example

ACV
  • 9,964
  • 5
  • 76
  • 81
2

The JPA 2.1 specification, section "2.1 The Entity Class", says:

No methods or persistent instance variables of the entity class may be final.

..meaning that there's no way for you to build a truly immutable JPA entity. But, I don't really see how that can be such a big issue. Just don't let the entity class expose public setters?

Martin Andersson
  • 18,072
  • 9
  • 87
  • 115
1

Entities are meant to be mutable when it comes to strict Java immutability. For example, lazily loaded associations will change the object state once the association is accessed.

If you need to use entity data in a real immutable fashion (for multi-threaded purposes for example), then consider using DTOs (because entities are not meant to be accessed cuncurrently either).

Dragan Bozanovic
  • 23,102
  • 5
  • 43
  • 110