I am working on a Spring mvc app in which I am using Trip model and TripStop model. Trip model has list of trip stop models. Following is my Trip model:
#Entity
#Table(name = "Trip")
public class TripModel {
#Id
#Column(name = "tripid")
#GeneratedValue
private int tripId;
#Column(name = "tripname")
private String tripName;
#Column(name = "tripdesc")
private String tripDesc;
#Column(name = "createdate")
private Date createDate;
#OneToMany(mappedBy = "tripModel", fetch = FetchType.EAGER)
#Fetch(value = FetchMode.SUBSELECT)
private List<TripStopModel> tripStopList;
}
Following is my trip stop model:
#Entity
#Table(name="TripStop")
public class TripStopModel {
#Id
#Column(name="tripstopid")
#GeneratedValue
private int tripStopId;
#Column(name="datetime")
private String tripStopDateTime;
#Column(name="createts")
private Date tripStopCreateTime;
#ManyToOne(optional=true)
#JoinColumn(name="locationid")
private LocationModel locationModel;
public LocationModel getLocationModel() {
return locationModel;
}
public void setLocationModel(LocationModel locationModel) {
this.locationModel = locationModel;
}
#ManyToOne(optional=true)
#JoinColumn(name="userid")
private UserModel userModel;
#ManyToOne(optional=true)
#JoinColumn(name="tripid")
private TripModel tripModel;
}
This works fine. But when trip id is 0 in TripStop table, it shows following exception:
02:32:43,784 ERROR [stderr] (http--0.0.0.0-8080-5) org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.example.scm.model.TripModel#0]
Is there any option with which we can used trip id = 0 in TripStop table, without any exception? How can I allow this?
The tripID is defaulting to 0 because you are using primitives. Switch to primitive wrappers so these values can default to null and this should solve it
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 😢
There is a given database structure and graphql schema.
Fortunately they have a lot in common but unfortunately there are some difference.
Let's say there are entities in java to match the following database structure.
SQL:
TABLE ANIMAL
+ID NUMBER(19)
+NR_OF_LEGS NUMBER(19)
TABLE SHEEP
+ID NUMBER
+LAST_TIME_SHEARED DATETIME
+ANIMAL_ID NUMBER(19)
TABLE COW
+MILK_IN_L NUMBER(3)
+ANIMAL_ID NUMER(19)
Java:
#Entity
#Table(name = "ANIMAL")
public class Animal
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name="nrOfLegs", nullable=false)
private long nrOfLegs;
}
#Entity
#Table(name = "SHEEP")
public class SheepE
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name="lastTimeSheared", nullable=false)
private Datetime lastTimeSheared;
#ManyToOne(targetEntity = AnimalE.class, cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "animalId", nullable = false, insertable = false, updatable = false)
private Animal animal;
}
#Entity
#Table(name = "COW")
public class CowE
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name="milkInL", nullable=false)
private int milkInL;
#ManyToOne(targetEntity = AnimalE.class, cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "animalId", nullable = false, insertable = false, updatable = false)
private Animal animal;
}
The existing GraphQl schema is considered to be like this:
type Sheep{
id: int!
lastTimeSheard: String!
nrOfLegs: int!
}
type Cow {
id: int!
milkInL: int!
nrOfLegs: int
}
The project uses graphql-java in version 11.0 (guess we should update soon)
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java</artifactId>
<version>11.0</version>
</dependency>
The graphql works fine and isimplemented like this:
#Component
public class GraphQLProvider {
#Autowired
GraphQLDataFetchers graphQLDataFetchers;
private GraphQL graphQL;
#PostConstruct
public void init() {this.graphQL = /*init;*/null;}
private RuntimeWiring buildWiring() {
RuntimeWiring.Builder b = RuntimeWiring.newRuntimeWiring()
.type(TypeRuntimeWiring.newTypeWiring("Query")
.dataFetcher("freightCarrier", graphQLDataFetchers.getCow()))
.type(TypeRuntimeWiring.newTypeWiring("Query")
.dataFetcher("personCarrier", graphQLDataFetchers.getSheep())));
return b.build();
}
}
#Component
public class GraphQLDataFetchers {
#AutoWired
private CowRepository cowRepo;
#AutoWired
private sheepRepository sheepRepo;
public DataFetcher getCow() {
DataFetcher dataFetcher = (DataFetchingEnvironment dfe) -> {
int id = dfe.getArgument("id");
return getGraphQlCowFromCowEntity(cowRepo.getById(id));//dirty!
};
return dataFetcher;
}
public DataFetcher getCow() {
DataFetcher dataFetcher = (DataFetchingEnvironment dfe) -> {
int id = dfe.getArgument("id");
return getGraphQlSheepFromSheepEntity(cowRepo.getById(id));//dirty!
};
return dataFetcher;
}
private Cow getGraphQlCowFromCowEntity(CowE ce){//dirty!
return new Cow(ce.getId(), ce.getMilkInL(),ce.getLegs());
}
private Sheep getGraphQlSheepFromSheepEntity(SheepE se){//dirty!
return new Sheep(se.getId(), se.getLastTime(),se.getLegs());
}
public class Sheep
private long id;
private Datetime lastTimeSheared;
private int nrOfLegs;
public Sheep(long id, DateTime lasttimeSheared, int nrOfLegs){
//u know what happens here
}
}
public class Cow
private long id;
private int milkInL;
private int nrOfLegs;
public Sheep(long id, int milkInL, int nrOfLegs){
//u know what happens here
}
}
So how to get rid of getGraphQlCowFromCowEntity and getGraphQlSheepFromSheepEntity. It double ups the code and also is in direct conflict to what graphql is suppose to be abstraction of the data. With this design here each time all fields are loaded through jpa and not only requested fields.
Imagine this is a way more complex environment with more fields.
The graphql schema can't be changed as it's not my responsibility, changing the entire back-end to match schema is also not what I want to archive.
Kind regards
You should use DTO. Retrieving and sending entity object is bad practice as you do not want your grahql api to change every time you refactor you database model, or in your case. Your Sheep and Cow objects are DTO, but you will need some way to convert your entity to DTO (getGraphQlCowFromCowEntity is fine, but you could use polymorphism - CowEntity.toDTO() - or have a service layer do the conversion, there are plenty of way to do this).
To answer your concerns about loading only the requested data, you want your DTO object to only be populated with the requested fields. One way to do this is, instead of populating all fields, have the DTO own a reference to the entity object and retrieve the data from the entity object only when requested.
public class Sheep {
private SheepE entity;
public Sheep(SheepE entity){
this.entity=entity;
}
public getId() {
return entity.getId();
}
public getLastTimeSheared() {
return entity.getLastTimeSheared();
}
...
}
Please see this answer I wrote to a similar question: Graphql Tools: Map entity type to graphql type
I'm trying to call a procedure function in DB and to mapped the Hierarchy answer to entity class using hibernate.The entities that I wrote suppose to represent the result structure that is returned from a ref cursor function.
The result of the function has a property which is a list of objects and inside that object there is also a property that is also a list of another objects.
On application startup I get the MappingException as I wrote in the title above.
I tried to add the annotation #OneToMany but it is not working because it is not looking at tables in DB.
Again, the entities represent the function result and not select from table tables.
Someone has any idea of any annotation that could prevent from this exeception to araise in the application startup?
My Entities:
Main Entity that call to procedure function (rc):
#NamedNativeQuery(name = "GetDeliveryOptionsRC.runFunction",
query = "{ ? = call fn_get_delivery_options_rc_dev(:i_account_number,:i_work_order_number,:i_task_id,:i_task_type,:i_content_type_code,:i_sap_operator_code)}", resultClass = GetDeliveryOptionsRC.class,
hints = {#QueryHint(name = "org.hibernate.callable", value = "true")})
#Entity
#XmlRootElement(name = "GetDeliveryOptionsRC")
public class GetDeliveryOptionsRC implements Serializable {
#Id
private Long rownum;
#Column(name = "ACCOUNT_NUMBER")private Long accountNumber;
#Column(name = "TASK_TYPE")private Long taskType;
#Column(name = "CONTENT_TYPE")private String contentType;
#Column(name = "CONTENT_TYPE_CODE")private Long contentTypeCode;
#Column(name = "MESSAGE_CODE")private Long messageCode;
#Column(name = "MESSAGE_ERROR_CODE")private Long messageErrorCode;
#Column(name = "MESSAGE_ERROR_DESCR")private String messageErrorDescr;
#Column(name = "DELIVERY_OPTIONS")private List<DeliveryOptionsRC> deliveryOptions;
//getters & setters
}
Delivery Options Entity:
#Entity
#XmlRootElement(name = "DeliveryOptions")
public class DeliveryOptionsRC implements Serializable {
#Id
#Column(name = "DELIVERY_TYPE") private String deliveryType;
#Column(name = "DELIVERY_DESCR") private String deliveryDescr;
#Column(name = "PRICE") private Double price;
#Column(name = "EXISTS_FLAG") private String existFlag;
#Column(name = "IS_VALID") private String isValid;
#Column(name = "IS_SAP_ORDER") private String isSapOrder;
#Column(name = "IS_OTHER_ADDRESS") private String isOtherAddress;
#Column(name = "IS_SCHEDULE_NEEDED") private String isScheduledNeeded;
#Column(name = "PICKUP_SOURCE") private String pickupSource;
#OneToMany(targetEntity=ChargeJobs.class, mappedBy="deliveryOptionsRC", fetch=FetchType.EAGER)
#Column(name = "CHARGE_JOBES") private List<ChargeJobs> chargeJobs;
//getters & setters
}
Charge Jobs Entity:
#Entity
#XmlRootElement(name = "ChargeJobs")
public class ChargeJobs implements Serializable {
#Column(name = "JOB_OFFER_ID") private Long jobOfferId;
#Id
#Column(name = "JOB_CODE") private String jobCode;
#Column(name = "PRICE") private Double price;
#Column(name = "QUANTITY") private Long quantity;
//getters & setters
}
The RC function:
CREATE OR REPLACE FUNCTION YES_SIMPLE.fn_get_delivery_options_rc_dev (i_account_number NUMBER,
i_work_order_number NUMBER ,
i_task_id NUMBER ,
i_task_type NUMBER,
i_content_type_code NUMBER,
i_sap_operator_code NUMBER
)
RETURN sys_refcursor
is
out_ref sys_refcursor ;
ret tr.delivery_options_list_t;
xml sys.xmltype;
begin
ret:= TR.fn_get_delivery_options (p_account_number => i_account_number,
p_work_order_number =>i_work_order_number,
p_task_id =>i_task_id,
p_task_type =>i_task_type,
p_content_type_code =>i_content_type_code,
p_sap_operator_code => i_sap_operator_code) ;
open out_ref for
select ret.Account_number Account_number
,ret.Task_Type Task_Type,
ret.content_type content_type,
ret.content_type_code content_type_code,
ret.message_code message_code,
ret.message_error_code message_error_code,
ret.message_error_descr message_error_descr
,cursor(select DELIVERY_TYPE ,
DELIVERY_DESCR ,
PRICE ,
EXISTS_FLAG ,
IS_VALID ,
IS_SAP_ORDER ,
IS_OTHER_ADRESS ,
IS_SCHEDULE_NEEDED ,
PICKUP_SOURCE,
cursor(select * from(select * from(select * from table(select ret.delivery_options from dual) ))) charge_jobs
from table(select ret.delivery_options from dual) ) delivery_options
from dual;
return out_ref;
end;
Getting the following result:
ROWNUM 1
ACCOUNT_NUMBER 13
TASK_TYPE 1
CONTENT_TYPE OTT
CONTENT_TYPE_CODE 2
MESSAGE_CODE 98
MESSAGE_ERROR_CODE 98
MESSAGE_ERROR_DESCR
DELIVERY_OPTIONS <Cursor>
DELIVERY_TYPE T
DELIVERY_DESCR Tech
PRICE 0
EXISTS_FLAG N
IS_VALID Y
IS_SAP_ORDER N
IS_OTHER_ADRESS N
IS_SCHEDULE_NEEDED Y
PICKUP_SOURCE
CHARGE_JOBS <Cursor>
JOB_OFFER_ID 900310
JOB_CODE D80
PRICE 0
QUANTITY 1
I'm trying to fetch just a part of the model using Ebean in Play! Framework, but I'm having some problems and I didn't found any solutions.
I have these models:
User:
#Entity
#Table(name = "users")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class User extends Model{
#Id
private int id;
#NotNull
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "last_name")
private String lastName;
#NotNull
#Column(nullable = false)
private String username;
#NotNull
#Column(nullable = false)
private String email;
private String gender;
private String locale;
private Date birthday;
private String bio;
#NotNull
#Column(nullable = false)
private boolean active;
private String avatar;
#Column(name = "created_at",nullable = false)
private Date createdAt;
#OneToMany
private List<UserToken> userTokens;
// Getters and Setters omitted for brevity
}
UserToken:
#Entity
#Table(name = "user_tokens")
public class UserToken extends Model {
#Id
private int id;
#Column(name = "user_id")
private int userId;
private String token;
#Column(name = "created_at")
#CreatedTimestamp
private Date createdAt;
#ManyToOne
private User user;
// Getters and Setters omitted for brevity
}
And then, I have a controller UserController:
public class UserController extends Controller{
public static Result list(){
User user = Ebean.find(User.class).select("firstName").where().idEq(1).findUnique();
return Results.ok(Json.toJson(user));
}
}
I expected that, when using the .select(), it would filter the fields and load a partial object, but it loads it entirely.
In the logs, there is more problems that I don't know why its happening.
It is making 3 queries. First is the one that I want. And then it makes one to fetch the whole Model, and another one to find the UserTokens. I don't know why it is doing these last two queries and I wanted just the first one to be executed.
Solution Edit
After already accepted the fact that I would have to build the Json as suggested by #biesior , I found (out of nowhere) the solution!
public static Result list() throws JsonProcessingException {
User user = Ebean.find(User.class).select("firstName").where().idEq(1).findUnique();
JsonContext jc = Ebean.createJsonContext();
return Results.ok(jc.toJsonString(user));
}
I render only the wanted fields selected in .select() after using JsonContext.
That's simple, when you using select("...") it always gets just id field (cannot be avoided - it's required for mapping) + desired fields, but if later you are trying to access the field that wasn't available in first select("...") - Ebean repeats the query and maps whole object.
In other words, you are accessing somewhere the field that wasn't available in first query, analyze your controller and/or templates, find all fields and add it to your select (even if i.e. they're commented with common HTML comment in the view!)
In the last version of Play Framework (2.6) the proper way to do this is:
public Result list() {
JsonContext json = ebeanServer.json();
List<MyClass> orders= ebeanServer.find(MyClass.class).select("id,property1,property2").findList();
return ok(json.toJson(orders));
}
I'm getting this exception in an attempt to persist an entity object in an Oracle database, and I only started getting this error after switching my JPA project to EclipseLink 2.0 from Hibernate, and I'm using "entity inheritance" if this could have anything to do with it (which I highly suspect).
*
Caused by: javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.0.1.v20100213-r6600): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: ORA-00957: duplicate column name
Error Code: 957
Call: INSERT INTO SUREC (ID, PERSON_ID, SURECID, VERSIYONNO, FAZ, FORM_TARIH, DURUMKODU_ID, surecId) VALUES (...
*
The exception message suggests that SURECID is generated twice in the SQL which seems to be causing the duplicate column error, however surecId is defined once as a property and annotated as a discriminator column in the entity class: (see below)
The base entity class resembles:
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorColumn(name = "surecId")
public class Surec implements java.io.Serializable {
private static final long serialVersionUID = -6008473677883005878L;
#Column(name = "ID")
private Long id;
#Basic(optional = false)
#Column(name = "FAZ")
private int faz;
#Basic(optional = false)
#Column(name = "FORM_TARIH")
#Temporal(TemporalType.DATE)
private Date formTarih;
#Column(name = "PERSON_ID")
private Integer personId;
// #Column(name = "SURECID", updatable = false, length=17)
#Column(updatable = false, length=17)
private String surecId;
#Column(name = "VERSIYONNO")
private Long versiyonno;
#JoinColumn(name = "DURUMKODU_ID", referencedColumnName = "ID")
#ManyToOne
private DurumKod durumKodu;
public Surec() {
}
public Surec(String surecId) {
this.surecId = surecId;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
.
.
.
public String getSurecId() {
return surecId;
}
public void setSurecId(String surecId) {
this.surecId = surecId;
}
.
.
.
I commented the "#Column(name=..." annotation jus to see if it could be causing the duplicate column error, but it didn't work out.
And below is the polymorphic entity extending Surec.java above:
#Entity
#DiscriminatorValue("atf")
public class MailOrder extends Surec {
private static final long serialVersionUID = 8333637555543614502L;
#Column(name = "AMOUNT")
private Double amount;
#Basic(optional = false)
#Column(name = "CURRENCY", length = 17)
private String currency;
#Column(name = "BANK")
private String Bank;
#Column(name = "ACCOUNT_ID", length = 31)
private String accountId;
#Column(name = "INVOICE_ID")
private Integer invoiceId;
public MailOrder() {
}
public MailOrder(String surecId) {
super(surecId);
}
public String getCurrency() {
return currency;
}
.
.
.
The error occurs when I try to persist this very sub-entity.
It doesn't override any property of its superclass, although I'm not sure if it's the constructor...
Any advise to resolve the problem (and acknowledgement of any possible EclipseLink or Oracle (or my!) bug will be appreciated.
This is a common issue if you have a relationship mapping that uses this field, and has to do with case sensitivity. Try indicating the descriminator colum as
#DiscriminatorColumn(name = "SURECID")
EclipseLink is case sensitive by default, which is why surecId is seen as a different field from SURECID. You can make EclipseLink case insensitive by using the eclipselink.jpa.uppercase-column-names property, which when set to true, forces EclipseLink to use the upper case on field name comparisons.
I guess you need to mark the property as insertable = false, updateable = false, since it's already inserted as a discriminator:
#Column(insertable = false, updatable = false, length=17)
private String surecId;