-
-
Notifications
You must be signed in to change notification settings - Fork 260
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improved Null-checking annotations #3060
Conversation
@@ -251,7 +250,7 @@ public static Transaction currentTransaction() { | |||
* </ul> | |||
*/ | |||
public static void flush() { | |||
currentTransaction().flush(); | |||
getDefault().flush(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Delegating to DefaultServer.flush, which may throw a "no currentTransaction" exception.
@@ -313,7 +312,7 @@ public static void endTransaction() { | |||
* Mark the current transaction as rollback only. | |||
*/ | |||
public static void setRollbackOnly() { | |||
getDefault().currentTransaction().setRollbackOnly(); | |||
Objects.requireNonNull(currentTransaction(),"no currentTransaction").setRollbackOnly(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: There is no "setRollbackOnly" in DefaultServer. Intention?
@@ -35,7 +35,7 @@ | |||
* <h5>The 'default' Database</h5> | |||
* <p> | |||
* One Database can be designated as the 'default' or 'primary' Database | |||
* (see {@link DatabaseConfig#setDefaultServer(boolean)}. Many methods on DB | |||
* (see {@link DatabaseConfig#setDefaultServer(boolean)}). Many methods on DB |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed missing )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is good.
@@ -166,13 +167,15 @@ public interface Database { | |||
/** | |||
* Return the BeanState for a given entity bean. | |||
* <p> | |||
* This will return null if the bean is not an enhanced entity bean. | |||
* This will throw an IllegalArgumentException if the bean is not an enhanced entity bean. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
checked impl. will never return null
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good thanks.
@@ -404,6 +404,7 @@ | |||
* | |||
* }</pre> | |||
*/ | |||
@Nullable | |||
default <A> A findSingleAttribute() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was the root cause, why I created this PR
@@ -14,7 +15,6 @@ | |||
import io.ebeaninternal.server.query.CQuery; | |||
import io.ebeaninternal.server.transaction.RemoteTransactionEvent; | |||
|
|||
import javax.annotation.Nullable; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: Wrong annotation was used here.
throw new IllegalArgumentException("No bean type " + type.getName() + " registered"); | ||
} | ||
return desc.createBean(); | ||
return descriptor(type).createBean(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code change: I moved this check to descriptor(class)
as it claims to never return null due @NonNullApi
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. Unfortunaltely, this is not completely true.
While DefaultServer is annotated with @NonNullApi
, SpiEbeanServer
does not. And we have also some code, in our app, where we check if a class is registered. So it might be better to annotate descriptor with @nullable
T bean = find(type).select(idProp.name()).setId(id).findOne(); | ||
if (bean == null) { | ||
throw new NullPointerException("Could not find reference bean " + id + " for " + desc); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code change: I throw an NPE now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NPE is more like for preconditions or if you really messed up your code, don't you think? How does this exception propagate? I would not expect to received a NPE in user code if an entity isn't found. Isn't there an EntityNotFoundException?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, thanks. EntityNotFoundException is the better choice here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Behaviour change means it should be in its own PR. We don't want subtle behaviour changes in a PR that says "Improved Null-checking annotations" and we do want behaviour changes in their own PR that justifies why the change is good.
@@ -63,6 +64,7 @@ public Database db() { | |||
/** | |||
* Return the current transaction. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... or null if there is no current transaction in scope.
@@ -866,7 +871,7 @@ public interface Database { | |||
* the bean does not exist then this returns false indicating that nothing was | |||
* deleted. However, if JDBC batch mode is used then this always returns true. | |||
*/ | |||
boolean delete(Object bean, Transaction transaction) throws OptimisticLockException; | |||
boolean delete(Object bean, @Nullable Transaction transaction) throws OptimisticLockException; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really? (haven't checked it). But if JavaDoc states ... with an explicit transaction.
on an API method it feels weird to me that this explicit transaction can actually be null
. Could very well hide some coding problems in user code.
If that is really true, then JavaDoc should describe that you can pass in null
as a transaction and what this means if you do so. Same for all the other methods taking an explicit transaction as input parameter. In that case JavaDoc of the equivalent method without transaction parameter could say something like this method behaves exactly as passing a null transaction to delete(bean, transaction)
and link to the other method. Would avoid duplicating JavaDoc on all these methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really? (haven't checked it). But if JavaDoc states ... with an explicit transaction.
Yes, the implementation checks it, and the methods without transaction parameter just pass null there.
If that is really true, then JavaDoc should describe that you can pass in null as a transaction and what this means if you do so.
ACK
T bean = find(type).select(idProp.name()).setId(id).findOne(); | ||
if (bean == null) { | ||
throw new NullPointerException("Could not find reference bean " + id + " for " + desc); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NPE is more like for preconditions or if you really messed up your code, don't you think? How does this exception propagate? I would not expect to received a NPE in user code if an entity isn't found. Isn't there an EntityNotFoundException?
Generally I feel like Ebean JavaDoc could make more use of |
I partially agree, What I don't like, if you just repeat the documentation like in this example ¹ /**
* Returns the current transaction
* @return the current transaction
*/
Transaction currentTransaction(); But you are right, for longer javadocs, a small summary would not too bad. (I don't want to (have to) revise the complete documentation in this PR, I just want to fix the NPE warnings in the IDE ;) ) ¹) Personally, I read the documentation in 90% in the IDE by jumping to the source. (Or in doubt, reading the source itself, because code never lies, while documentation can be incorrect) |
Right so I will not merge this PR because it contains some changes that I don't like / see as being a negative. Some changes look good but once they are included with other changes that went into behaviour changes and other changes that I believe are net negative. |
@@ -35,7 +35,7 @@ | |||
* <h5>The 'default' Database</h5> | |||
* <p> | |||
* One Database can be designated as the 'default' or 'primary' Database | |||
* (see {@link DatabaseConfig#setDefaultServer(boolean)}. Many methods on DB | |||
* (see {@link DatabaseConfig#setDefaultServer(boolean)}). Many methods on DB |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is good.
* <p> | ||
* For DB's supporting getGeneratedKeys and sequences such as Oracle10 you do | ||
* not need to use this method generally. It is made available for more | ||
* complex cases where it is useful to get an ID prior to some processing. | ||
*/ | ||
@Nullable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has the potential effect that code getting the nextId ... really really should expect and handle null. We may not want that because probably this is the 0.01% case.
To me this is not the obviously correct thing to do in that practically what we have today might be better.
@@ -232,6 +230,7 @@ public static Transaction beginTransaction(TxScope scope) { | |||
/** | |||
* Returns the current transaction or null if there is no current transaction in scope. | |||
*/ | |||
@Nullable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This to me has real potential to be a net negative. More controversial than it looks.
@@ -120,6 +120,7 @@ public interface Database { | |||
/** | |||
* Return the associated read only DataSource for this Database instance (can be null). | |||
*/ | |||
@Nullable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect net negative
@@ -166,13 +167,15 @@ public interface Database { | |||
/** | |||
* Return the BeanState for a given entity bean. | |||
* <p> | |||
* This will return null if the bean is not an enhanced entity bean. | |||
* This will throw an IllegalArgumentException if the bean is not an enhanced entity bean. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good thanks.
T bean = find(type).select(idProp.name()).setId(id).findOne(); | ||
if (bean == null) { | ||
throw new NullPointerException("Could not find reference bean " + id + " for " + desc); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Behaviour change means it should be in its own PR. We don't want subtle behaviour changes in a PR that says "Improved Null-checking annotations" and we do want behaviour changes in their own PR that justifies why the change is good.
Hello @rob-bygrave, @jnehlmeier, thank you for your constructive feedback. Rob already merged some changes with #3066 I'll try to summarize, what's left in this PR
void deleteUser(String userId) {
assert DB.currentTransaction() == null : "must not run in transaction";
if (externalApi.deleteUser(userId)) {
user = DB.find(User.class, userId);
user.setDeleted(true);
DB.save(user);
}
} It would be bad, if this code runs in a transaction, that is rolled back afterwards.
I will not continue with the pull request for the time being, but will make it available for discussion first, since it only solves cosmetic problems in the IDE for us. Roland |
…gleAttribute()
Ok, I see. To me I'd say this use case is like My gut says that the alternative is for Hmm, pondering.
For these cases I don't see any downside apart from verbosity. I don't think it negatively impacts existing code that is using those methods. |
I think we close this one and have a new fresh PR if desired. |
…gleAttribute()
…gleAttribute()
…gleAttribute()
…gleAttribute()
Hello @rbygrave, in the following example
IntelliJ complains, that
value
could never be null, which is not true.In this PR, I tried to improve the API and added more
@NonNullApi
/@Nullable
annotations.I moved some null checks, so this PR is only 99% no code change.
Please take a look, if the annotations makes sense this way.