Я с трудом пытаюсь понять, как правильно выполнить ORM для определенной базы данных.
Моя схема состоит из трех таблиц: таблица пользователя, таблица обзора и таблица голосования. Пользователи могут публиковать обзоры для альбомов, а также могут назначать положительный или отрицательный рейтинг любому обзору. Альбомы предоставляются из внешнего API, поэтому их таблицы отсутствуют в схеме, но на их идентификаторы ссылаются.
Первичный ключ пользователя просто состоит из его имени пользователя. Первичный ключ рецензии состоит из имени пользователя рецензента, который является внешним ключом, и идентифицированного идентификатора альбома. Наконец, первичный ключ голосования состоит из имени пользователя избирателя, опять-таки внешнего ключа и первичного ключа проголосовавшего отзыва, состоящего, как было сказано ранее, из имени пользователя рецензента и идентифицированного идентификатора альбома.
Пользователь можетопубликуйте рецензию для каждого отдельного альбома, а также можете назначить голосование для каждого отдельного рецензирования.
Это модель ER, представляющая схему:
Комусопоставьте идентификаторы сущностей, я использую аннотацию @IdClass
, но я не уверен, что направляюсь в правильном направлении. Я также пытался использовать аннотацию @EmbeddedId
, но результат тот же.
Вот так классы моих сущностей выглядят так:
@Entity
public class User implements Serializable {
private static final long serialVersionUID = 1;
@Id @Column(name = "username")
private String username;
@Column(unique = true, nullable = false)
private String email;
@Column(name = "password", nullable = false)
private String password;
@Temporal(TemporalType.TIMESTAMP) @Column(name="signUpDate", nullable = false)
private Date signUpDate;
// empty constructor, getters, setters, equals and hashCode implementations
}
@Entity @IdClass(ReviewId.class)
public class Review implements Serializable {
private static final long serialVersionUID = 1;
@Id @ManyToOne @JoinColumn(name = "reviewerUsername", referencedColumnName = "username")
private User reviewer;
@Id @Column(name = "reviewedAlbumId")
private Long reviewedAlbumId;
@Column(name = "content", nullable = false, length = 2500)
private String content;
@Column(name = "rating", nullable = false)
private Integer rating;
@Temporal(TemporalType.TIMESTAMP) @Column(name = "publicationDate", nullable = false)
private Date publicationDate;
// empty constructor, getters, setters, equals and hashCode implementations
}
@Entity @IdClass(VoteId.class)
public class Vote implements Serializable {
private static final long serialVersionUID = 1;
@Id @ManyToOne @JoinColumn(name = "voterUsername", referencedColumnName = "username")
private User voter;
@Id @ManyToOne @JoinColumns({
@JoinColumn(name = "reviewerUsername", referencedColumnName = "reviewerUsername"),
@JoinColumn(name = "reviewedAlbumId", referencedColumnName = "reviewedAlbumId")
})
private Review review;
@Column(name = "vote") // @todo add attribute nullable = false
private Boolean vote;
// empty constructor, getters, setters, equals and hashCode implementations
}
Этимои классы идентификаторов:
public class ReviewId implements Serializable {
private static final long serialVersionUID = 1L;
private User reviewer;
private Long reviewedAlbumId;
// empty constructor, getters, setters, equals and hashCode implementations
}
public static class VoteId implements Serializable {
private static final long serialVersionUID = 1L;
private User voter;
private Review review;
// empty constructor, getters, setters, equals and hashCode implementations
}
А вот содержимое сценария MySQL, использованного для генерации схемы:
DROP SCHEMA IF EXISTS albumReviewsDatabase;
CREATE SCHEMA albumReviewsDatabase;
USE albumReviewsDatabase;
CREATE TABLE user (
username VARCHAR(20) PRIMARY KEY,
email VARCHAR(254) NOT NULL UNIQUE,
password CHAR(60) NOT NULL,
signUpDate TIMESTAMP NOT NULL DEFAULT now()
) ENGINE = INNODB;
CREATE TABLE review (
reviewerUsername VARCHAR(20) NOT NULL,
reviewedAlbumId BIGINT(20) NOT NULL,
content TEXT NOT NULL,
rating SMALLINT UNSIGNED NOT NULL,
publicationDate TIMESTAMP NOT NULL DEFAULT now(),
CHECK (rating >= 0 AND rating <= 10),
PRIMARY KEY (reviewerUsername, reviewedAlbumId),
FOREIGN KEY (reviewerUsername) REFERENCES user(username)
ON DELETE CASCADE
ON UPDATE CASCADE
) ENGINE = INNODB;
CREATE TABLE vote (
voterUsername VARCHAR(20) NOT NULL,
reviewerUsername VARCHAR(20) NOT NULL,
reviewedAlbumId BIGINT(20) NOT NULL,
vote BOOLEAN NOT NULL,
PRIMARY KEY (voterUsername, reviewerUsername, reviewedAlbumId),
FOREIGN KEY (voterUsername) REFERENCES user(username)
ON DELETE CASCADE
ON UPDATE CASCADE,
FOREIGN KEY (reviewerUsername, reviewedAlbumId) REFERENCES review(reviewerUsername, reviewedAlbumId)
ON DELETE CASCADE
ON UPDATE CASCADE
) ENGINE = INNODB;
В настоящее время я использую OpenJPA в качестве персистентностипровайдер в экземпляре веб-профиля TomEE, а используемая версия JPA - 2.0.
Очевидно, что я что-то неправильно понимаю в ORM JPA, потому что при развертывании приложения, содержащего эти объекты, я получаю следующее исключение:
<openjpa-2.4.2-r422266:1777108 fatal user error> org.apache.openjpa.util.MetaDataException: The id class specified by type "class application.model.Review" does not match the primary key fields of the class. Make sure your identity class has the same primary keys as your persistent type, including pk field types. Mismatched property: "reviewer"
Исключение выдается из-за класса Review
отображение, а не класс Vote
;однако я уверен, что, решив проблему с классом Review
, то же самое появится снова для Vote
.
. Я бы предпочел избежать использования аннотации @IdClass
вместо * 1042. *, но какой бы из двух я ни использовал, это не проблема.