Skip to content
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

Improve EOAttribute's value factory method to accept numeric arguments #962

Conversation

hprange
Copy link
Contributor

@hprange hprange commented Nov 12, 2021

When creating custom prototypes, one can configure a factory method to convert the attribute's value from/to a string or byte array. It's not possible to use an integer or a float value as an argument/return type, though, even if you set a value type of integer ("i") or float ("f"). Trying to fetch/save an entity containing an attribute using this prototype will throw the following exception:

Error encountered converting value of class MyEnum to type specified in attribute 'myEnumAttribute' of entity 'MyEntity'.
Conversion exception is : EOAttribute adaptorValueByConvertingAttributeValue(Object):
Unable to convert value of class java.lang.Integer for attribute 'enumAttribute' in entity 'MyEntity' to adaptor type EOAttribute.AdaptorBytesType.
Check the signature of the conversion method MyEnum.toInteger().

This pull request adds the ability to configure a factory method that receives numeric values as an argument and a conversion method that returns numeric values. For instance, it allows the creation of enum prototypes that translate from/to a number in the database.

Screen Shot 2021-11-12 at 7 02 50 PM

I'm testing this change using an Oracle database, and everything has been working fine so far. I'll try to test it on other databases as well.

When creating custom prototypes, one can configure a factory method to convert the attribute's value from/to a string or byte array. It's not possible to use an integer or a float value as an argument/return type, though, even if you set a value type of integer ("i") or float ("f"). Trying to fetch/save an entity containing an attribute using this prototype will throw the following exception:

```
Error encountered converting value of class MyEnum to type specified in attribute 'myEnumAttribute' of entity 'MyEntity'.
Conversion exception is : EOAttribute adaptorValueByConvertingAttributeValue(Object):
Unable to convert value of class java.lang.Integer for attribute 'enumAttribute' in entity 'MyEntity' to adaptor type EOAttribute.AdaptorBytesType.
Check the signature of the conversion method MyEnum.toInteger().
```

This pull request adds the ability to configure a factory method that receives numeric values as an argument and a conversion method that returns numeric values. For instance, it allows the creation of enum prototypes that translate from/to a number in the database.

I'm testing this change using an Oracle database, and everything has been working fine so far. I'll try to test it on other databases as well.
Copy link
Contributor

@paulhoadley paulhoadley left a comment

Choose a reason for hiding this comment

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

This all looks fine, though I am having trouble testing it. I have an entity with a custom Type enum attribute, which I've changed over to use int4 as the external type (PostgreSQL) from the javaEnum prototype. I've added factory and conversion methods on Type:

public static Type fromInt(int i) {
	for (Type t : values()) {
		if (t.ordinal() == i) {
			return t;
		}
	}
	return null;
}

public int toInt() {
	return ordinal();
}

Shouldn't that be enough to get me there? I can successfully save a Type value to the database, but I can't read it back. Instead, I'm getting a ClassCastExcecption from the template method:

java.lang.ClassCastException: java.lang.Integer cannot be cast to net.logicsquad.juno.model.person.Employer$Type
	at net.logicsquad.juno.model.person._Employer.type(_Employer.java:187)

Factory method doesn't seem to get called. Any thoughts?

Screen Shot 2022-01-07 at 12 11 37 pm

@hprange
Copy link
Contributor Author

hprange commented Jan 8, 2022

@paulhoadley It's broken. I'm already working on a fix. Thanks for taking the time to test it.

…tabase

Complements the previous commit by converting numeric values from the database to custom types using an attribute's `valueFactoryClass` and `valueFactoryMethod` properties. The parameter type passed to the `valueFactoryMethod` adheres to the `valueType` configured in the EOModel. It must be a reference type (java.lang.Integer, java.lang.Long, etc.), not a primitive type.

The `DateJDBCColumn` class was deprecated in favor of `ERXEnhancedJDBCColumn` to accommodate this change. I'm not sure about the impact of this change on Wonder users, but it doesn't seem too risky. Please, let me know if I'm overlooking something.
@hprange
Copy link
Contributor Author

hprange commented Jan 9, 2022

@paulhoadley I added the missing parts to this pull request. I tested the previous solution without ever calling the attribute getter method—shame on me. 🤦‍♂️ I'll do another round of tests this week to ensure I'm not missing something. Thanks again for taking the time to review my work. 😀

I'm still not happy with one tiny detail, though. Suppose the value factory method has a primitive type parameter (instead of a reference type). In that case, one gets an exception like the following:

java.lang.NoSuchMethodException: Class net.logicsquad.juno.model.person.Employer$Type does not implement method fromInt

This misleading error message may take lots of time for the inattentive to figure out the solution. Maybe we should log a warning before throwing the NoSuchMethodException just in case. Suggestions welcome.

@paulhoadley
Copy link
Contributor

Suppose the value factory method has a primitive type parameter (instead of a reference type). In that case, one gets an exception like the following:

I'm running into that right now. None of public static Type fromInt(int i), public static Type fromInt(Integer i) nor public static Type fromInt(Number i) seem to satisfy the search for a factory method. What's the signature it's looking for?

This is not the final solution. It would be great to fix it in the `NSSelector` class instead. 🤔
@hprange
Copy link
Contributor Author

hprange commented Jan 10, 2022

@paulhoadley According to the screenshot you shared above, the value type of your property is i in your model. Thus, the parameter of your factory method should be of type Integer.

public static Type fromInt(Integer i)

I reran the tests with a nested enum type (as your example) and had no problems either.

I added a new commit to this pull request to improve the error message temporarily. Could you please try again and let me know if it works?

@paulhoadley
Copy link
Contributor

Thanks @hprange—works perfectly. Perhaps I had botched something myself when testing yesterday. I think this is ready to go.

@paulhoadley paulhoadley merged commit 1aafd73 into wocommunity:master Jan 16, 2022
@hprange hprange deleted the feature/addFactoryMethodArgumentIsNumber branch June 3, 2022 22:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants