[ 
https://issues.apache.org/jira/browse/IGNITE-20024?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

bin.yin updated IGNITE-20024:
-----------------------------
    Summary: When H2 is used as the underlying storage, an exception is thrown 
"Can not set java.time.LocalDateTime field 
org.apache.ignite.examples.model.Person.createdTime to java.sql.Timestamp"  
(was: When MySQL is used as the underlying storage, an exception is thrown "Can 
not set java.time.LocalDateTime field 
org.apache.ignite.examples.model.Person.createdTime to java.sql.Timestamp")

> When H2 is used as the underlying storage, an exception is thrown "Can not 
> set java.time.LocalDateTime field 
> org.apache.ignite.examples.model.Person.createdTime to java.sql.Timestamp"
> ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: IGNITE-20024
>                 URL: https://issues.apache.org/jira/browse/IGNITE-20024
>             Project: Ignite
>          Issue Type: Bug
>          Components: persistence
>            Reporter: bin.yin
>            Assignee: bin.yin
>            Priority: Major
>
> h3. Question:
> {{First, I use `IgniteCache#loadCache` load from RDBMS. When I use 
> IgniteCache.get(key) to get the corresponding entity, ignite throws the 
> following error "Can not set java.time.LocalDateTime field 
> org.apache.ignite.examples.model.Person.createdTime to java.sql .Timestamp"}}
> h3. Conditions and usage:
>  # This code run in 'ignite-examples' module;
>  # In ignite 2.14.0, I use H2 as the underlying storage;
>  # In `{{{}CacheJdbcPojoStoreFactory`{}}}, I configured the mapping between 
> the `{{{}datetime type`{}}} of the database and JDK8's 
> `{{{}LocalDateTime`{}}}:
> {{new JdbcTypeField(Types.TIMESTAMP, "created_time", LocalDateTime.class, 
> "createdTime")}}
> h3. Problem analysis:
> By default, {{CacheJdbcPojoStore}} uses 
> {{org.apache.ignite.cache.store.jdbc.JdbcTypesTransformer#getColumnValue}} to 
> parse jdbc's ResultSet.
> For the {{LocalDateTime}} type of JDK8, {{java.sql.ResultSet#getObject(int)}} 
> is used to obtain the value of the specified column (here it is read as 
> {{{}java.sql.Timestamp{}}}). When the object is finally constructed, the type 
> is inconsistent Causes {{sun.reflect.FieldAccessor#set}} assignment to fail.
>  
> *Java Code:*
> CacheJdbcPojoStore.java:
>  
> {code:java}
> package org.apache.ignite.examples.datagrid.store.auto;
> import org.apache.ignite.Ignite;
> import org.apache.ignite.IgniteCache;
> import org.apache.ignite.Ignition;
> import org.apache.ignite.cache.CacheAtomicityMode;
> import org.apache.ignite.cache.store.jdbc.CacheJdbcPojoStoreFactory;
> import org.apache.ignite.cache.store.jdbc.JdbcType;
> import org.apache.ignite.cache.store.jdbc.JdbcTypeField;
> import org.apache.ignite.cache.store.jdbc.dialect.H2Dialect;
> import org.apache.ignite.configuration.CacheConfiguration;
> import org.apache.ignite.examples.model.Person;
> import org.apache.ignite.examples.util.DbH2ServerStartup;
> import org.h2.jdbcx.JdbcConnectionPool;
> import javax.cache.configuration.Factory;
> import javax.sql.DataSource;
> import java.sql.Types;
> import java.time.LocalDateTime;
> /**
>  * To start the example, you should:
>  * <ul>
>  *     <li>Start H2 database TCP server using {@link DbH2ServerStartup}.</li>
>  *     <li>Start example using {@link CacheAutoStoreExample}.</li>
>  * </ul>
>  * <p>
>  */
> public class CacheJdbcPojoStore {
>     private static final String CACHE_NAME = 
> CacheJdbcPojoStore.class.getSimpleName();
>     public static void main(String[] args) throws Exception {
>         DbH2ServerStartup.populateDatabase();
>         try (Ignite ignite = Ignition.start("example-ignite.xml")) {
>             System.out.println();
>             System.out.println(">>> Cache store example started.");
>             try (IgniteCache<Long, Person> cache = 
> ignite.getOrCreateCache(cacheConfiguration())) {
>                 cache.loadCache(null, Long.class.getName(), "SELECT * FROM 
> PERSON WHERE ID = 3");
>                 System.out.println(cache.get(3L));
>             }
>             finally {
>                 ignite.destroyCache(CACHE_NAME);
>             }
>         }
>     }
>     private static CacheConfiguration<Long, Person> cacheConfiguration() {
>         CacheConfiguration<Long, Person> cacheCfg = new 
> CacheConfiguration<>(CACHE_NAME);
>         cacheCfg.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL);
>         // Configure JDBC store.
>         CacheJdbcPojoStoreFactory<Long, Person> factory = new 
> CacheJdbcPojoStoreFactory<>();
>         factory.setDialect(new H2Dialect());
>         factory.setDataSourceFactory((Factory<DataSource>) () -> {
>             return 
> JdbcConnectionPool.create("jdbc:h2:tcp://localhost/mem:ExampleDb", "sa", "");
>         });
>         JdbcType jdbcType = new JdbcType();
>         jdbcType.setCacheName(CACHE_NAME);
>         jdbcType.setDatabaseTable("PERSON");
>         jdbcType.setKeyType(Long.class);
>         jdbcType.setValueType(Person.class);
>         jdbcType.setKeyFields(new JdbcTypeField(Types.BIGINT, "id", 
> Long.class, "id"));
>         jdbcType.setValueFields(
>                 new JdbcTypeField(Types.BIGINT, "id", Long.class, "id"),
>                 new JdbcTypeField(Types.VARCHAR, "first_name", String.class, 
> "firstName"),
>                 new JdbcTypeField(Types.VARCHAR, "last_name", String.class, 
> "lastName"),
>                 new JdbcTypeField(Types.TIMESTAMP, "created_time", 
> LocalDateTime.class, "createdTime")
>         );
>         factory.setTypes(jdbcType);
>         cacheCfg.setCacheStoreFactory(factory);
>         cacheCfg.setIndexedTypes(Long.class, Person.class);
>         return cacheCfg;
>     }
> }
> {code}
> Person.java:
>  
> {code:java}
> package org.apache.ignite.examples.model;
> import java.io.Serializable;
> import java.time.LocalDateTime;
> import java.util.concurrent.atomic.AtomicLong;
> import org.apache.ignite.cache.affinity.AffinityKey;
> import org.apache.ignite.cache.query.annotations.QuerySqlField;
> import org.apache.ignite.cache.query.annotations.QueryTextField;
> /**
>  * Person class.
>  */
> public class Person implements Serializable {
>     /** */
>     private static final AtomicLong ID_GEN = new AtomicLong();
>     /** Name of index by two fields (orgId, salary). */
>     public static final String ORG_SALARY_IDX = "ORG_SALARY_IDX";
>     /** Person ID (indexed). */
>     @QuerySqlField(index = true)
>     public Long id;
>     /** Organization ID (indexed). */
>     @QuerySqlField(index = true, orderedGroups = @QuerySqlField.Group(name = 
> ORG_SALARY_IDX, order = 0))
>     public Long orgId;
>     /** First name (not-indexed). */
>     @QuerySqlField
>     public String firstName;
>     /** Last name (not indexed). */
>     @QuerySqlField
>     public String lastName;
>     @QuerySqlField
>     public LocalDateTime createdTime;
>     /** Resume text (create LUCENE-based TEXT index for this field). */
>     @QueryTextField
>     public String resume;
>     /** Salary (indexed). */
>     @QuerySqlField(index = true, orderedGroups = @QuerySqlField.Group(name = 
> ORG_SALARY_IDX, order = 1))
>     public double salary;
>     /** Custom cache key to guarantee that person is always colocated with 
> its organization. */
>     private transient AffinityKey<Long> key;
>     /**
>      * Default constructor.
>      */
>     public Person() {
>         // No-op.
>     }
>     /**
>      * Constructs person record.
>      *
>      * @param org       Organization.
>      * @param firstName First name.
>      * @param lastName  Last name.
>      * @param salary    Salary.
>      * @param resume    Resume text.
>      */
>     public Person(Organization org, String firstName, String lastName, double 
> salary, String resume) {
>         // Generate unique ID for this person.
>         id = ID_GEN.incrementAndGet();
>         orgId = org.id();
>         this.firstName = firstName;
>         this.lastName = lastName;
>         this.createdTime = LocalDateTime.now();
>         this.salary = salary;
>         this.resume = resume;
>     }
>     /**
>      * Constructs person record.
>      *
>      * @param id Person ID.
>      * @param orgId Organization ID.
>      * @param firstName First name.
>      * @param lastName Last name.
>      * @param salary    Salary.
>      * @param resume    Resume text.
>      */
>     public Person(Long id, Long orgId, String firstName, String lastName, 
> double salary, String resume) {
>         this.id = id;
>         this.orgId = orgId;
>         this.firstName = firstName;
>         this.lastName = lastName;
>         this.createdTime = LocalDateTime.now();
>         this.salary = salary;
>         this.resume = resume;
>     }
>     /**
>      * Constructs person record.
>      *
>      * @param id Person ID.
>      * @param firstName First name.
>      * @param lastName Last name.
>      */
>     public Person(Long id, String firstName, String lastName) {
>         this.id = id;
>         this.firstName = firstName;
>         this.lastName = lastName;
>         this.createdTime = LocalDateTime.now();
>     }
>     /**
>      * Gets cache affinity key. Since in some examples person needs to be 
> collocated with organization, we create
>      * custom affinity key to guarantee this collocation.
>      *
>      * @return Custom affinity key to guarantee that person is always 
> collocated with organization.
>      */
>     public AffinityKey<Long> key() {
>         if (key == null)
>             key = new AffinityKey<>(id, orgId);
>         return key;
>     }
>     /**
>      * {@inheritDoc}
>      */
>     @Override public String toString() {
>         return "Person [id=" + id +
>                 ", orgId=" + orgId +
>                 ", lastName=" + lastName +
>                 ", firstName=" + firstName +
>                 ", createdTime=" + createdTime +
>                 ", salary=" + salary +
>                 ", resume=" + resume + ']';
>     }
> } {code}
> DbH2ServerStartup.java:
>  
>  
> {code:java}
> package org.apache.ignite.examples.util;
> import java.io.IOException;
> import java.io.StringReader;
> import java.sql.SQLException;
> import org.apache.ignite.IgniteException;
> import org.h2.jdbcx.JdbcConnectionPool;
> import org.h2.tools.RunScript;
> import org.h2.tools.Server;
> /**
>  * Start H2 database TCP server in order to access sample in-memory database 
> from other processes.
>  */
> public class DbH2ServerStartup {
>     /** Create table script. */
>     private static final String CREATE_PERSON_TABLE =
>         "create table if not exists PERSON(id bigint AUTO_INCREMENT PRIMARY 
> KEY, first_name varchar(50), last_name varchar(50), created_time TIMESTAMP 
> DEFAULT CURRENT_TIMESTAMP);";
>     /** Sample data script. */
>     private static final String POPULATE_PERSON_TABLE =
>         "delete from PERSON;\n" +
>         "insert into PERSON(id, first_name, last_name) values(1, 'Johannes', 
> 'Kepler');\n" +
>         "insert into PERSON(id, first_name, last_name) values(2, 'Galileo', 
> 'Galilei');\n" +
>         "insert into PERSON(id, first_name, last_name) values(3, 'Henry', 
> 'More');\n" +
>         "insert into PERSON(id, first_name, last_name) values(4, 'Polish', 
> 'Brethren');\n" +
>         "insert into PERSON(id, first_name, last_name) values(5, 'Robert', 
> 'Boyle');\n" +
>         "insert into PERSON(id, first_name, last_name) values(6, 'Wilhelm', 
> 'Leibniz');";
>     /**
>      * 初始化数据库
>      *
>      * @throws SQLException if
>      */
>     public static void populateDatabase() throws SQLException {
>         // Try to connect to database TCP server.
>         JdbcConnectionPool dataSrc = 
> JdbcConnectionPool.create("jdbc:h2:tcp://localhost/mem:ExampleDb", "sa", "");
>         // Create Person table in database.
>         RunScript.execute(dataSrc.getConnection(), new 
> StringReader(CREATE_PERSON_TABLE));
>         // Populates Person table with sample data in database.
>         RunScript.execute(dataSrc.getConnection(), new 
> StringReader(POPULATE_PERSON_TABLE));
>     }
>     /**
>      * Start H2 database TCP server.
>      *
>      * @param args Command line arguments, none required.
>      * @throws IgniteException If start H2 database TCP server failed.
>      */
>     public static void main(String[] args) throws IgniteException {
>         try {
>             // Start H2 database TCP server in order to access sample 
> in-memory database from other processes.
>             Server.createTcpServer("-tcpDaemon").start();
>             Server.createWebServer("-webDaemon").start();
>             populateDatabase();
>         }
>         catch (SQLException e) {
>             throw new IgniteException("Failed to start database TCP server", 
> e);
>         }
>         try {
>             do {
>                 System.out.println("Type 'q' and press 'Enter' to stop H2 TCP 
> server...");
>             }
>             while ('q' != System.in.read());
>         }
>         catch (IOException ignored) {
>             // No-op.
>         }
>     }
> }
>  {code}
>  
>  
>  



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to