2016-09-25 100 views
1

我在將簡單數據結構存入數據庫時​​遇到了問題。Hibernate,Spring,PostgreSQL - 列索引超出範圍

每條消息可能有多個消息接收者。我需要的一切就是保存在數據庫Message和MessageReceivers(MR)中。 MR具有名爲fk_message_id的列,該列應該自動填充message_id(M)。

在數據庫(PostgreSQL的)表與SQL代碼創建:

CREATE TABLE public.message 
(
message_id integer NOT NULL DEFAULT nextval('message_message_id_seq'::regclass), 
fk_author_id integer NOT NULL, 
topic text NOT NULL, 
text text NOT NULL, 
audit_cd timestamp without time zone NOT NULL DEFAULT now(), 
audit_md timestamp without time zone, 
CONSTRAINT message_pkey PRIMARY KEY (message_id) 
) 

CREATE TABLE public.message_receiver 
(
fk_message_id integer NOT NULL, 
fk_user_id integer NOT NULL, 
is_read boolean NOT NULL, 
read_date timestamp without time zone, 
audit_cd timestamp without time zone NOT NULL DEFAULT now(), 
autid_md timestamp without time zone, 
CONSTRAINT message_receiver_pkey PRIMARY KEY (fk_message_id, fk_user_id), 
CONSTRAINT message_receiver_fk_message_id_fkey FOREIGN KEY (fk_message_id) 
    REFERENCES public.message (message_id) MATCH SIMPLE 
    ON UPDATE CASCADE ON DELETE CASCADE 
) 

Message.java

@Entity 
@Table(name="message") 
public class Message implements Serializable { 

private static final long serialVersionUID = 1L; 

@Id 
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator="message_message_id_seq") 
@SequenceGenerator(name="message_message_id_seq", sequenceName="message_message_id_seq", allocationSize=1) 
@Column(name="message_id") 
private Long id; 

@NotNull 
@Column(name="fk_author_id") 
private Long author; 

@OneToMany 
@Cascade(CascadeType.PERSIST) 
@JoinColumn(name="fk_message_id", nullable = false) 
private List<MessageReceiver> receivers; 

@NotNull 
@Column(name="topic") 
private String topic; 

@NotNull 
@Column(name="text") 
private String text; 

@NotNull 
@Column(name="audit_cd") 
@Convert(converter=PersistentLocalDateTime.class) 
private LocalDateTime sendDate; 

...getters, setters, constructors... 
} 

MessageReceiver.java

@Entity 
@Table(name="message_receiver") 
public class MessageReceiver implements Serializable { 

private static final long serialVersionUID = 2L; 

@Id 
@Column(name="fk_message_id") 
private Long messageId; 

@Id 
@Column(name="fk_user_id") 
private Long receiverId; 

@NotNull 
@Column(name="is_read") 
private Boolean isRead; 

@Column(name="read_date") 
@Convert(converter = PersistentLocalDateTime.class) 
private LocalDateTime readDate; 

...getters, setters, constructors... 
} 

當我嘗試保存消息與接收器獲取:

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not insert: [com.example.foldermessage.model.MessageReceiver]; SQL [insert into message_receiver (is_read, read_date, fk_message_id, fk_user_id) values (?, ?, ?, ?)]; nested exception is org.hibernate.exception.DataException: could not insert: [com.example.foldermessage.model.MessageReceiver]] with root cause 

org.postgresql.util.PSQLException: The column index is out of range: 5, number of columns: 4. 
at org.postgresql.core.v3.SimpleParameterList.bind(SimpleParameterList.java:68) ~[postgresql-9.4.1211.jar:9.4.1211] 
at org.postgresql.core.v3.SimpleParameterList.setNull(SimpleParameterList.java:157) ~[postgresql-9.4.1211.jar:9.4.1211] 
at org.postgresql.jdbc.PgPreparedStatement.setNull(PgPreparedStatement.java:287) ~[postgresql-9.4.1211.jar:9.4.1211] 
at org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:61) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final] 
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:257) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final] 
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:252) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final] 
at org.hibernate.type.ComponentType.nullSafeSet(ComponentType.java:343) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final] 
at org.hibernate.persister.entity.AbstractEntityPersister.dehydrateId(AbstractEntityPersister.java:2636) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final] 
at org.hibernate.persister.entity.AbstractEntityPersister.dehydrate(AbstractEntityPersister.java:2604) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final] 
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2883) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final] 
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3386) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final] 
at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:89) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final] 
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:560) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final] 
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:434) ~[hibernate-core-5.0.9.Final.jar:5.0.9.Final] 

我用JpaRepository執行保存操作:

@Repository 
public interface MessageRepository extends JpaRepository<Message, Long> {} 

插入查詢似乎確定,我看不到任何錯誤了。

我也嘗試將MR實體ID更改爲具有@IdClass批註和@PrimaryKeyJoinColumn的複合鍵。那些嘗試沒有幫助。

+0

您的表格只有4列。你確定你的數據庫模式與你的類結構相匹配嗎? (什麼時候是你最後一次執行創建你的數據庫的腳本?) –

+0

@MikeNakis在帖子開頭的sql查詢是直接從PgAdmin複製的。 類結構是否必須與數據庫表結構相同?在類中我沒有放置audit_md列。 – mateusz

+0

是的,類結構和數據庫模式必須匹配,否則預計hibernate會嘗試訪問數據庫中不存在的列。查看hibernate的'hbm2ddl'屬性,它允許你從你的類結構自動創建你的數據庫模式。如果你不想(或不能)這樣做,你也可以使用'hbm2ddl'來至少讓hibernate在程序啓動時驗證你的類結構是否與你的數據庫模式一致,以避免令人討厭的意外。 –

回答

0

我找到了臨時解決方案。

  1. 首先我啓用了DDL spring.jpa.hibernate.ddl-auto=create (or update)來自動生成模式。

  2. 在以前的解決方案message_receiver有主鍵fk_message_id, fk_user_id。這個組合鍵的主要目的是防止兩次或更多次向一個用戶發送相同的消息。

我決定把生成的值代替複合鍵。通過在PostgreSQL級別添加UNIQUE CONSTRAINT可以實現關於消息唯一性的約束。

MessageReceiver.java

@Entity 
@Table(name="message_receiver") 
public class MessageReceiver implements Serializable { 

private static final long serialVersionUID = 1L; 

@Id 
@Column(name="message_receiver_id") 
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator="message_receiver_id_seq") 
@SequenceGenerator(name="message_receiver_id_seq", sequenceName="message_receiver_id_seq", allocationSize=1) 
private Long messageReceiverId; 

@NotNull 
@Column(name="fk_message_id") 
private Long messageId; 

@NotNull 
@Column(name="fk_user_id") 
private Long receiverId; 

@NotNull 
@Column(name="is_read") 
private Boolean isRead; 

@Column(name="read_date") 
@Convert(converter = PersistentLocalDateTime.class) 
private LocalDateTime readDate; 

..getters, setters.. 

Message.java

@Entity 
@Table(name="message") 
public class Message implements Serializable { 

private static final long serialVersionUID = 1L; 

@Id 
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator="message_message_id_seq") 
@SequenceGenerator(name="message_message_id_seq", sequenceName="message_message_id_seq", allocationSize=1) 
@Column(name="message_id") 
private Long id; 

@NotNull 
@Column(name="fk_author_id") 
private Long author; 

@OneToMany(fetch=FetchType.LAZY, cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}) 
@JoinColumn(name="fk_message_id") 
private List<MessageReceiver> receivers = new ArrayList<>(); 

@NotNull 
@Column(name="topic") 
private String topic; 

@NotNull 
@Column(name="text") 
private String text; 

@NotNull 
@Column(name="audit_cd") 
@Convert(converter=PersistentLocalDateTime.class) 
private LocalDateTime sendDate; 

..getters, setters.. 

現在,我很好奇,爲什麼第一個解決方案不起作用,以及如何使其發揮作用。我嘗試了@PrimaryKeyJoinColumn,@IdClass,@OneToMany(mappedBy),@JoinColumn(可爲空,可插入,可更新)的組合。

當我啓用DDL(創建)休眠生成相同的表,因爲我把它放在開始。這意味着它不適用於不適當的表結構。

如果改變生成主鍵的策略有助於保存複合鍵導致錯誤。在日誌中,我經常看到Hibernate添加到實體*_IdBackref。也許它嘗試保存/映射此ID到數據庫中,但它的列未準備好。

我會發布最終的解決方案,如果我能找到它。