I have the following schema, using Objectify
#Entity
public class Book{
#Id
private long id;
private Key<Page> pages;
…
}
#Entity
public class Page{
#Id
private String id;
…
public Page(Book book,int pageNumber){
this.id = book.getId()+””+pageNumber;
…
}
}
I am getting the complaint
com.googlecode.objectify.LoadException: Error loading
Book(1401058017250)/Page("14010580172505639445604728832"): Loaded
Entity has parent but com.company.api.db.Page has no #Parent
Caused by: java.lang.IllegalStateException: Loaded Entity has parent
but com.company.api.db.Page has no #Parent
I am coming from Eclipse and Datanucleus and in that framework the code was
#Entity
public class Book{
#Id
private long id;
private List<Page> pages;
…
}
#Entity
public class Page{
#Id
private Key key;
…
public Page(Book book,int pageNumber){
this.key = createKey(book.getId(),pageNumber);
…
}
private Key createKey(long bookId, long pageNumber) {
Key bookKey = KeyFactory.createKey(Book.class.getSimpleName(), bookId);
Key key = KeyFactory.createKey(bookKey, Page.class.getSimpleName(), bookId + "" + pageNumber);
return key;
}
}
Whether it was the best code or not, it used to work. So anyway, I am migrating to Objectify and Android Studio. How do I rewrite this schema so I stop getting the error? If someone does know the answer, please just copy and paste my code snippet and make the appropriate edits.
In your old code, you're marking bookKey as the parent entity. In new code, you need to have:
#Parent
Key<Book> parentBook;
Your new classes should be:
#Entity
public class Book{
#Id
private long id;
private List<Key<Page>> pages;
…
}
#Entity
public class Page{
#Id
private String id;
#Parent
Key<Book> parentBook;
…
public Page(long bookId, int pageNumber){
this.id = bookId + ”” +pageNumber;
this.parentBook = Key.create(Book.class, bookId);
…
}
}
Read more about Keys and objectify here: https://code.google.com/p/objectify-appengine/wiki/Concepts#Keys
Related
So, I'm trying to persist an entity in the database that has a composite key, declared using the #IdClass annotation, which one of the ID keys I have turned into an object so ensure some validation of the data.
Before, when this ID was just a String, it was working without any problems, but now that I have changed it's type, it seens that Hibernate can't determine it's type in the database.
I found a question with a problem that was almost exactly the same as the mine, here. After I added the #Column annotation to the fields in the IdClass, I feel that the Hibernate could determine the type of the field in the database, but now it fails to perform the conversion.
I already have the converter class with the #Converter annotation and implementing the AttributeConverter interface, but I think that it isn't being reached by the Spring/Hibernate.
The involved classes bellow:
The converter
#Converter
public class ChapterNumberConverter implements AttributeConverter<ChapterNumber, String> {
#Override
public String convertToDatabaseColumn(ChapterNumber attribute) {
String value = attribute.getValue();
return value;
}
#Override
public ChapterNumber convertToEntityAttribute(String dbData) {
ChapterNumber chapterNumber = new ChapterNumber(dbData);
return chapterNumber;
}
}
The composite ID class
public class ChapterID implements Serializable {
private static final long serialVersionUID = 4324952545057872260L;
#Column
private Long id;
#Column
#Convert(converter = ChapterNumberConverter.class)
private String number;
#Column
private Long publisher;
#Column
private Long manga;
public ChapterID() {
}
public ChapterID(Long id, String number, Long publisher, Long manga) {
this.id = id;
this.number = number;
this.publisher = publisher;
this.manga = manga;
}
// ... getters and setters
}
The entity class
#Entity
#Table(name = "chapter", uniqueConstraints = #UniqueConstraint(columnNames = {"number", "publisher_id", "manga_id"}))
#IdClass(ChapterID.class)
public class Chapter {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
#Id
#Convert(converter = ChapterNumberConverter.class)
private ChapterNumber number;
#Id
#ManyToOne
#JoinColumn(name = "publisher_id")
private Publisher publisher;
#Id
#ManyToOne
#JoinColumn(name = "manga_id")
private Manga manga;
#Column(nullable = false)
#Convert(converter = ChapterLanguageEnumConverter.class)
private ChapterLanguage language;
public Chapter() {
}
public Chapter(ChapterNumber chapterNumber, Publisher publisher, Manga manga, ChapterLanguage language) {
this.number = chapterNumber;
this.publisher = publisher;
this.manga = manga;
this.language = language;
}
public Chapter(String chapterNumber, Publisher publisher, Manga manga, ChapterLanguage language) {
this(new ChapterNumber(chapterNumber), publisher, manga, language);
}
// ... getters and setters
}
I just want to validate the number field in the entity class, so, if there is another way to do this without using a custom type, otherwise, if anyone knows what I can do to teach correctly the Hibernate how to persist this field, tell me please 😢
I'm currently developing my project under Spring with JPA.
First off, here is my database schema for the background information
So, I'm undergoing difficulties when I try to use history_id of HISTORY as the primary key of TAG. It gives me ...Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: This class [class com.wrapsody.demo.HistoryTag] does not define an IdClass error.
So I added #IdClass(HistoryTag.HistoryTagAssignId.class) in my HistoryTag.java
#NoArgsConstructor(access = AccessLevel.PROTECTED) #Data #Entity
#IdClass(HistoryTag.HistoryTagAssignId.class)
public class HistoryTag implements Serializable {
#Id
#ManyToOne
private History history;
#Column
private String tagName;
#Builder
public HistoryTag(String tagName) {
this.tagName = tagName;
}
#NoArgsConstructor
public static class HistoryTagAssignId implements Serializable {
private History history;
public HistoryTagAssignId(History history) {
this.history = history;
}
}
}
For the reference, this is History.java
#NoArgsConstructor(access = AccessLevel.PROTECTED)
#Data
#Entity
public class History {
#Id
#GeneratedValue
private Long historyId;
#Column
private String historyMaterName;
#Column
private String historyFreeSetName;
History(String historyMaterName, String historyFreeSetName) {
this.historyMaterName = historyMaterName;
this.historyFreeSetName = historyFreeSetName;
}
}
Any guidance towards solving this error msg is appreciated.
Thanks!
For tag table you dont need entity class. Its duable in History entity with:
#ElementCollection
#CollectionTable(
name="TAG",
joinColumns=#JoinColumn(name="HISTORY_ID")
)
#Column(name="TAG_NAME")
List<String> tags;
https://en.wikibooks.org/wiki/Java_Persistence/ElementCollection#Basic_Collections
I am using Spring-Boot with JPA and a MySQL backend. Now I got quite confused about the repositories Spring-Boot provides. I know these are quite powerful (and seem to be quite useful since they can shorten your code a lot). Still, I do not understand how to represent Joins within them, since the result-set should be a combination of specified attributes in the select of a few Entities.
Now let's assume we have three tables Book, Author, AuthorOfBook, where the last one is simply connecting Book and Author by a combined Primary key. I guess we had the following Java-Classes:
Entity Book:
#Entity
#Table(name="BOOK")
public class Book {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int id;
#Column(name = "TITLE")
private String title;
}
Entity Author
#Entity
#Table(name="AUTHOR")
public class Author {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int id;
#Column(name = "LASTNAME")
private String lastname;
#Column(name = "FIRSTNAME")
private String firstname;
//Let's assume some getters and setters and a constructor
}
Entity AuthorOfBook:
#Entity
#Table(name="BOOK")
public class Book {
#EmbeddedId
private AuthorOfBookId pk;
}
An Embedded ID
#Embeddable
public class AuthorOfBookId implements Serializable {
private int authorId;
private int bookId;
}
Repository
#Repository
public interface AuthorOfBookRepository extends JpaRepository<,AuthorOfBookId> {
}
Now how would I represent that query:
SELECT b.name, a.firstname, a.lastname from AuthorOfBook ab inner join Book b on b.id = ab.book_id inner join Author a on a.id = ab.author_id where a.lastname = :lastname;
in my repository? I know the signature would need to be like
#Query([the query string from above])
public (...) findAuthorAndBookByAuthorLastname(#Param("lastname") String lastname);
but I cannot make out what Type the return would be like. What is that method returning? (simply AuthorOfBook would not work I guess)
You don't want AuthorOfBook as a separate Entity. Book should have a field of type Author as a #ManyToOne relationship. That way, given any Book, you can find the author's details.
If you want to handle audits fields you can do something like this:
Audit class
#Embeddable
public class Audit {
#Column(name = "created_on")
private Timestamp createdOn;
#Column(name = "updated_on")
private Timestamp updatedOn;
#Column(name = "is_deleted")
private Boolean isDeleted;
//getters and setters
}
AuditListener to update automatically audits fields
public class AuditListener {
private Long loggedUser = 1001L;
/**
* Method to set the fields createdOn, and isDeleted when an entity is persisted
* #param auditable
*/
#PrePersist
public void setCreatedOn(Auditable auditable) {
Audit audit = auditable.getAudit();
if (audit == null) {
audit = new Audit();
auditable.setAudit(audit);
}
audit.setIsDeleted(Boolean.FALSE);
audit.setCreatedOn(Timestamp.from(Instant.now()));
}
/**
* Method to set the fields updatedOn and updatedBy when an entity is updated
* #param auditable
*/
#PreUpdate
public void setUpdatedOn(Auditable auditable) {
Audit audit = auditable.getAudit();
audit.setUpdatedOn(Timestamp.from(Instant.now()));
}
}
And add this to the entities
#EntityListeners(AuditListener.class)
public class Book implements Auditable {
#Embedded
private Audit audit;
I am new to hibernate and having a tough time trying to wrap my head around setting up Joined inheritance with composite Primary Key. With my current setup, I get a:
JDBCException: could not insert: LandHolidayPackage
I am essentially looking for two things:
Are the inheritance annotations in place ?
Is the composite PK setup properly ?
DB Design:
Reference
Here are my classes and the annotations involved:
#Entity
#Table(name = "HOLIDAYPACKAGE")
public final class HolidayPackage {
private Integer idPackage;
private String name;
private Set<HolidayPackageVariant> holidayPackageVariants = new HashSet<HolidayPackageVariant>(0);
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "IDHOLIDAYPACKAGE", nullable = false)
public Integer getIdPackage() {
return idPackage;
}
#OneToMany(fetch=FetchType.LAZY, cascade={CascadeType.ALL}, mappedBy = "holidayPackage")
public Set<HolidayPackageVariant> getHolidayPackageVariants() {
return holidayPackageVariants;
}
// ommitted other part of the code
}
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
#Table(name="HOLIDAYPACKAGEVARIANT")
public abstract class HolidayPackageVariant {
private Integer idHolidayPackageVariant;
private HolidayPackage holidayPackage;
private String typeHolidayPackage;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="IDHOLIDAYPACKAGEVARIANT", nullable=false)
public Integer getIdHolidayPackageVariant() {
return idHolidayPackageVariant;
}
#ManyToOne(fetch=FetchType.LAZY, cascade={CascadeType.ALL})
#JoinColumn(name="IDHOLIDAYPACKAGE", nullable=false)
public HolidayPackage getHolidayPackage() {
return holidayPackage;
}
#Column(name="TYPEHOLIDAYPACKAGE", nullable=true)
public String getTypeHolidayPackage() {
return typeHolidayPackage;
}
// ommitted setters, equals hashCode
}
#Entity
#Table(name="LANDHOLIDAYPACKAGEVARIANT")
public final class LandHolidayPackageVariant extends HolidayPackageVariant{
private static final String LAND = "LAND";
protected LandHolidayPackageVariant() {}
public LandHolidayPackageVariant(HolidayPackage holidayPackage) {
super(holidayPackage, LAND);
}
}
#Entity
#Table(name="FLIGHTHOLIDAYPACKAGEVARIANT")
public final class FlightHolidayPackageVariant extends HolidayPackageVariant{
private static final String FLIGHT = "FLIGHT";
private Destination originCity;
protected FlightHolidayPackageVariant(){}
public FlightHolidayPackageVariant(HolidayPackage holidayPackage,
Destination originCity) {
super(holidayPackage, FLIGHT);
setOriginCity(originCity);
}
#ManyToOne(fetch=FetchType.LAZY, cascade={CascadeType.ALL})
#JoinColumn(name="IDDESTINATION", nullable=false)
public Destination getOriginCity() {
return originCity;
}
// ommited other setters etc functions
}
You annotated the properties in stead of the fields. JPA by default tries to access the fields. If you want JPA to use the fields you have to annotate the class with #AccessType(AccessType.Field).
I am trying to do mapping in JPA.
#Entity
public class Auction {
#Id
private Integer auctionId;
#OneToMany(mappedBy="auctionId")
#MapKey(name="auctionParamId")
private Map<AuctionParam, AuctionParamValue> values;
}
#Entity
public class AuctionParam {
#Id
private Integer auctionParamId;
private String description;
}
#Entity
public class AuctionParamValue {
#EmbeddedId
private AuctionParamValuePK pk;
private String value;
}
#Embeddable
public class AuctionParamValuePK {
#ManyToOne
#JoinColumn(name="auctionId")
private Auction auction;
#ManyToOne
#JoinColumn(name="auctionParamId")
private AuctionParam auctionParam;
}
Showing an error:-
.Error-Details:-Exception Description:
Entity [class
com.eaportal.domain.AuctionParamValue]
uses [class
com.eaportal.domain.AuctionParamValuePK]
as embedded id class
whose access-type
has been determined as [FIELD].
But
[class
com.eaportal.domain.AuctionParamValuePK]
does not define any [FIELD]. It is
likely that you have not provided
sufficient metadata in your id class
[class
com.eaportal.domain.AuctionParamValuePK].
If you come up with a solution please let me know.
Thanks in Advance
Tushar
You cannot use an EmbeddedId with relationships. Use an IdClass.
#Entity
#IdClass(AuctionParamValuePK.class)
public class AuctionParamValue {
#Id
#ManyToOne
#JoinColumn(name="auctionId")
private Auction auction;
#Id
#ManyToOne
#JoinColumn(name="auctionParamId")
private AuctionParam auctionParam;
#Basic
private String value;
}
public class AuctionParamValuePK {
private int auction;
private int auctionParam;
}
I think there are some errors in your Auction class. This is how I think it should look
#Entity
public class Auction {
#Id
private Integer auctionId;
#OneToMany(mappedBy="auction") // not auctionId
#MapKey(name="auctionParam") // not auctionParamId
private Map<AuctionParam, AuctionParamValue> values;
}
(The annotation values have to correspond with fields (or properties), not with columns)