0

In our java application we have two entities: A main account and the settings for this account. We use hibernate for providing persistence. We want the account settings to be lazy loaded. So we did this:

AccountMain:

@OneToOne(optional = false, cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
private AccountMainSettings        accountMainSettings;

@JoinColumn(name = AccountMainSettings.ACCOUNT_MAIN_SETTINGS_ID, unique = true, nullable = false, updatable = false, insertable = true)
public final AccountMainSettings getAccountMainSettings() {
return this.accountMainSettings;
}

AccountMainSettings:

@OneToOne(mappedBy = "accountMainSettings")
private AccountMain        accountMain;

public final AccountMain getAccountMain() {
return this.accountMain;
}

When we load the AccountMainSettings object in AccountMain it is proxied as it should be. But when we call a method of AccountMainSettings, the object is not loaded from the database an an NPE is thown of course. Having read Making-a-onetoone-relation-lazy didn't help much. We neither have a nullable association nor do we want to convert it to a ManyToOne association. When we switch to eager loading the problem is "solved", as the settings are loaded but they contain many fields so we don't want them to be unnecessarily loaded.

How can we implement lazy loading in this context?

Update: Here our jUnit Test:

@Test
public final void getMainAccountByAccountId() {
final AccountMain accountMain = this.accountMainDAO.getMainAccountByAccountId(PersistTestCaseConstants.SAVED_MAIN_ACCOUNT_ID);
final AccountMainSettings accountMainSettings = accountMain.getAccountMainSettings();
final String imprint = accountMainSettings.getImprint();
assertEquals(PersistTestCaseConstants.OBJECT_SUCESSFUL_ADDED, imprint.length(), 1000);
}

And the Stacktrace:

java.lang.NullPointerException
                at com.persolog.eport.service.dao.AccountMainDAOTest.getMainAccountByAccountId(AccountMainDAOTest.java:418)
                at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
                at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
                at java.lang.reflect.Method.invoke(Method.java:601)
                at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
                at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
                at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
                at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
                at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
                at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
                at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
                at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
                at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
                at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
                at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
                at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
                at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
                at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
                at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
                at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
                at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
                at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
                at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
                at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
                at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
                at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
                at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
                at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Community
  • 1
  • 1

2 Answers2

0

Getter shouldn't be final. And use @JoinColumn annotation on field, not on getter. Or use all mapping for getter, then you should add @Access(AccessType.PROPERTY)

evg
  • 1,208
  • 1
  • 7
  • 12
  • OK, we now use the `@JoinColumn` annotation on the field and added the `@Access` annotation but still get the NPE. – Planetzone Aug 09 '12 at 10:24
  • Can you provide more code? Stacktrace? Especially we are interested in what is null? – evg Aug 09 '12 at 10:39
  • you dont need the @Access if you are adding it annotations to the field only – RNJ Aug 09 '12 at 10:41
  • jUnit Test: `@Test public final void getMainAccountByAccountId() { final AccountMain accountMain = this.accountMainDAO.getMainAccountByAccountId(PersistTestCaseConstants.SAVED_MAIN_ACCOUNT_ID); final AccountMainSettings accountMainSettings = accountMain.getAccountMainSettings(); final String imprint = accountMainSettings.getImprint(); System.Out.println("Length: " + imprint.lenght()); }` – Planetzone Aug 09 '12 at 10:51
  • `imprint.lenght()` throws the NPE. I have updated the post with the jUnit Test. – Planetzone Aug 09 '12 at 11:07
  • try to remove lazy fetching as @user846476 said and see if you still get this error. – evg Aug 09 '12 at 11:14
  • Well I have an idea. As I noticed you are a big fan of final modifier. Try to remove this modifier from Account and Settings classes. Probably somehow this blocks invocation of generated proxy methods. – evg Aug 09 '12 at 20:00
  • Final modifier removed, same result as before. – Planetzone Aug 10 '12 at 07:00
0

I have this and it works for me in my code.

@JoinColumn(name = "contract_id", referencedColumnName = "id")
@OneToOne(fetch = FetchType.LAZY)
private Contract contract;

Are you missing the referencedColumnName in your code? you are specifying the name of the join column but not the name of the column on the other side of the relationship

Note all my annotations are on the field and my getter and setter are just plain old getters and setters in this class

RNJ
  • 14,308
  • 16
  • 73
  • 125
  • We tested this `@JoinColumn(name = "settingsId", referencedColumnName = "accountmainsettingsid")` but still got an NPE. – Planetzone Aug 09 '12 at 10:26
  • 1
    if you take off the (fetch = ...) does it work. By default it does eager fetching. It would be interesting to see here if the problem is the lazy fetching or the annotations in your class – RNJ Aug 09 '12 at 11:01
  • Yes it does. Eager fetching works as described in my first post. – Planetzone Aug 09 '12 at 11:14