Skip to content

Give the option to have always non-null @Embeddable(s) refereneces #3702

@gzougianos

Description

@gzougianos

Hi. It is very useful for us domain-driven freaks to be able to do the following:

@Entity
@Table(name = "t_user")
public class User {
    @Id
    private Long id;

    @Column
    private String name;

    @Embedded
    private final Address address = new Address();
    //getters, setters
}

Where Address:

@Embeddable
public class Address {

    @Column
    private String streetName;

    @Column
    private String zipCode;
}

Suppose now the following MCVE:

public class Main {

    public static void main(String[] args) {
        Database database = createDatabase();

        User user = new User();
        user.setName("John Doe");

        database.save(user);

        user = database.find(User.class, 1L);
        System.out.println(user);
        System.out.println(user.getAddress()); //prints null!
    }

    private static Database createDatabase() {
        DatabaseConfig config = new DatabaseConfig();

        config.setName("playground");

        config.addPackage(User.class.getPackageName());
        config.setDdlCreateOnly(true);
        config.setDdlGenerate(true);
        config.setDdlRun(true);

        // Configure data source
        DataSourceConfig dsConfig = new DataSourceConfig();
        dsConfig.setUsername("sa");  // H2 default system admin username is 'sa'
        dsConfig.setPassword("");
        dsConfig.setUrl("jdbc:h2:mem:playground;DB_CLOSE_DELAY=-1");
        dsConfig.setDriver("org.h2.Driver");

        config.setDataSourceConfig(dsConfig);

        return DatabaseFactory.create(config);
    }
}

When I create a new User, the Address is always non-null due to basic Java. However, when I load back the User object the Address is null because ALL FIELDS of address are null. It would be convinient to be able to do something like:

    @io.ebean.Embedded(allowNullRef = false)
    private final Address address = new Address();

Where in this case, Ebean ensures that address embeddable field will never be null.

Of course I tried many ways to work around it. Let's say I give up on the final and do the following:

    @Embedded
    private Address address = new Address();

    public Address getAddress() {
        if (address == null) {
            address = new Address();
        }
        return address;
    }

But in this case, if I do the following:

        user = database.find(User.class, 1L);
        System.out.println(user);
        System.out.println(user.getAddress());//prints non-null, and thats ok
        System.out.println(user.getAddress().getStreetName());//prints null, ofc thats ok

        database.update(user);

Then, Ebean will detect the address as a "new" reference. Thus, Ebean thinks that the object is dirty and the update will do an actual update. However, in reality nothing was changed, other than the Address reference where the getter could be called even just for reading as shown in the example.

I tried to do the same with things like PostLoad etc, but those things are trickier to work with in the first place when other frameworks are also involved into a real application. Other workaround I found, but I really don't like is the following:

    @Embedded
    private final Address address = new Address();

And then:

@Embeddable
public class Address {

    @Column
    private String streetName;

    @Column
    private String zipCode;

    @Column
    private final int jpa_workaround = 0;
}

And now Ebean dirty check works:
07:11:22.965 [main] DEBUG io.ebean.internal - Update skipped as bean is unchanged: User@0(id:1, name:John Doe, address:Address@1(jpa_workaround:0))

What do you think? Would it be possible? I think I could even try to contribute and implement the change myself. Is this discussed already maybe? Because I could not find any "legit" information upon it. Is there maybe a workaround I did not think of? Let me know...

Thanks!

P.S: This is what I use:

        <dependency>
            <groupId>io.ebean</groupId>
            <artifactId>ebean</artifactId>
            <version>17.1.1</version>
        </dependency>

        <dependency>
            <groupId>io.ebean</groupId>
            <artifactId>ebean-ddl-generator</artifactId>
            <version>17.1.1</version>
        </dependency>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions