使用ormlite访问现有的postgres数据库

时间:2014-03-12 07:57:09

标签: java postgresql ormlite

我刚开始编写一个应该使用ormlite来访问我已创建的postgreSQL数据库的应用程序。它使用下面的数据库方案和域对象类。但是,我无法创建一个运行下面的测试方法的新用户。使用此类访问数据库可以正常工作。我得到的异常只是告诉我postgre无法插入:

java.sql.SQLException: Unable to run insert stmt on object net.avedo.spozz.models.User@78412176: INSERT INTO "users" ("id" ,"cdate" ,"mdate" ,"name" ,"email" ,"password" ,"avatar_id" ) VALUES (?,?,?,?,?,?,?)
    at com.j256.ormlite.misc.SqlExceptionUtil.create(SqlExceptionUtil.java:22)
    at com.j256.ormlite.stmt.mapped.MappedCreate.insert(MappedCreate.java:135)
    at com.j256.ormlite.stmt.StatementExecutor.create(StatementExecutor.java:450)
    at com.j256.ormlite.dao.BaseDaoImpl.create(BaseDaoImpl.java:310)
    at net.avedo.spozz.models.UserTest.testUserCreation(UserTest.java:178)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:264)
    at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:153)
    at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:124)
    at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:200)
    at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:153)
    at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:103)
Caused by: org.postgresql.util.PSQLException: ERROR: column "cdate" is of type timestamp without time zone but expression is of type character varying
  Hint: You will need to rewrite or cast the expression.
  Position: 100
    at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2103)
    at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1836)
    at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:257)
    at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:512)
    at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:388)
    at org.postgresql.jdbc2.AbstractJdbc2Statement.executeUpdate(AbstractJdbc2Statement.java:334)
    at com.j256.ormlite.jdbc.JdbcDatabaseConnection.insert(JdbcDatabaseConnection.java:170)
    at com.j256.ormlite.stmt.mapped.MappedCreate.insert(MappedCreate.java:91)
    ... 28 more

那么我想念什么?我如何扩展Avatar类以支持像avatar bytea NOT NULL这样的bytea字段?

postgreSQL数据库方案

CREATE TABLE avatars (
   id BIGSERIAL PRIMARY KEY,
   cdate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
   mdate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE users (
   id BIGSERIAL PRIMARY KEY,
   cdate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
   mdate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
   name VARCHAR(160) UNIQUE NOT NULL,
   email VARCHAR (355) UNIQUE NOT NULL,
   password VARCHAR(30) NOT NULL,
   avatar_id BIGINT,
   CONSTRAINT user_avatar_id FOREIGN KEY (avatar_id)
      REFERENCES avatars (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
);

一个简单的测试用例

@Test
public void testUserCreation() throws Exception {
    try {
        // Setup the user database object, ...
        Dao<User, Integer> userDao = getUserDao();

        // ... create a new user ...
        User user = new User();
        user.setName("Andi");
        user.setEmail("info@avedo.net");
        user.setPassword("geheim");
        userDao.create(user);

        // ... and finally query all users.
        List<User> userList = userDao.query(
                    userDao.queryBuilder().where()
             .eq("name", "Andi")
             .prepare());

        Assert.assertTrue("User creation failed.", userList.get(0).getName().equals("Andi"));
        Assert.assertTrue("User creation failed." + userList.get(0).getName(), userList.get(0).getName().equals("Andi"));
    } catch (SQLException e) {
        throw new Exception("Failed to create user: " + e.getMessage());
    }
}

Avatar.java

@DatabaseTable(tableName = "avatars")
public class Avatar {
    @DatabaseField(generatedIdSequence = "avatars_id_seq", useGetSet = true)
    private long id;

    @DatabaseField(canBeNull = false, defaultValue="CURRENT_TIMESTAMP", useGetSet = true)
    private String cdate;

    @DatabaseField(canBeNull = false, defaultValue="CURRENT_TIMESTAMP", useGetSet = true)
    private String mdate;

    public Avatar() {
        // ORMLite needs a no-arg constructor 
    }

    // Getter and setter methods.
}

User.java

@DatabaseTable(tableName = "users")
public class User {
    @DatabaseField(generatedIdSequence = "users_id_seq", useGetSet = true)
    private long id;

    @DatabaseField(canBeNull = false, defaultValue="CURRENT_TIMESTAMP", useGetSet = true)
    private String cdate;

    @DatabaseField(canBeNull = false, defaultValue="CURRENT_TIMESTAMP", useGetSet = true)
    private String mdate;

    @DatabaseField(canBeNull = false, useGetSet = true)
    private String name;

    @DatabaseField(canBeNull = false, useGetSet = true)
    private String email;

    @DatabaseField(canBeNull = false, useGetSet = true)
    private String password;

    @DatabaseField(columnName = "avatar_id", foreign = true, useGetSet = true)
    private Avatar avatar;

    public User() {
        // ORMLite needs a no-arg constructor 
    }

    // Getter and setter methods.
}

1 个答案:

答案 0 :(得分:1)

我能够通过将cdate和mdate的属性类型从String更改为Date来解决我的问题。此外,我不得不从@DatabaseField注释中删除defaultValuecanBeNull参数。这让我接下来的课程:

@DatabaseTable(tableName = "users")
public class User {
    @DatabaseField(generatedIdSequence = "users_id_seq", useGetSet = true)
    private long id;

    @DatabaseField(useGetSet = true)
    private Date cdate;

    @DatabaseField(useGetSet = true)
    private Date mdate;

    @DatabaseField(canBeNull = false, useGetSet = true)
    private String name;

    @DatabaseField(canBeNull = false, useGetSet = true)
    private String email;

    @DatabaseField(canBeNull = false, useGetSet = true)
    private String password;

    @DatabaseField(columnName = "avatar_id", foreign = true, useGetSet = true)
    private Avatar avatar;

    public User() {
        // ORMLite needs a no-arg constructor 
    }

    // Getter and setter methods.
}

最后,我必须相应地调整数据库方案:

CREATE TABLE avatars (
   id BIGSERIAL PRIMARY KEY,
   cdate TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   mdate TIMESTAMP DEFAULT CURRENT_TIMESTAMP
--   avatar bytea NOT NULL
);

CREATE TABLE users (
   id BIGSERIAL PRIMARY KEY,
   cdate TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   mdate TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
   name VARCHAR(160) NOT NULL,
   email VARCHAR (355) UNIQUE NOT NULL,
   password VARCHAR(30) NOT NULL,
   avatar_id BIGINT,
   CONSTRAINT user_avatar_id FOREIGN KEY (avatar_id)
      REFERENCES avatars (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
);

为了避免NULL值,我将cdate和mdate的默认值设置为CURRENT_TIMESTAMP并添加了一个触发器,如果​​相应的行发生变化,它将自动更新mdate的值:

CREATE OR REPLACE FUNCTION update_timestamp() RETURNS TRIGGER AS 
$update_timestamp$
    BEGIN
        NEW.mdate := CURRENT_TIMESTAMP;
        RETURN NEW;
    END;
$update_timestamp$ 
LANGUAGE plpgsql;

CREATE TRIGGER update_timestamp BEFORE INSERT OR UPDATE ON avatars
    FOR EACH ROW EXECUTE PROCEDURE update_timestamp();

CREATE TRIGGER update_timestamp BEFORE INSERT OR UPDATE ON users
    FOR EACH ROW EXECUTE PROCEDURE update_timestamp();