Skip to content

Support BigDecimal fields in documents with jdk17 #1439

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

Closed
jorgerod opened this issue May 18, 2022 · 7 comments
Closed

Support BigDecimal fields in documents with jdk17 #1439

jorgerod opened this issue May 18, 2022 · 7 comments
Assignees
Labels
type: bug A general bug

Comments

@jorgerod
Copy link
Contributor

jorgerod commented May 18, 2022

Hi

I am testing jdk17 in an application that contains a field in a document of type BigDecimal.
On application startup, I get the following error:

Trace
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field private final java.math.BigInteger java.math.BigDecimal.intVal accessible: module java.base does not "opens java.math" to unnamed module @7d0587f1
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354) ~[na:na]
	at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) ~[na:na]
	at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:178) ~[na:na]
	at java.base/java.lang.reflect.Field.setAccessible(Field.java:172) ~[na:na]
	at org.springframework.util.ReflectionUtils.makeAccessible(ReflectionUtils.java:787) ~[spring-core-6.0.0-M3.jar:6.0.0-M3]
	at org.springframework.data.mapping.context.AbstractMappingContext$PersistentPropertyCreator.doWith(AbstractMappingContext.java:496) ~[spring-data-commons-3.0.0-M3.jar:3.0.0-M3]
	at org.springframework.util.ReflectionUtils.doWithFields(ReflectionUtils.java:711) ~[spring-core-6.0.0-M3.jar:6.0.0-M3]
	at org.springframework.data.mapping.context.AbstractMappingContext.doAddPersistentEntity(AbstractMappingContext.java:382) ~[spring-data-commons-3.0.0-M3.jar:3.0.0-M3]
	at org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:339) ~[spring-data-commons-3.0.0-M3.jar:3.0.0-M3]
	at org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext.addPersistentEntity(CouchbaseMappingContext.java:139) ~[spring-data-couchbase-5.0.0-M3.jar:5.0.0-M3]
	at org.springframework.data.mapping.context.AbstractMappingContext$PersistentPropertyCreator.lambda$createAndRegisterProperty$3(AbstractMappingContext.java:551) ~[spring-data-commons-3.0.0-M3.jar:3.0.0-M3]
	at java.base/java.lang.Iterable.forEach(Iterable.java:75) ~[na:na]
	at org.springframework.data.mapping.context.AbstractMappingContext$PersistentPropertyCreator.createAndRegisterProperty(AbstractMappingContext.java:548) ~[spring-data-commons-3.0.0-M3.jar:3.0.0-M3]
	at org.springframework.data.mapping.context.AbstractMappingContext$PersistentPropertyCreator.doWith(AbstractMappingContext.java:502) ~[spring-data-commons-3.0.0-M3.jar:3.0.0-M3]
	at org.springframework.util.ReflectionUtils.doWithFields(ReflectionUtils.java:711) ~[spring-core-6.0.0-M3.jar:6.0.0-M3]
	at org.springframework.data.mapping.context.AbstractMappingContext.doAddPersistentEntity(AbstractMappingContext.java:382) ~[spring-data-commons-3.0.0-M3.jar:3.0.0-M3]
	at org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:339) ~[spring-data-commons-3.0.0-M3.jar:3.0.0-M3]
	at org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext.addPersistentEntity(CouchbaseMappingContext.java:139) ~[spring-data-couchbase-5.0.0-M3.jar:5.0.0-M3]
	at org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext.getPersistentEntity(CouchbaseMappingContext.java:159) ~[spring-data-couchbase-5.0.0-M3.jar:5.0.0-M3]
	at org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext.getPersistentEntity(CouchbaseMappingContext.java:41) ~[spring-data-couchbase-5.0.0-M3.jar:5.0.0-M3]
	at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:191) ~[spring-data-commons-3.0.0-M3.jar:3.0.0-M3]
	at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:90) ~[spring-data-commons-3.0.0-M3.jar:3.0.0-M3]
	at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.lambda$afterPropertiesSet$6(RepositoryFactoryBeanSupport.java:281) ~[spring-data-commons-3.0.0-M3.jar:3.0.0-M3]
	at java.base/java.util.Optional.ifPresent(Optional.java:178) ~[na:na]
	at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:281) ~[spring-data-commons-3.0.0-M3.jar:3.0.0-M3]
	at org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactoryBean.afterPropertiesSet(CouchbaseRepositoryFactoryBean.java:89) ~[spring-data-couchbase-5.0.0-M3.jar:5.0.0-M3]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1787) ~[spring-beans-6.0.0-M3.jar:6.0.0-M3]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1736) ~[spring-beans-6.0.0-M3.jar:6.0.0-M3]
	... 44 common frames omitted

Versions

  • Spring-boot version: 3.0.0-M2
  • Spring-data-couchbase version: 5.0.0-M3

Related issue: #1278

