-
Notifications
You must be signed in to change notification settings - Fork 13
Support initialisation of ToMany collections using List.of() etc #220
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
Conversation
- Add Support for List.of() & Collections.emptyList() - Add Support for Set.of() & Collections.emptySet() - Add Support for Map.of() & Collections.emptyMap()
|
|
||
|
|
||
| // these methods work because the underlying collection is a BeanCollection | ||
| bean.listOf().add(new Contact("junk")); |
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.
My expectation is, that this should fail.
When initializing with List.of etc. the collection is immutable.
Can we transfer this information to the BeanCollection? (if agent detects immutable initialization, the replaced collection is also immutable from the perspective of the user - not for lazy loading)
BTW. There are other ways to initialize immutable lists:
- Collections.EMTY_LIST
- Collections.unmodifiableList(new ArrsyList())
- Arrays.asList(...)
I think, the agent should fail or at least warn, if an unknown initialization is detectef
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.
My expectation is, that this should fail.
That is reasonable. My expectation was that people wouldn't use List.of() at all ...
Can we transfer this information to the BeanCollection?
The initialisation for OneToMany and ManyToMany is removed. So for example, if we detect new ArrayList<>() initialisation for a OneToMany - that bytecode is removed so it is as if it wasn't there at all.
So this PR was done such that initialisation via List.of() ... was detected and removed as if it wasn't there at all.
That is, the only reasons why we'd choose to initialise OneToMany collections in such a way is so that (A) the fields can be final and (B) it gives the impression to people reading the code that the collection will not be null.
... noting the for (B) this is only an impression because ebean will make sure the collection is not null regardless [well except for the new immutable read only stuff that isn't in yet and will throw an exception ... so also there no application code will see a null OneToMany collection].
So that all said, I've pushed another PR just now that detects when initialisation code for OneToMany or ManyToMany isn't supported and throws a new EnhancementException [and for maven we will fail the build at enhancement due when this is detected].
I think, the agent should fail or at least warn, if an unknown initialisation is detect
Yes. I think I've just managed to do that with the latest commit pushed here plus a change to the maven plugin to detect and fail the build for that case.
The outstanding question I think is, should we support List.of() and Collections.emptyList() [where support means that this bytecode is detected and removed just like new ArrayList<>() is].
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.
That is reasonable. My expectation was that people wouldn't use List.of() at all ...
I would never have thought of that ;)
We would need the following use cases:
null/new ArrayList()for normal lists- a distinction between unsorted sets and sorted sets/maps (we initialize them with
LinkedHashSet/LinkedHashMap)
The outstanding question I think is, should we support List.of() and Collections.emptyList()
if we decide to do, I would expect, that the BeanList/BeanSet etc. is also readonly. So BeanList needs a readOnly flag and the agent must call the new constructor new BeanList(underlyingList, true)
I can imagine use cases, where I want to protect the modification of that list by someone else.
but this is more theoretical, so it would be also OK to throw an error in the enhancer
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.
a distinction between unsorted sets and sorted sets/maps
I have no intention of ebean supporting unsorted sets/maps. I don't think that is a need / requirement.
I would expect, that the BeanList/BeanSet etc. is also readonly.
I don't think we can easily do that at this point. I don't want to try to get that clever unless we have to.
My suspicion is that I don't think people are thinking deeply about this. I suspect the use of List.of() was more just a bit of an accident.
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.
Reviewing the JPA spec, the initialisation of OneToMany and ManyToMany is vague:
"Collection-valued persistent fields and properties must be defined in terms of one of the following collection-valued interfaces regardless of whether the entity class otherwise adheres to the JavaBeans method conventions noted above and whether field or property access is used: java.util.Collection, java.util.Set, java.util.List [3], java.util.Map. The collection implementation type may be used by the application to initialize fields or properties before the entity is made persistent."
Noting that stack overflow has - https://stackoverflow.com/questions/20715143/to-initialize-or-not-initialize-jpa-relationship-mappings
At this stage, it feels like the safest option would be to not support List.of() and Collections.emptyList() etc as long as we can explicitly fail the enhancement. Noting that there might have "holes" in this detection like an application that ONLY using javaagent style enhancement could miss an error going to system out [but the entity would not be enhanced so it would not be useable as an entity - it's just the error might get misleading].
…sation is detected maven plugin will also be updated to fail the build when this exception is thrown
ebean-agent/src/main/java/io/ebean/enhance/ant/OfflineFileTransform.java
Show resolved
Hide resolved
ebean-agent/src/main/java/io/ebean/enhance/entity/ConstructorDeferredCode.java
Outdated
Show resolved
Hide resolved
…isation for OneToMany etc The thinking is that many folks are not reading the JPA spec, and there are some folks "suggesting" to initialise OneToMany collections with Collections.emptyList() ... even though it isn't as per JPA spec. Hmmm.
rPraml
left a comment
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 looks good to me now. We should not add too much complexity
|
Ok cool - I think we can roll with this. |
Previously didn't account for Collections.EMPTY_LIST (which is field bytecode rather than method bytecode). Also account for initialising as an unsupported List, Set, Map implementation like ebeans own BeanList. The expected implementations are ArrayList, LinkedHashSet, LinkedHashMap.
|
Also needs PR #221 to capture some non valid cases |
For #220, detect unexpected OneToMany initialisation cases
With 220 the ebean-agent is now conservative and only allows certain initialisation for OneToMany. This fixes the 220 change to allow OneToMany to be set via constructor. [PUTFIELD on a OneToMany was proceeded by an ALOAD]
For #220, explicitly allow OneToMany to be set via constructor
For example: