Доступ к существующей базе данных postgres с помощью 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 для поддержки поля bytea, такого как avatar bytea NOT NULL
?
Схема базы данных 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 ответ
Я смог решить мою проблему, изменив тип атрибута cdate и mdate с String
в Date
, Кроме того, мне пришлось удалить defaultValue
а также canBeNull
параметры из аннотации @DatabaseField. Что оставляет меня в следующем классе:
@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();