Skip to content

JAVA-1310: Implementation of MapperConfiguration and basic behavior#771

Merged
olim7t merged 3 commits into
apache:java1310from
avivcarmis:java1310
Feb 23, 2017
Merged

JAVA-1310: Implementation of MapperConfiguration and basic behavior#771
olim7t merged 3 commits into
apache:java1310from
avivcarmis:java1310

Conversation

@avivcarmis
Copy link
Copy Markdown

@avivcarmis avivcarmis commented Nov 25, 2016

Hi all!
As discussed with Alexandre on the Jira ticket, i took some effort to try and implement the mapper configuration the way i believe to be the most convenient.
So iv'e implemented the following abilities:

  • Make the annotation parsing logic configurable at mapper level (only fields, only getters, or both)
  • Make transient properties configurable at mapper level (should properties be transient by default or not)
  • Make the class hierarchy scan configurable at mapper level (enable/disable scanning of hierarchy, scan all classes or just annotated classes and supplying of a maximal ancestor to scan for properties in class hierarchy).
  • Make transient property names available to extend at mapper level (additionally to marking it at table and UDT annotations).

Take a look and let me have some feedback if you have any. I think once the general concept of MapperConfiguration is implemented, we can build more features on top of it (for example solving JAVA-1316).

Note: I added two elements to clirr.xml that prevented me from compiling the source, let me know if i made a mistake.

@avivcarmis
Copy link
Copy Markdown
Author

Hi again. I had all tests pass from intellij, but i had to tweak them to also pass through mvn directly.
Also, i had an issue discovered on another test resulting in inherited properties overriding child properties.
All of those fixed.

Additionally i wanted to supply an example of usage of the new MapperConfiguration interface. It's all documented in the classes JavaDoc but just to give a quick jump start - the main idea is this:

// Let's create a simple mapping manager - that a mapping manager who inherits
// the default configuration to all mappers created through it.
MappingManager managerWithDefaultConfiguration = new MappingManager(session());
// Now let's create a custom configuration
MapperConfiguration customConfiguration = new MapperConfiguration();
// Now let's change some configuration inside, in this case, namely, we add
// "anotherProperty" to transient property names, and then we set it to
// inherit properties only from annotated classes
customConfiguration.getPropertyScanConfiguration().setExcludedProperties(ImmutableSet.of("class", "metaClass", "anotherProperty"));
customConfiguration.getPropertyScanConfiguration().getHierarchyScanStrategy().setScanOnlyAnnotatedClasses(true);
// Now we create a custom mapping manager - just for the sake of example,
// this manager will inherit given configuration to all mappers created through it.
MappingManager managerWithCustomConfiguration = new MappingManager(session(), customConfiguration);
// Now we create a mapper with all configuration set to default
Mapper<User> mapperWithDefaultConfiguration = managerWithDefaultConfiguration.mapper(User.class);
// Now we create a mapper with configuration equals to customConfiguration
// variable above
Mapper<User> mapperWithCustomConfiguration = managerWithCustomConfiguration.mapper(User.class);
// Now let's define some new custom configuration, namely, let's tell this
// configuration to ignore ALL fields, unless annotated by either of:
// PartitionKey, ClusteringColumn, Column, com.datastax.driver.mapping.annotations.Field or Computed
MapperConfiguration customConfiguration2 = new MapperConfiguration();
customConfiguration2.getPropertyScanConfiguration().setPropertyMappingStrategy(MapperConfiguration.PropertyMappingStrategy.WHITE_LIST);
// Now either or those mappers will be created with given (same) configuration
Mapper<User> mapperWithCustomConfiguration2 = managerWithDefaultConfiguration.mapper(User.class, customConfiguration2);
Mapper<User> anotherMapperWithCustomConfiguration2 = managerWithCustomConfiguration.mapper(User.class, customConfiguration2);

@adutra adutra changed the title Implementation of MapperConfiguration and basic behavior JAVA-1310: Implementation of MapperConfiguration and basic behavior Dec 22, 2016
@adutra adutra added this to the 3.2.0 milestone Dec 22, 2016
@newkek
Copy link
Copy Markdown
Contributor

newkek commented Feb 8, 2017

So I've taken some time to take a look at this PR. This is very extended work, thank you @avivcarmis for your involvement.

As an overall remark: I believe that while this is very extensive and detailed, there are a lot of new configuration classes introduced, and while reviewing I found it pretty hard to follow. Remembering all of these classes' purposes and relationships and names may be extra complications for users. (PropertyScanStrategy/PropertyScanScope/HierarchyScanStrategy/PropertyMappingStrategy/...)

So I agree with the need of a MapperConfiguration object to do this, but trying to strip down to the simplest approach that would allow to configure all that's required, without introducing too much independent classes and names (also, there's a lot of questions where my mind might not be clear on, so don't hesitate to correct me if you see there's an aspect of the problem I overpass):

  • MapperConfiguration: as this is a "configuration for the mapping of the classes" couldn't it be called MappingConfiguration?

  • PropertyScanConfiguration: This class seems to be only containing the other actual configurations, is this necessary? Couldn't we move the configurations up to the MapperConfiguration class, and remove this one?

  • PropertyScanScope: This seems to allow both "scan fields" and "scan getters" to be true, is it actually necessary? If one sets true to both, it seems like "scan getters" takes precedence, right? Why would it take precedence? Couldn't it be a simple Enum SCAN_FIELDS or SCAN_GETTERS and people choose the one they want for a particular class?

  • HierarchyScanStrategy: Is it necessary to have a specific class for that? It seems like this provides the feature of saying whether a user wants the ancestors of the class to be scanned and until where. Couldn't we just do so that people just provide a deepestAncestorClass, in the MapperConfiguration, and when it is set, the mapper will behave accordingly (will go look for ancestors until deepest achieved). This avoids the 2 extra fields (are they just the opposite of each other or is there more to the story?) boolean hierarchyScanStrategyEnabled and boolean scanOnlyAnnotatedClass and the HierarchyScanStrategy class.

Overall I agree that having all the separate fine-grained config classes kind of gives a more extensible approach, but it's quite hard to understand the meaning of all the classes. If I resume the configurations we want to expose, there are:

  1. Enable Scan for ancestors, until a configurable deepest ancestor.
  2. Access direct fields, Or use getters.
  3. White List or Black List of fields.

Then I think simpler options directly on MapperConfiguration could be provided for these.

For 1) and 3), are these settings that make sense on the global MappingManager level? To my point of view, no, if you have 2 completely different classes to map, these settings are likely to be different. So users would more frequently want to specify these settings at the Mappers' level, then do we need to have the MapperConfiguration at the MappingManager level? I think it may not be completely necessary and can only be on the Mapper.

Thoughts?

@avivcarmis
Copy link
Copy Markdown
Author

@newkek Hi Kevin.

Okay so there's a lot to cover. For starters i'll say that i agree with all your inputs. I do have some thoughts that may make you change your mind regarding some of the comments. Let's decide which direction to go with each one.


As a general overview, i gotta say i kind of took it one step forward and instead of simply creating an interface for defining field mapping config, i tried to lay the ground for defining config for the entire mapper behavior, so that in the future we would be able to add more parameters on top of that config object instead of passing multiple config objects. Since i'm not that creative with examples, i can give you a real one, in the next PR i've pushed i've added an interface for defining naming conversion strategy (auto mapping camelCase to snake_case) - so then instead of passing (or not) one object for setting the mapping config and then passing (or not) an object for defining naming strategy, you can just pass the one global mapper config object, with just the settings you want to tweak changed, the rest left with their default values.
So this might explain the first two questions - why i've named it MapperConfiguration (it might in the future define more, hopefully all the mapper config) instead of MappingConfiguration, and why propertyScanConfiguration is a nested field within it and not at the top level.
Then again, coming to think of it, all of the mapper configuration will include mapping settings so MappingConfiguration sounds as intuitive to me. Regarding the second one (PropertyScanConfiguration), i think you have a fresher view over this and you should now know what i was trying to do, so i think you have a better position in deciding which one is more convenient (separation vs. over-complication).


Third point (PropertyScanScope) -

If one sets true to both, it seems like "scan getters" takes precedence

This is not entirely true, you may have (not that i can think of a good reason for that) some fields with getters and then some others without. You may want to give the opportunity to scan both. For example you may want to map both boo and goo:

public class Foo {
    
    @Column
    String boo;

    @Column 
    public boolean isGoo() {
        return something + someOtherThing > 1;
    }
    
}

Again, this is not a classic case so we can skip it if it's over complicating things. Also there's the last option of no fields and no getters allowed meaning no columns are mapped at all which is obviously redundant.
I think we have 3 options:

  1. leave it like that
  2. define enum {ONLY_GETTERS, ONLY_FIELDS, FIELDS_AND_GETTERS}
  3. define enum {ONLY_GETTERS, ONLY_FIELDS}
    Again, trade off between separation vs. over-complication, whichever one you think is the most convenient.

Regarding the HierarchyScanStrategy - YES there's a bit more to the story (:
boolean hierarchyScanStrategyEnabled indeed configures whether to scan upwards,
boolean scanOnlyAnnotatedClass defines whether to scan only ancestors annotated with table classes (Table, UDT, Accessor) or allow scanning of all any class.
For example, i like to have a parent class containing dateCreated and dateModified fields and extend it for each entity. But i'm not sure whether i can annotate it with Table, since it's not really a table, i just want to inherit those fields and their behavior...
Again, it might not be necessary if we force all inherited stuff to come from annotated classes. But we don't have to, and there comes the trade-off again. When you give someone too many options - he might get lost. But then again, not having an option you would like also sucks. You think it's necessary?
Regarding the deepestAncestorClass we can set it by default to Object.class (actually it already is) making scanning enabled all the way by default, and when a user wants to disable it he performs hierarchyScanStrategy.setDeepestAllowedAncestor(null), although i think hierarchyScanStrategy.setHierarchyScanEnabled(false) is simpler. Maybe we can replace it with hierarchyScanStrategy.enable() and hierarchyScanStrategy.disable().
So we also have 3 options:

  1. leave it like that
  2. remove boolean hierarchyScanEnabled and use deepestAllowedAncestor == null to check enabled/disabled
  3. leave the fields like they are but replace the setHierarchyScanEnabled setter with two setters enable() and disable()

Regarding the last point, i think that this settings (hierarchy scanning and whitelist vs blacklist) may absolutely be shared between classes. I actually wanted it for a project i worked on myself. I use annotate every field i want to persist with Column - for me it's easier to read the code this way - i do it across all my entity classes, and since the default strategy is black list i would have to explicitly create a config object for every mapper i create. Instead i want to pass it once and then have it for all the mappers.


So again i want to say that i do not disagree with any of the comments i just want you to know what i meant to achieve and maybe with popper documentation it may be convenient, but i think you're in a better position to decide on every one of those.
There's also little work to be done on every one of them so this is also not an issue.
Also if something was unclear let me know. I tried not to write too much although i kind of blew that either. (:

Cheers!

@newkek
Copy link
Copy Markdown
Contributor

newkek commented Feb 10, 2017

So this might explain the first two questions - why i've named it MapperConfiguration (it might in the future define more, hopefully all the mapper config) instead of MappingConfiguration, and why propertyScanConfiguration is a nested field within it and not at the top level.

Well, looking at the other PR (the naming strategy) isn't it a way to define "How property are going to be mapped when scanning them"? Why isn't it part of the PropertyScanConfiguration? Meaning, all the mapper does, is scanning objects' properties and mapping them, then PropertyScanConfiguration still seems like the only thing that could ever be configured on it. This class still seems superfluous to me.

About scanning for (getters && fields), I see your point. Although yeah I am not sure either if there is a need but I understand it would be a possible feature. Still, the extra class seems superfluous and I'd rather opt for a Enum config. So I'd personally leave a "?" on whether to choose option 2 or 3 for now.

For HierarchyScanStrategy, OK I see the distinction now for scanning only annotated classes VS scanning all classes. Although I'm not sure that allowing scanning only annotated classes would be such a powerful feature, and may imply that we support bad practices to the Cassandra data modelling front, but I'm not sure that's something I'd be the more appropriate to judge so ¯_(ツ)_/¯. Still, the other setting of Enabling the upward scan, and specifying a Up limit class to scan, both together seem redundant and I'd be more in favor of your option #2. And also to remove the HierarchyScanStrategy class.

After thinking more about it yeah I think the White and black listing config could be also a MappingManager level setting (thinking about JAVA-1310 so yes let's keep the MapperConfiguration on the MappingManager as well.

In general I've had my fair share of struggles when dealing with libraries that have too much configuration classes (taking the example of Astyanax), I believe the driver has a lot already, and creating a single class for just One option each time here seems too much. The more classes there are, the more potential opportunities of breaking changes, and for these reasons as you may have guessed I'm a big supporter of the KISS principle when possible. Maybe we can have the other driver devs weigh in on these points and see their points of view.

@avivcarmis
Copy link
Copy Markdown
Author

So to sum up what we have so far:

  • PropertyScanConfiguration class should be removed and it's properties should be transferred to MapperConfiguration.
  • PropertyScanScope class will be changed to an enum containing {ONLY_GETTERS, ONLY_FIELDS, FIELDS_AND_GETTERS}.
  • HierarchyScanStrategy class will be migrated to a single field of type Class<?> indicating the deepest ancestor to scan. It's default value will be Object.class and we simply allow scanning of all classes (annotated and non-annotated), meaning that by default we scan properties up the hierarchy up until Object.class (I believe this keeps backwards compatibility as it is the current default behavior). To disable scanning all together, one has to call settings.setDeepestAncestorClass(null).

Let me know how you feel about it and i'll get right on it!

@olim7t olim7t closed this Feb 16, 2017
@olim7t
Copy link
Copy Markdown
Contributor

olim7t commented Feb 16, 2017

Sorry I closed this by mistake, I removed my old branch and I didn't realize it was based on it. Will reopen shortly.

@olim7t olim7t reopened this Feb 16, 2017
Copy link
Copy Markdown
Contributor

@olim7t olim7t left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great contribution @avivcarmis !

To chime in on the conversation with Kevin:

I would slightly prefer MappingConfiguration as well (you can pass it to MappingManager so it's not specific to mapper instances).

PropertyScanConfiguration class should be removed and it's properties should be transferred to MapperConfiguration.

Actually I'm not convinced. I think we should think about what future options might come, and if it makes sense to group them all together.

PropertyScanScope class will be changed to an enum containing {ONLY_GETTERS, ONLY_FIELDS, FIELDS_AND_GETTERS}.

+1

HierarchyScanStrategy class will be migrated to a single field of type Class<?> indicating the deepest ancestor to scan.

+1

Comment thread clirr-ignores.xml
<method>java.lang.String[] transientProperties()</method>
<justification>False positive, it's an annotation and the new method has a default value</justification>
</difference>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

// try to register an updated version of the previous codec
try {
getUDTCodec(udtClass);
getUDTCodec(udtClass, defaultConfiguration); // TODO - this will update with default configuration even though previously loaded with a custom one. Solution may be to pass the MappedUDTCodec it's configuration on creation time, and then at this point we are able to get it from the previous instance
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. Instead of udtClasses, we can store the whole codecs in a deletedCodecs set, and add a way to access the configuration in MappedUDTCodec (package-private getter or direct field access).

* hierarchyScanEnabled set to false - parent classes will not be scanned at all.
* if scanOnlyAnnotatedClasses set to true, only parents annotated with table classes
* ({@code Table}, {@code UDT}, {@code Accessor}) will be scanned. And lastly - deepestAllowedAncestor
* will define the deepest parent class to scan - which default to {@code Object.class}</li>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These detailed explanations are great. I think they would be better located on each corresponding setter's javadocs in PropertyScanConfiguration (this is likely the first thing a developer will use in their IDE).

*/
public class MapperConfiguration {

private PropertyScanConfiguration propertyScanConfiguration;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The configuration is shared across multiple threads, so every mutable field (here and inside PropertyScanConfiguration itself) should be volatile.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah i'll will.

Maybe it's worth mentioning this case:

MapperConfiguration config = ...
Mapper<User> userMapper1 = manager.mapper(User.class, config);
config.changeSomething();
Mapper<User> userMapper2 = manager.mapper(User.class, config);
// at this point we should make sure that userMapper1 has no direct references to config fields,
// otherwise it would make it change it's config from outside and after creation

So for this end each configuration class implements a copy constructor and on mapper creation we clone the given config instance.

I think if we are to extend the usage of the config class in the future it may be a bit hard to maintain since the developer must remember it or fields will not be copied. If we want to avoid it at the price of a bit efficiency loss we can use some 3rd party lib to deep copy the object only on mapper creation and then we can remove those constructors.

Thoughts?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the other hand, if I set a global configuration and then change something on it, I kind of expect the change to be reflected on every mapper, currently with the copy this would not be the case.
Maybe we're overthinking this. All the configuration we have so far is stuff you set once at startup, and don't change at runtime. We could make the configuration classes immutable and avoid the copies.

we can use some 3rd party lib to deep copy the object

We avoid dependencies as much as possible. It leads to dependency hell if users already use another version of the library in their application. Guava is a recent example (JAVA-1328).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 on making the configuration immutable. I think changing the config at runtime doesn't offer a lot of value and also adds uncertainty.

return this;
}

public static class PropertyScanConfiguration {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mind moving this to a top-level class? Historically the driver codebase has been using inner classes a lot, but I must say I'm not a fan of it, it creates very long source files.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure!

@SuppressWarnings("unchecked")
private <T> Mapper<T> getMapper(Class<T> klass) {
private <T> Mapper<T> getMapper(Class<T> klass, MapperConfiguration configuration) {
configuration = new MapperConfiguration(configuration);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The configuration is not used in the map key. So if you do this:

Mapper<User> userMapper1 = manager.mapper(User.class, config1);
Mapper<User> userMapper2 = manager.mapper(User.class, config2);

Then you get the same object and userMapper2 uses config1.

Granted, this is a weird use case, so I'm not sure we want to go through the trouble of modifying the map key. But this should at least be documented in the javadoc, and maybe we should log a warning or throw an error on the second call (that forces us to store the configuration in the mapper, and implement equals on all configuration classes).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree.
We can easily implement a key containing both class and config with equals method and make sure it won't happen.

Whichever we like better.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can easily implement a key containing both class and config with equals method and make sure it won't happen.

+1 (a bit tedious for sure).

Copy link
Copy Markdown
Contributor

@olim7t olim7t Feb 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means all configuration classes must implement equals and hashcode. In hindsight I would be fine with just documenting it.

@adutra
Copy link
Copy Markdown
Contributor

adutra commented Feb 17, 2017

I would slightly prefer MappingConfiguration as well

+1

PropertyScanConfiguration class should be removed and it's properties should be transferred to MapperConfiguration.

Actually I'm not convinced. I think we should think about what future options might come, and if it makes sense to group them all together.

Yes, I'm +0 here so let's keep it the way it is now imo.

PropertyScanScope class will be changed to an enum containing {ONLY_GETTERS, ONLY_FIELDS, FIELDS_AND_GETTERS}.

+1

+1, but with some minor changes:

  1. I would name it as a strategy, stg like PropertyAccessStrategy;
  2. We need to account for setters too, so I would suggest the following names: GETTERS_AND_SETTERS, FIELDS, BOTH.

HierarchyScanStrategy class will be migrated to a single field of type Class<?> indicating the deepest ancestor to scan.

+1

+1

Copy link
Copy Markdown
Contributor

@adutra adutra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks very good in overall! Please take a look at my (very minor) suggestions. Thanks @avivcarmis !

}

/**
* Returns a collection of properties to exclude from mapping
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I would suggest to complete the documentation by stating that properties defined here will be transient on a per-mapper basis; if the user wants a more fine-grained tuning, there is also the transientProperties attribute in @Table and @UDT.
  2. I would suggest to normalize the namings here. Since it's already called transientProperties in the annotations and we cannot change that, why not call this method getTransientProperties() instead? Same for some local variables in AnnotationParser, these are currently called classLevelTransients, which is a bit confusing.


}

public enum PropertyMappingStrategy {BLACK_LIST, WHITE_LIST}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer OPT_IN and OPT_OUT, but if I'm the only one to ask that, it's fine to keep it the way it is.

// (should properties be transient by default or not)
switch (mapperConfiguration.getPropertyScanConfiguration().getPropertyMappingStrategy()) {
case BLACK_LIST:
return hasAnnotation(Transient.class) ||
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a pull request ( #750 ) that suggests to also consider fields annotated with the Java keyword transient – as they are probably not meant to be serialized nor persisted in any form. I think it's a valid point but the author has not accepted the CLA, so if you want to consider that here, I wouldn't be against it.

@SuppressWarnings("unchecked")
private <T> Mapper<T> getMapper(Class<T> klass) {
private <T> Mapper<T> getMapper(Class<T> klass, MapperConfiguration configuration) {
configuration = new MapperConfiguration(configuration);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can easily implement a key containing both class and config with equals method and make sure it won't happen.

+1 (a bit tedious for sure).

*/
class PropertyMapper {

private static final Set<Class<? extends Annotation>> COLUMN_ANNOTATIONS = ImmutableSet.of(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of defining this here, I'd suggest you to reuse AnnotationParser.VALID_COLUMN_ANNOTATIONS and AnnotationParser.VALID_FIELD_ANNOTATIONS by making them package-private and combining them together.

@avivcarmis
Copy link
Copy Markdown
Author

OK, i'm summing up everything we went through so far. Anyone objecting to anything comment so i know to hold that up. Personally i'm good with everything we have. The last point should be decided on since it was left to later discussion.

  1. PropertyScanScope class will be changed to an enum containing {GETTERS_AND_SETTERS, FIELDS, BOTH}.
  2. HierarchyScanStrategy class will be migrated to a single field of type Class<?> indicating the deepest ancestor to scan.
  3. Add this change.
  4. Document the fact that MappingManager getters will account for configuration object only for the first time, then they'll be ignored as discussed here.
  5. Add volatile modifier and remove copy constructors and duplication of config object on creation to allow changes to be reflected within an already created mapper as discussed here.
  6. Move class docs to getter methods.
  7. Move nested classes to top level ones.
  8. Rename transient config properties and document transient hierarchy (config transient props vs. class annotation transient properties vs. transient annotation) as discussed here.
  9. Rename PropertyMappingStrategy values to {OPT_IN, OPT_OUT} as discussed here.
  10. Reuse AnnotationParser.VALID_COLUMN_ANNOTATIONS as discussed here.
  11. Account for java transient modifier as discussed here (i'm actually +1 on that, fell for it myself when i started using the mapper).
  12. Rename MapperConfiguration to MappingConfiguration.
  13. Last thing left open is whether we want to remove PropertyScanConfiguration field and move it's props to MappingConfiguration top level.

@adutra
Copy link
Copy Markdown
Contributor

adutra commented Feb 18, 2017

Thanks for summing it up @avivcarmis !

I am 👍 for everything but the following:

Item 4) I am still a bit uncomfortable with it. If only the first configuration object is taken into account, there is a code smell here: it means that the configuration is meant to be manager-wide, not mapper-wide. If you agree with my diagnostic, how about creating a setter for MappingConfiguration on MappingManager and re-use that instance for all mappers? A constructor parameter would be ok as well.

Item 13) I would rather keep this class for the reasons Olivier stated.

* @param excludedProperties a collection of properties to exclude from mapping
* @return the PropertyScanConfiguration to enable builder pattern
*/
public PropertyScanConfiguration setExcludedProperties(Collection<String> excludedProperties) {
Copy link
Copy Markdown
Contributor

@tolbertam tolbertam Feb 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to Olivier's comment about immutability, Unless I missed a previous discussion rather than setters, I would prefer we use a builder strategy for PropertyScanConfiguration and MappingConfiguration. Once intialized it would be nice if the configuration would not change and we use a builder strategy for a lot of other modes of configuration.

}

@Test(groups = "short")
public void ignore_non_annotated_classes() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nit: should be should_ignored_non_annotated_classes()

@olim7t
Copy link
Copy Markdown
Contributor

olim7t commented Feb 21, 2017

@avivcarmis

  1. Add volatile modifier and remove copy constructors and duplication of config object on creation to allow changes to be reflected within an already created mapper as discussed here.

I think the final consensus was rather to make all configuration objects immutable, ie make the fields final and remove setters.

@adutra

Item 4) I am still a bit uncomfortable with it. If only the first configuration object is taken into account, there is a code smell here: it means that the configuration is meant to be manager-wide, not mapper-wide.

The configuration object taken into account depends on the first call to manager.mapper. If you provide a configuration, it will be used for this mapper, otherwise the manager-wide configuration will be used.

@olim7t
Copy link
Copy Markdown
Contributor

olim7t commented Feb 22, 2017

@avivcarmis we plan to wrap up 3.2.0 development soon, do you have bandwidth to complete those pull requests? The roadmap looks pretty clear now, so we can help you with some of the remaining tasks (we generally squash before merging, so the commits will remain attributed to you no matter what).

@olim7t olim7t merged commit b2b55d8 into apache:java1310 Feb 23, 2017
@olim7t
Copy link
Copy Markdown
Contributor

olim7t commented Feb 23, 2017

Argh, got caught again by the fact that this PR was opened against the driver's existing java1310 branch :rage4:
Let me see if I can fix this.

@olim7t
Copy link
Copy Markdown
Contributor

olim7t commented Feb 23, 2017

Nope, that doesn't work. This time I accidentally pushed your java1310 branch to our repo, and because the target was our repo's branch it's considered as merged 🤦‍♂️
I reset our branch to its previous state, but that didn't reset the PR's state.
Anyways, I'll start a new PR.

@olim7t
Copy link
Copy Markdown
Contributor

olim7t commented Feb 23, 2017

Resurrected as #814.
Sorry @avivcarmis, the branch is now in our repo so you won't be able to push directly to it. I suggest that you rename your old branch, pull our branch locally, and push any changes to your fork. I'll cherry pick your commits.

@adutra
Copy link
Copy Markdown
Contributor

adutra commented Feb 24, 2017

If you provide a configuration, it will be used for this mapper, otherwise the manager-wide configuration will be used.

Is this really useful? Can't we assume that one configuration is enough for the whole MappingManager?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants