I have problem with persisting simple data structure into database.
Each message could has multiple message receivers. Everything I need is to save in database Message and MessageReceivers (MR). MR has column named fk_message_id which should be filled with message_id (M) automatically.
In database (PostgreSQL) tables are created with SQL code:
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...
}
When I try to save message with receivers I get:
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]
I use JpaRepository to perform saving operation:
@Repository
public interface MessageRepository extends JpaRepository<Message, Long> {}
Insert query seems ok and I can't see any error in it.
I also have tried change MR entity ids into composite key with @IdClass annotation and @PrimaryKeyJoinColumn. Those attempts didn't help.
hbm2ddlproperty of hibernate which allows you to have your database schema automagically created from your class structure. If you don't want to (or cannot) do this, you can also usehbm2ddlto at least have hibernate verify that your class structure agrees with your database schema during program startup, in order to avoid nasty surprises.insert into message_receiver (audit_cd, is_read, autid_md, read_date, fk_message_id, fk_user_id) values (?, ?, ?, ?, ?, ?)Error:org.postgresql.util.PSQLException: The column index is out of range: 7, number of columns: 6.audit_cd, is_read, autid_md, read_date, fk_message_id, fk_user_id. Make it contain those columns.