I am trying to make a database with these models
Report model
#DatabaseTable(tableName = "report")
public class Report {
#DatabaseField(generatedId = true)
private int id;
#DatabaseField(columnName = "client_name")
private String client;
#ForeignCollectionField(maxEagerLevel = 2, eager = true)
private Collection<Product> productList;
Product model
#DatabaseTable(tableName = "product")
public abstract class Product implements Comparable<Product> {
#DatabaseField(generatedId = true)
private int id;
#DatabaseField(foreign = true)
private Report report;
#DatabaseField(columnName = "instance")
private String instance;
#DatabaseField(columnName = "product_type")
private String productType;
So I am able to successfully insert in the tables,
but when trying out:
List<Report> reports = reportDao.queryBuilder().where().eq("client_name", clientName).query();
I get an exception
Could not create object for class model.product.Product
So it makes sense, because Product is abstract, but is there a way using the "Instance" attribute from Product interface which indicates what kind of implementation of product it is, to create the appropriate object while running the query?
Thank you
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 am working on a nutrition tracking app. For that I am trying to setup a many to many relation with some additional fields using Room library. I am not sure yet if its even possible?
The use case I am having trouble with is that I want to retrieve a list of Meals(Breakfast, Lunch, Dinner) along with the foods the user has had along with the portion sizes.
So it will be something like -> Meals would have a List of Item. Item would have (1)Food + (2)portionSize of that food. The classes are as follows:
#Entity
public class Meal {
#PrimaryKey( autoGenerate = true)
#ColumnInfo(name = "MEAL_ID")
private Long mealId;
}
#Entity
public class Food {
#PrimaryKey( autoGenerate = true)
#ColumnInfo(name = "FOOD_ID")
private Long id;
}
//Join Table
#Entity(tableName = "MEAL_FOOD", primaryKeys = {"MEAL_ID", "FOOD_ID"})
public class MealFood {
#ColumnInfo(name = "MEAL_ID")
private Long mealId;
#ColumnInfo(name = "FOOD_ID")
private Long foodId;
private Double portionSize;
}
Now for accessing the data, I am trying something like the following pojo:
public class MealWithFoods {
#Embedded
public Meal meal;
#Relation(parentColumn = "MEAL_ID", entityColumn = "FOOD_ID", entity = Food.class, associateBy = #Junction(MealFood.class))
public List<Item> items;
}
public class Item {
#Embedded
private Food food;
#Relation(parentColumn = "FOOD_ID", entityColumn = "FOOD_ID", projection = {"PORTION_SIZE"}, entity = MealFood.class)
private Double portionSize;
}
DAO:
public interface MealsWithFoodsDao {
#Transaction
#Query("SELECT * FROM MEAL")
public List<MealWithFoods> getMealsWithFoods();
}
I have been trying at it for sometime now. Can someone pls offer a path forward. Thanks.
I have these Objects:
#Data
#Entity
#Table
#EqualsAndHashCode(callSuper = true)
public class User extends AbstractEntity implements Serializable {
private static final long serialVersionUID = -55089179131569489L;
private String username;
private String email;
private boolean admin;
private String name;
private String surname;
#OneToMany(mappedBy = "owner")
private List<Ad> ads;
}
and
#Entity
#Table
#Data
#EqualsAndHashCode(callSuper = true)
public class Ad extends AbstractEntity implements Serializable {
private static final long serialVersionUID = -4590938091334150254L;
private String name;
private String description;
private double price;
#Enumerated(EnumType.STRING)
private Category category;
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "OWNER_ID")
private User owner;
}
When I try to execute a POST with an object of type Ad.class with inside an existing object of type User.class (already in the Database) the service saves only the Ad object and the join column "OWNER_ID" remains empty.
I think that the mapping is correct. Could you help me to figure out the problem?
This is my Repository:
#Repository
#Transactional(readOnly = true)
public interface AdRepository extends PagingAndSortingRepository<Ad, String>
{}
and this is my RestRepository
#RepositoryRestResource(collectionResourceRel = "ad", path = "ad")
public interface AdRestRepository extends PagingAndSortingRepository<Ad, String> {}
If I step back a little and generalize your problem,
You are trying to POST a sub resource and expect both actions of
making a new resource (Ad)
making association with the owner (User)
to be happened with a single call.
But unfortunately spring-data-rest does not support such a behavior. You need 2 calls to do this.
One to make the resource (Ad) => POST to /ads with actual payload
Second to make the association => POST to users/{ownerId} with the hateoas link of the resource created by the first call.
Take a look at this section of official documentation.
I'm using spring data and I made this query:
#Query("SELECT DISTINCT u.pk.fleet.fleetName FROM FleetHasUser u WHERE u.pk.user.username = ?1")
List<FleetName> allFleetNameForUser(String username);
The aim of this query is find all FleetName for a specific user.
This is the part of database schema interested in:
The FleetHasUser class:
#Entity
#Table(name = "fleet_has_user", catalog = "dart")
#AssociationOverrides({
#AssociationOverride(name = "pk.user",
joinColumns = #JoinColumn(name = "id_username")),
#AssociationOverride(name = "pk.fleet",
joinColumns = #JoinColumn(name = "id_fleet")) })
public class FleetHasUser implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private FleetHasUserKeys pk = new FleetHasUserKeys();
#EmbeddedId
public FleetHasUserKeys getPk() {
return pk;
}
the FleetHasUserKeys class:
#Embeddable
public class FleetHasUserKeys implements Serializable {
private static final long serialVersionUID = 1L;
protected User user;
protected Fleet fleet;
Fleet class:
#Entity
#Table(name = "fleet", catalog = "dart")
public class Fleet implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private Integer idFleet;
private Ecu ecu;
private String application;
private Cubic cubic;
private Integer power;
private String euroClass;
private String engineType;
private Traction traction;
private String transmission;
private String note;
private FleetName fleetName;
#JsonIgnore
private Set<Car> cars = new HashSet<Car>(0);
#JsonIgnore
private Set<FleetHasUser> fleetHasUsers = new HashSet<FleetHasUser>(0);
As you can see FleetName is a class. I tried without distinct and the list had duplicated elements, so before create a subquery I inserted distinct only for test and it worked. I didn't find information on google, but is it possible to use distinct on a object? How does it work(check the primary key fields)?
Maybe may I even create a Spring data query like findDistinctPkFleetFleetNameByPkUserUsername?
Thanks
As far as i know, the distinct is being applied on the query not on the mapping phase, as you can see #Query doc the annotation only executes jpql queries so a native distinct.
As you say other option to execute a distinct is
Spring data Distinct
You cannot apply "Distinct" to objects, but you can map then on Set's to avoid duplicated, but the better approach is filter properly the data on the database.