Workaround:
Add JVM Parameter --add-opens java.base/java.math=ALL-UNNAMED

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label May 18, 2022
@mikereiche
Copy link
Collaborator

I tried to reproduce this by adding a BigInteger field to User and running the integration tests. There were no failures. Could you provide the entity class? Or your github project?

BigInteger is supposed to be handled by BigIntegerToString and StringtoBigInteger Converters

OtherConverters.java:	public enum BigIntegerToString implements Converter<BigInteger, String> {
OtherConverters.java:	public enum StringToBigInteger implements Converter<String, BigInteger> { 

@mikereiche
Copy link
Collaborator

mikereiche commented May 18, 2022

I added the tests below and they pass. They use the converters from OtherConverters. Is it possible that you are using a CustomCouchbaseConverter that does not include OtherConverters (it will come from new CouchbaseCustomConversions(Collections.emptyList())) ? Or a couchbaseMappingContext that does not make use of customConversions? couchbaseCustomConversions bean which is an arg to CouchbaseMappingContext couchbaseMappingContext(CustomConversions customConversions)

	@Bean(name = BeanNames.COUCHBASE_CUSTOM_CONVERSIONS)
	public CustomConversions customConversions() {
		return new CouchbaseCustomConversions(Collections.emptyList());
	}

Tests I added to MappingCouchbaseConverterTests that pass:


	@Test
	void writesBigInteger() {
		CouchbaseDocument converted = new CouchbaseDocument();
		BigIntegerEntity entity = new BigIntegerEntity(new BigInteger("12345678901234567890"));

		converter.write(entity, converted);
		Map<String, Object> result = converted.export();
		assertThat(result.get("_class")).isEqualTo(entity.getClass().getName());
		assertThat(result.get("attr0")).isEqualTo("12345678901234567890");
		assertThat(converted.getId()).isEqualTo(BaseEntity.ID);
	}

	@Test
	void readsBigInteger() {
		CouchbaseDocument source = new CouchbaseDocument();
		source.put("_class", BigIntegerEntity.class.getName());
		source.put("attr0", "12345678901234567890");

		BigIntegerEntity converted = converter.read(BigIntegerEntity.class, source);
		assertThat(converted.attr0).isEqualTo(new BigInteger("12345678901234567890"));
	}

	private class BigIntegerEntity extends BaseEntity {
		public BigIntegerEntity(BigInteger attr0){
			this.attr0 = attr0;
		}
		BigInteger attr0;
	}

@jorgerod
Copy link
Contributor Author

Hi @mikereiche

First of all, thank you for the quick response.

My problem es with BigDecimal (with BigInteger works).

I attach a demo as simple as possible and when I start the app I get the error I mentioned before.
demo.zip

@mikereiche
Copy link
Collaborator

I have bad reading skills. I will add a converter for BigDecimal to OtherConverters and that will fix your problem. When I do that, you should be able to pick it up from 5.0.0-SNAPSHOT.

@mikereiche mikereiche added the type: bug A general bug label May 23, 2022
@mikereiche mikereiche self-assigned this May 23, 2022
@mikereiche mikereiche removed the status: waiting-for-triage An issue we've not yet triaged label May 23, 2022
@martin8877
Copy link

Same issue with Java 17 and Spring Boot 2.7.0 (spring-data-couchbase 4.4.0)

@daskerim
Copy link

Hi, we have managed to solve this issue for versions Java:17, Spring:2.6.2, Spring-Data-Couchbase:4.3.0 by adding this line below to getMappingCouchbaseConverter() method as mentioned in the example below.

((CouchbaseMappingContext) mappingCouchbaseConverter.getMappingContext()).setSimpleTypeHolder(customConversions.getSimpleTypeHolder());

This line adds our converter to Couchbase context from start. Without adding this line, the program was giving this error:

Unable to make field private final java.math.BigInteger java.math.BigDecimal.intVal accessible: module java.base does not \"opens java.math\" to unnamed module
public class CouchbaseConfiguration extends AbstractCouchbaseConfiguration {

    private final ApplicationContext applicationContext;
    
    @Bean
    public CouchbaseTemplate bucketTemplate() {
        return new CouchbaseTemplate(bucketFactory(), getMappingCouchbaseConverter());
    }
    
    public CouchbaseClientFactory bucketFactory() {
        return new SimpleCouchbaseClientFactory(getCluster(), “bucketName”, null);
    }
    
    @Bean
    public Cluster getCluster() {
        return Cluster.connect(“connectionString”, “username”, “password”);
    }
    
    public MappingCouchbaseConverter getMappingCouchbaseConverter() {
        MappingCouchbaseConverter mappingCouchbaseConverter = new MappingCouchbaseConverter();
        
        CustomConversions customConversions = customConversions();
        
        mappingCouchbaseConverter.setCustomConversions(customConversions);
        mappingCouchbaseConverter.setApplicationContext(applicationContext);
        mappingCouchbaseConverter.afterPropertiesSet();
        
        ((CouchbaseMappingContext) mappingCouchbaseConverter.getMappingContext()).setSimpleTypeHolder(customConversions.getSimpleTypeHolder());
        
        return mappingCouchbaseConverter;
    }
    
    public CustomConversions customConversions() {
        return new CouchbaseCustomConversions(
                Arrays.asList(
                        CouchbaseFieldsConverters.BigDecimalToStringConverter.INSTANCE,
                        CouchbaseFieldsConverters.StringToBigDecimalConverter.INSTANCE
                )
        );
    }
}

public class CouchbaseFieldsConverters {

    @WritingConverter
    public enum BigDecimalToStringConverter implements Converter<BigDecimal, String> {
        INSTANCE;

        @Override
        public String convert(BigDecimal source) {
            return source == null ? null : source.toString();
        }
    }

    @ReadingConverter
    public enum StringToBigDecimalConverter implements Converter<String, BigDecimal> {
        INSTANCE;

        @Override
        public BigDecimal convert(String source) {
            return source == null ? null : new BigDecimal(source);
        }
    }
}

But, as mentioned above, after adding the BigDecimal converter to OtherConverters by default in 5.0.0-SNAPSHOT, we won't need to add this line anymore.

@mikereiche mikereiche added this to the 4.4.1 (2021.2.1) milestone Jun 7, 2022
@jorgerod
Copy link
Contributor Author

Hi, we have managed to solve this issue for versions Java:17, Spring:2.6.2, Spring-Data-Couchbase:4.3.0 by adding this line below to getMappingCouchbaseConverter() method as mentioned in the example below.

((CouchbaseMappingContext) mappingCouchbaseConverter.getMappingContext()).setSimpleTypeHolder(customConversions.getSimpleTypeHolder());

This line adds our converter to Couchbase context from start. Without adding this line, the program was giving this error:

Unable to make field private final java.math.BigInteger java.math.BigDecimal.intVal accessible: module java.base does not \"opens java.math\" to unnamed module
public class CouchbaseConfiguration extends AbstractCouchbaseConfiguration {

    private final ApplicationContext applicationContext;
    
    @Bean
    public CouchbaseTemplate bucketTemplate() {
        return new CouchbaseTemplate(bucketFactory(), getMappingCouchbaseConverter());
    }
    
    public CouchbaseClientFactory bucketFactory() {
        return new SimpleCouchbaseClientFactory(getCluster(), “bucketName”, null);
    }
    
    @Bean
    public Cluster getCluster() {
        return Cluster.connect(“connectionString”, “username”, “password”);
    }
    
    public MappingCouchbaseConverter getMappingCouchbaseConverter() {
        MappingCouchbaseConverter mappingCouchbaseConverter = new MappingCouchbaseConverter();
        
        CustomConversions customConversions = customConversions();
        
        mappingCouchbaseConverter.setCustomConversions(customConversions);
        mappingCouchbaseConverter.setApplicationContext(applicationContext);
        mappingCouchbaseConverter.afterPropertiesSet();
        
        ((CouchbaseMappingContext) mappingCouchbaseConverter.getMappingContext()).setSimpleTypeHolder(customConversions.getSimpleTypeHolder());
        
        return mappingCouchbaseConverter;
    }
    
    public CustomConversions customConversions() {
        return new CouchbaseCustomConversions(
                Arrays.asList(
                        CouchbaseFieldsConverters.BigDecimalToStringConverter.INSTANCE,
                        CouchbaseFieldsConverters.StringToBigDecimalConverter.INSTANCE
                )
        );
    }
}

public class CouchbaseFieldsConverters {

    @WritingConverter
    public enum BigDecimalToStringConverter implements Converter<BigDecimal, String> {
        INSTANCE;

        @Override
        public String convert(BigDecimal source) {
            return source == null ? null : source.toString();
        }
    }

    @ReadingConverter
    public enum StringToBigDecimalConverter implements Converter<String, BigDecimal> {
        INSTANCE;

        @Override
        public BigDecimal convert(String source) {
            return source == null ? null : new BigDecimal(source);
        }
    }
}

But, as mentioned above, after adding the BigDecimal converter to OtherConverters by default in 5.0.0-SNAPSHOT, we won't need to add this line anymore.

This workaround works for me.

Thank you

mikereiche added a commit that referenced this issue Jun 15, 2022
Add BigDecimal converter, add converter tests for BigDecimal and BigInteger.
Since BigDecimail is not support, it cannot be used in the CustomerConverter tests.
So instead use ChoiceFormat for CustomConverter tests.

Closes #1439.
mikereiche added a commit that referenced this issue Jun 16, 2022
Add BigDecimal converter, add converter tests for BigDecimal and BigInteger.
Since BigDecimail is not support, it cannot be used in the CustomerConverter tests.
So instead use ChoiceFormat for CustomConverter tests.

Closes #1439.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

No branches or pull requests

5 participants