I get an exception when I try to save entities when composite key is used. Here is the exception, models and repository I try to save to. Can anyone help?
org.springframework.beans.TypeMismatchException: Failed to convert
property value of type 'org.CheckType_$$_jvst96c_4' to required type
'int' for property 'checkType'; nested exception is
java.lang.IllegalArgumentException: Cannot convert value of type
'org.CheckType_$$_jvst96c_4' to required type 'int' for property
'checkType': PropertyEditor
[org.springframework.beans.propertyeditors.CustomNumberEditor]
returned inappropriate value of type 'org.CheckType_$$_jvst96c_4'
#Entity
public class CheckType {
#Id
private int id;
#NotNull
private String name;
#OneToMany(mappedBy = "checkType")
private List<ClientCheck> checks;
//getters and setters
}
#Entity
public class Offer {
#Id
#Column(name = "offerid")
private Integer offerId;
//...
#OneToMany(mappedBy = "offer")
private List<ClientCheck> clientCheckList;
//getters and setters
}
#Entity
#IdClass(ClientCheckId.class)
public class ClientCheck {
#Id
#ManyToOne
#JoinColumn(name = "offer_id", referencedColumnName = "offerid")
private Offer offer;
#Id
#ManyToOne
#JoinColumn(name = "check_type_id", referencedColumnName = "id")
private CheckType checkType;
#Id
private Date timestamp;
private String result;
//getters and setters
}
public class ClientCheckId implements Serializable {
private int offer;
private int checkType;
private Date timestamp;
//getters and setters
//equals and hashCode
}
public interface ClientCheckRepo extends JpaRepository<ClientCheck, ClientCheckId> {
}
If another entity is a part of composite key, you must declare that entity in your ID class, not its ID:
public class ClientCheckId implements Serializable {
private int offer;
private CheckType checkType;
private Date timestamp;
//getters and setters
//equals and hashCode
}
The error message is quite readable if you want how Hibernate works: org.CheckType_$$_jvst96c_4 is the enriched version of your class, which Hibernate tries to assign to checkType field of ClientCheckId via reflection, and fails because of type incompatibility.
Related
I am currently working on a project with following data structure:
public class PinaColadaId implements Serializable {
private UUID id;
#Temporal(TemporalType.TIMESTAMP)
private Date time;
// Constructor + Getters and Setters
}
#Entity
#Table(name = "pina_coladas")
#IdClass(PinaColadaId.class)
public class PinaColadaEntity {
#Column(columnDefinition = "char(255)")
#Type(type="org.hibernate.type.UUIDCharType")
private #Id UUID id;
private #Id #Temporal(TemporalType.TIMESTAMP) Date time;
#OneToOne(mappedBy = "entityID", cascade=CascadeType.ALL)
private PineappleWrapper pineapple;
#OneToMany(mappedBy = "entityID", cascade=CascadeType.ALL)
private List<CoconutWrapper> coconuts;
// Constructor + Getters and Setters
}
#Entity
public class PineappleWrapper {
private #Id #GeneratedValue long id;
private String manufacturer;
private String origin;
#OneToOne
private PinaColadaEntity entityID;
// Constructor + Getters and Setters
}
#Entity
public class CoconutWrapper {
private #Id #GeneratedValue long id;
private int shipmentNumber;
private int juice;
#ManyToOne
private PinaColadaEntity entityID;
// Constructor + Getters and Setters
}
The issue is that Spring Boot together with Hibernate and JPA correctly generate all the tables in my database, however when I attempt to store PineappleWrapper or CoconutWrapper, it stores all the values of PineappleWrapper and/or CoconutWrapper except for the id and time of the parent. The columns are generated yet they store the value "null".
Any and all help is much appreciated, -AwesomeDude091
Edit: I am aware of the JoinColumn annotation and it proposed implementation in my Wrapper classes, but I do not know how to use them with my two ID variables (id and time)
I have an Order entity and OrderProduct. I want to show order details on frontend and of course order products in it. So how to fetch product object in OrderProduct JSON. I'm missing product object in products array. I don't need order object one more time and i think it going to be a infinite recursion stuff with it. :)
My Order entity:
#Entity
#Getter
#Setter
#Table(name ="orders")
public class Order{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
private BigDecimal totalPrice;
#OneToMany(mappedBy = "order", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JsonManagedReference(value="orders")
private List<OrderProduct> products = new ArrayList<>();
private int userId;
#DateTimeFormat(pattern="dd/MM/yyyy")
private Date date = new Date();
#DateTimeFormat(pattern="dd/MM/yyyy")
private Date deliveryDate;
#Enumerated(EnumType.STRING)
private OrderType orderType;
}
My OrderProduct entity:
#Entity
#Setter
#Getter
public class OrderProduct {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.EAGER)
#JsonBackReference(value="product")
#JoinColumn(name = "product_id")
private Product product;
#ManyToOne
#JsonBackReference(value="orders")
#JoinColumn(name = "order_id")
private Order order;
private Integer quantity;
}
Product entity:
#Entity
#Getter
#Setter
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique = true)
private String name;
private double price;
#OneToMany(mappedBy = "product", cascade = CascadeType.ALL)
#JsonManagedReference(value="ingredients")
private List<Ingredient> ingredients = new ArrayList<>();
#OneToMany(mappedBy = "product",fetch = FetchType.EAGER)
#JsonManagedReference(value="product")
private List<OrderProduct> products = new ArrayList<>();
private String fileName;
}
This can help annotate one of your entity clases with
#JsonIdentityInfo(
property = "id",
generator = ObjectIdGenerators.PropertyGenerator.class
)
Every time when JSON serialization go in circles object data will be replaced with object id or orher field of entity for your choose.
You can use #JsonViewannotation to define the fields that you need to serialize to JSON
How it works:
You need define class with interfaces. For example:
public class SomeView {
public interface id {}
public interface CoreData extends id {}
public interface FullData extends CoreData {}
}
Mark entity fields with #JsonView(<some interface.class>)
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonView(SomeView.id.class)
private Long id;
#Column(nullable = false)
#JsonView(SomeView.CoreData.class)
private String username;
#Column(nullable = false)
#JsonView(SomeView.FullData.class)
private String email;
}
Annotate endpoint with #JsonView(<some interface.class>)
#GetMapping()
#JsonView(<some interface.class>)
public User getUser() {
return <get user entity somwhere>
}
In case #JsonView(SomeView.id.class) you will get this JSON:
{
id: <some id>
}
In case #JsonView(SomeView.CoreData.class):
{
id: <some id>,
username: <some username>
}
In case #JsonView(SomeView.FullData.class):
{
id: <some id>,
username: <some username>,
email: <some email>
}
#JsonView also works with embeded objects and you can annotate one field with multiply views classes - #JsonView({SomeView.FullData.class, SomeOtherView.OtherData.class})
In your case i think you should annotate all the fields you need except:
#OneToMany(mappedBy = "product",fetch = FetchType.EAGER)
#JsonManagedReference(value="product")
private List<OrderProduct> products = new ArrayList<>();
in Product
to avoid circular serialization
Or as alternative you can just use DTO classes or seralize oject to JSON manualy (https://thepracticaldeveloper.com/java-and-json-jackson-serialization-with-objectmapper/)
This can be done by my library beanknife
// This configure generate a class named ProductInfo which has the same shape with Product without property "products"
#ViewOf(value = Product.class, genName="ProductInfo", includePattern = ".*", excludes = {"products"})
class ProductInfoConfigure {}
// This configure generate a class named OrderProductRelation with the same shape of OrderProduct.
// But it has not order property and the type of its product property is change to ProductInfo generated above.
#ViewOf(value = OrderProduct.class, genName="OrderProductRelation", includePattern = ".*", excludes = {"order"})
class OrderProductRelationConfigure {
#OverrideViewProperty("product")
private ProductInfo product;
}
// This configure generate a class named OrderDetail with the same shape of Order.
// But the type of its products property is change to List<OrderProductRelation>
#ViewOf(value = Order.class, genName="OrderDetail", includePattern = ".*")
class OrderDetailConfigure {
#OverrideViewProperty("products")
private List<OrderProductRelation> products;
}
will generate these classes:
class ProductInfo {
private Long id;
private String name;
private double price;
private List<Ingredient> ingredients; // it is not processed because you have not provide the class Ingredient
private String fileName;
}
public class OrderProductRelation {
private Long id;
private ProductInfo product;
private Integer quantity;
}
public class OrderDetail {
public Long id;
private BigDecimal totalPrice;
private List<OrderProductRelation> products;
private int userId;
private Date date = new Date();
private Date deliveryDate;
private OrderType orderType;
}
Then
Order order = ...
OrderDetail orderDetail = OrderDetail.read(order);
// serialize the otherDetail instead of order.
List<Order> orders = ...
List<OrderDetail> orderDetails = OrderDetail.read(orders);
// serialize the orderDetails instead of orders.
Possible problems:
I doesn't use Lombok, so Lombok may need to be adapted because it change the byte code on the fly. But it is not a big problem, I will try to adapt it if someone commit the issue and provide enough use cases.
The generated class does not inherit the annotation on the original class. In next release I will provide a sulotion. At this moment, as a workaround, we can use custom method to convert the property manually. such as
#ViewOf(value = Order.class, genName="OrderDetail", includePattern = ".*")
class OrderDetailConfigure {
#OverrideViewProperty("products")
private List<OrderProductRelation> products;
#OverrideViewProperty("orderType")
public static String orderType(Order source) {
return source.getOrder().name();
}
}
The generated class will be changed to
public class OrderDetail {
public Long id;
private BigDecimal totalPrice;
private List<OrderProductRelation> products;
private int userId;
private Date date = new Date();
private Date deliveryDate;
private String orderType;
}
Update
Version 1.2.0 released. Add support of annotation inheritance.
#ViewOf(value = Order.class, genName="OrderDetail", includePattern = ".*")
#UseAnnotation({DateTimeFormat.class, Enumerated.class, JsonProperty.class})
class OrderDetailConfigure {
#OverrideViewProperty("products")
private List<OrderProductRelation> products;
}
generate
public class OrderDetail {
public Long id;
private BigDecimal totalPrice;
private List<OrderProductRelation> products;
private int userId;
#DateTimeFormat(pattern="dd/MM/yyyy")
private Date date;
#DateTimeFormat(pattern="dd/MM/yyyy")
private Date deliveryDate;
#Enumerated(EnumType.STRING)
private OrderType orderType;
}
Lets assume we have a complex JPA relation, a fraction of which looks like this:
#MappedSuperclass
public class DiffEntity {
private String diffId;
public DiffEntity() {
this.diffId = UUID.randomUUID().toString();
}
//...
}
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class ParentEntity extends DiffEntity {
#Id
#GeneratedValue
private long id;
#Column
private String name;
//...
}
#Entity
public class Construct extends ParentEntity {
#Column
private String variable;
#OneToMany(mappedBy = "construct", cascade = CascadeType.ALL)
private List<Partconstruct> partconstructs;
//...
}
#Entity
public class Partconstruct extends ParentEntity {
#OneToMany(mappedBy = "partconstruct", cascade = CascadeType.ALL)
private List<Field> fields;
#OneToMany(mappedBy = "partconstruct", cascade = CascadeType.ALL)
private List<Hardparameter> hardparameters;
#ManyToOne
#JoinColumn(name = "construct_id")
private Construct construct;
//...
}
#Entity
public class Field extends ParentEntity {
#Column
private int fieldSize;
#ManyToOne
#JoinColumn(name = "partconstruct_id")
private Partconstruct partconstruct;
//...
}
#Entity
public class Hardparameter extends ParentEntity {
#Column
private String value;
#ManyToOne
#JoinColumn(name = "partConstruct_Id")
private Partconstruct partConstruct;
//...
}
We are concerned with Construct type objects. Construct is deeply cloned and persisted, having all its nested objects on the object graph being cloned too and getting a new Id (primary key). On every clone the diffId (from DiffEntity entity) stays the same (it serves the purpose of correlating objects for a diffing feature).
How would it be possible to search and get a reference for a specific DiffEntity given we have the below:
a reference to the Construnct instance
type of the nested object
diffId we are after.
I have tried different versions of object graph traversers with reflection, which will work for a small in size Construct object, but once it becomes too big performance is very slow.
Is there any magic on the entity manager itself to achieve that ?
I am trying to set up our database in our project. But I get some errors, when I try to use #ManyToOne and#OneToMany on a #MappedSuperclass Entity:
#MappedSuperclass
public abstract class Person extends Model{
//public abstract class Person {
// ATTRIBUTES
#Column(columnDefinition = "varchar(20) not null")
private String firstName;
#Column(columnDefinition = "varchar(20) not null")
private String lastName;
#Column(columnDefinition = "varchar(20) not null")
private String password;
#Column(columnDefinition = "varchar(50) not null")
private String eMail;
#Column(columnDefinition = "varchar(8) not null")
private String svn;
private static int staticId = 0; // Identifier, staticId is unique.
#Id
#Column(columnDefinition = "integer not null")
private int id;
#Column(columnDefinition = "integer")
private int age;
#Column(columnDefinition = "varchar(10)")
private String telephoneNumber;
#Column(columnDefinition = "decimal(10,2)")
private double salary;
#Column(columnDefinition = "boolean")
private boolean allowedOvertime;
//#OneToMany(mappedBy = "person")
private List<TimeEntryMonth> listTimeEntryMonth;
#OneToMany(mappedBy = "person")
private List<Vacation> listVacation;
#OneToMany(mappedBy = "person")
private List<SickLeave> listSickLeave;
One of the classes who extends from Person:
#Entity
public class Employee extends Person {
// ATTRIBUTES
private String position;
private Boss boss;
And one of the #OneToMany relations:
public class SickLeave extends Model {
/* ATTRIBUTES */
#Id
private int id;
#ManyToOne
private Person person;
private int personIdSL;
private String reason;
If I compile my whole Project without the #ManyToOne and #OneToMany, it will work fine. But with the it will lead into som errors:
Error injecting constructor, java.lang.RuntimeException: Error reading
annotations for models.SickLeave
I tried to delete the abstract and replaced #MappedSuperclass with #Entity and the project works. So I think that I cant have #OneToMany and #ManyToOne relations on a #MappedSuperclass, But I dont want to refactor my whole project
Is there any (easier) way to handle such issues?
Thank you.
Person is not an Entity, so you can't use OneToMany to it. You will need to make Person an Entity or point to Employee, which seems to make more sense to me.
I have such a simple scheme
and the following entities:
#Entity
public class Ticket {
#Id
#GeneratedValue
private Integer id;
#ManyToOne
private Event event;
#OneToOne
private User user;
#Embedded
private Seat seat;
private TicketState state;
private Float price;
// getters, setters, etc.
#Entity
public class Event {
#Id
#GeneratedValue
private Integer id;
#OneToOne
private Movie movie;
#Embedded
private Auditorium auditorium;
private LocalDateTime startDateTime;
#OneToMany
private Set<Ticket> tickets = new HashSet<>();
// getters, setters, etc.
#Entity
public class User {
#Id
#GeneratedValue
private Integer id;
#Enumerated(EnumType.STRING)
private UserRole role;
private String name;
private String email;
private Instant birthday;
#OneToMany
private List<Ticket> tickets = new ArrayList<>();
private boolean lucky;
// getters, setters, etc.
#Embeddable
public class Auditorium {
private Integer id;
private String name;
private Integer seatsNumber;
#ElementCollection
private List<Integer> vipSeats;
// getters, setters, etc.
Also these entities was added to hibernate.cfg.xml.
Than I run app I have the following exception:
Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: com.epam.spring.core.domain.Event column: id (should be mapped with insert="false" update="false")
At first glance I don't see any duplications in Event, as mentioned in exception. What should I fix in entities mapping description to resolve the problem according my scheme? Thank you!
Both Event and Auditorium map to column named id.
Specify a different column name in Auditorium or use #AttributeOverride in Event to override the default name.
When you map an entity with annotations, you do not need to repeat yourself on hibernate.cfg.xml. Try to delete it e run your code again.
Updating my answer based on Dragan Bozanovic's, Auditorium should NOT have an #Id annotated field (but we can't see that from your code, if it has).