bin.yin created IGNITE-20024:
--------------------------------

             Summary: 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"
                 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


h3. Question:

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