Xorty
Xorty

Reputation: 18861

JPA: Selecting subset of entity won't load @OneToOne property

I have a gigantic entity and I'd like to load its subset (ID and baz property):

@Entity
public class GiganticEntity {

    @Id Long id;

    @OneToOne(mappedBy = "giganticEntity")
    Foo foo;

    @OneToOne(mappedBy = "giganticEntity")
    Bar bar;

    @OneToOne(mappedBy = "giganticEntity")
    Baz baz;

    // default constructor + getters/setters

    public GiganticEntity(Long id, Baz baz) {
        this.id = id;
        this.baz = baz;
    }
}

I tried using following JPA query, but baz property will be null:

"SELECT new package.GiganticEntity(ge.id, ge.baz) " +
"FROM GiganticEntity ge WHERE ge.id = 1";

I tried adding an explicit join, but it resulted in null as well:

"SELECT new package.GiganticEntity(ge.id, b) FROM GiganticEntity ge " +
    "LEFT JOIN ge.baz as b " +
    "WHERE ge.id = 1";

If I only select my gigantic entity like this then everything works (but I am trying to save some joins):

"SELECT GiganticEntity g WHERE g.id = 1";

Is this achievable with JPA? I am using Hibernate as its implementation.

EDIT: Query actually needs to be LEFT JOIN, so I need all gigantic entites with baz-es.

Upvotes: 5

Views: 1173

Answers (4)

DuncanKinnear
DuncanKinnear

Reputation: 4643

The @OneToOne association must be defined as optional = false. See this question and answer

Yes, I know that sounds crazy, but it's the only way to tell Hibernate to create a Proxy for the associated Entity.

Upvotes: 0

Didier L
Didier L

Reputation: 20579

If you don't want to always fetch a OneToOne (or a ManyToOne), you should explicitly declare them as lazy (default is eager). Change your code as follows:

@Entity
public class GiganticEntity {
    @Id Long id;

    @OneToOne(mappedBy = "giganticEntity", fetch = FetchType.LAZY)
    Foo foo;

    @OneToOne(mappedBy = "giganticEntity", fetch = FetchType.LAZY)
    Bar bar;

    @OneToOne(mappedBy = "giganticEntity", fetch = FetchType.LAZY)
    Baz baz;

    // default constructor + getters/setters
}

Then write your query to fetch what you wish:

SELECT GiganticEntity g LEFT JOIN FETCH g.baz WHERE g.id = 1

Upvotes: 0

Vlad Mihalcea
Vlad Mihalcea

Reputation: 153780

Since GiganticEntity has an inverse one-to-one association to a Baz:

@OneToOne(mappedBy = "giganticEntity")
Baz baz;

It means that Baz has also an association to GiganticEntity:

@OneToOne
GiganticEntity giganticEntity;

The query can therefore become:

select new package.GiganticEntity(g.id, b)  
from Baz b
join b.giganticEntity g
where g.id : id

Edit

According to the question requirements changes:

Query actually needs to be LEFT JOIN, so I need all gigantic entites with baz-es.

You can map multiple entities to the same table. You will have the GiganticEntity containing all associations and several entity views:

@Entity
@Table(name="GiganticEntity")
@Immutable
public class GignaticBazViewEntity {

    @Id Long id;

    @OneToOne(mappedBy = "bar")
    Bar bar;

    @OneToOne(mappedBy = "baz")
    Baz baz;

    public GiganticEntity(Long id, Bar bar, Baz baz) {
        this.id = id;
        this.bar = bar;
        this.baz = baz;
    }
}

The query goes like this:

select g
from GignaticBazViewEntity g
left join fetch g.bar
left join fetch g.baz
where g.id : id

or

select g
from GignaticBazViewEntity g
FETCH ALL PROPERTIES
where g.id : id

Upvotes: 2

Antoniossss
Antoniossss

Reputation: 32517

Why such stragne contruct? I would rather

"SELECT GiganticEntity ge LEFT JOIN FETCH ge.baz WHERE g.id = 1 ";

OR

"SELECT GiganticEntity ge FETCH ALL PROPERTIES WHERE g.id = 1 ";

Upvotes: 0

Related Questions