[
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)