Good day!
I am triing to save my Entity model by always get an error
Error inserting bean [class models.CategoryEntity] with unidirectional relationship. For inserts you must use cascade save on the master bean [class models.CategoryEntity].]
Here my class
#Entity
public class CategoryEntity extends Model {
#Id
private String categoryId;
private String Name;
private Integer level;
#OneToMany(targetEntity = CategoryEntity.class, cascade = CascadeType.ALL)
private List<CategoryEntity> categories;
//GETERS SETRES
}
I tried to save the header category, but error is the same
If I understand the question correctly and what you want is that each CategoryEntity contains a list of other CategoryEntities, 2 possible approaches come to mind (although none of them use #OneToMany):
Approach 1:
You could create a #ManyToMany relationship and define a #JoinTable whilst naming its keys:
#Entity
public class CategoryEntity extends Model {
#Id
private String categoryId;
private String name;
private Integer level;
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable( name = "category_category",
joinColumns = #JoinColumn(name = "source_category_id"),
inverseJoinColumns = #JoinColumn(name = "target_category_id"))
public List<CategoryEntity> category_entity_lists = new ArrayList<CategoryEntity>();
}
Approach 2:
Or you could create a new entity for the list of category entities and create a #ManyToMany relationship, e.g.:
#Entity
public class CategoryList extends Model
{
#Id
public Long id;
#ManyToMany
#JoinTable(name="categorylist_category")
public List<CategoryEntity> category_list = new ArrayList<CategoryEntity>();
}
And then in your model:
#Entity
public class CategoryEntity extends Model {
#Id
private String categoryId;
private String name;
private Integer level;
#OneToOne
public CategoryList this_category_list;
#ManyToMany(mappedBy="category_list")
public List<CategoryList> in_other_category_lists = new ArrayList<CategoryList>();
}
Didn't test the code, but what this should do is that each CategoryEntity can be part of several CategoryLists. Each CategoryList contains a list of CategoryEntities.
You would have to initialize this_category_list and add CategoryEntities to its category_list field.
Your model doesnt make sense.
why do you have as class attribute the class itself?
the List should not be in the same class
Related
In my Spring Boot app, I use Hibernate and applied the necessary relations to the following entities properly.
#Entity
public class Recipe {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable=false, length=50)
private String title;
#OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL)
private List<RecipeIngredient> recipeIngredients = new ArrayList<>();
}
#Entity
public class RecipeIngredient {
#EmbeddedId
private RecipeIngredientId recipeIngredientId = new RecipeIngredientId();
#ManyToOne(optional = true, fetch = FetchType.LAZY)
#MapsId("recipeId")
#JoinColumn(name = "recipe_id", referencedColumnName = "id")
private Recipe recipe;
#ManyToOne(optional = true, fetch = FetchType.LAZY)
#MapsId("ingredientId")
#JoinColumn(name = "ingredient_id", referencedColumnName = "id")
private Ingredient ingredient;
}
#Entity
public class Ingredient
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique=true, nullable=false, length=50)
#EqualsAndHashCode.Include
private String name;
#OneToMany(mappedBy = "ingredient", cascade = CascadeType.ALL)
private Set<RecipeIngredient> recipeIngredients = new HashSet<>();
}
Now I am trying to retrieve data by merging related entities. For example, when retrieving a Recipe, I also need to retrieve all Ingredients belonging to this Recipe.
As far as I know, I can use Projection and maybe it is better to only use Hibernate features and retrieve related table data via Java Stream. I have no idea how should I retrieve data via Hibernate.
Suppose that I just need an Optional<Recipe> that has List<Ingredient>. Then, I probably need a DTO class something like that:
#Data
public class ResponseDTO {
private Long id;
private String title;
List<RecipeIngredient> ingredients;
// getter, setter, constructor
}
So, how should I populate this DTO with the requested Recipe and corresponding Ingredient data (getting Ingredient names besides id values) using Java Stream?
Or if you suggest Projection way, I tried it but the data is multiplied by the ingredient count belonging to the searched recipe.
Update:
#Getter
#Setter
#NoArgsConstructor
public class ResponseDTO {
private Long id;
private String title;
List<IngredientDTO> ingredientDTOList;
public ResponseDTO(Recipe recipe) {
this.id = recipe.getId();
this.title = recipe.getTitle();
this.ingredientDTOList = recipe.getRecipeIngredients().stream()
.map(ri -> new IngredientDTO(ri.getIngredient().getName()))
.toList();
}
}
#Getter
#Setter
public class IngredientDTO {
private Long id;
private String name;
public IngredientDTO(String name) {
this.name = name;
}
}
First, in the ResponseDTO you will need you change the type of ingredients from List<RecipeIngredient> to List<Ingredient>.
To manually perform the mapping, you should use (to map from a suppose Recipe recipe to a RespondeDTO response):
ResponseDTO recipeToResponseDTO(Recipe recipe) {
ResponseDTO response = new ResponseDTO();
response.setId(recipe.getId());
response.setTitle(recipe.getTitle());
response.setIngredients(recipe.recipeIngredients.stream()
.map(RecipeIngredient::getIngredient()
.collect(Collectors.toList());
return response;
}
On the other hand, to model a n-n relation, I encourage you to use the approach proposed by E-Riz in the comment.
I have two entity table one category and other is subject
My category entity
#Entity
public class Category extends AuditableEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(unique = true)
private String name;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "description_id")
private CategoryDescription description;
#OneToMany( mappedBy = "category", cascade = CascadeType.ALL,
orphanRemoval = true)
private List<Subject> subjects;
//getter and setter
}
And my Subject entity
#Entity
public class Subject extends AuditableEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(unique = true)
private String name;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "description_id")
private SubjectDescription description;
#ManyToOne(fetch = FetchType.EAGER)
private Category category;
//Getter and Setter
}
Category Repository
#Repository
#Transactional
public interface CategoryRepository extends
JpaRepository<Category, Integer> {
}
Subject Repository
#Repository
#Transactional
public interface SubjectRepository extends JpaRepository<Subject,
Integer> {
}
Category Controller
#RestController
#RequestMapping("/category/api/")
public class CategoryResource {
private final CategoryService categoryService;
private final SubjectService subjectService;
private final TopicService topicService;
public CategoryResource(CategoryService categoryService,
SubjectService subjectService, TopicService topicService) {
this.categoryService = categoryService;
this.subjectService = subjectService;
this.topicService = topicService;
}
#PostMapping("save")
public void saveCategory(#RequestBody Category category) {
categoryService.save(category);
}
I am using postman to save data. Problem is that after saving data to the category and subject table my subject table column category_id is null i can not established a relationship between them my sql structure and data is after saving data it shows like
Category table
Subject Table
category_id is NULL how to set category id i am trying many ways but couldn't find a solution.Please help me to solve this issue
It's great that you are learning spring boot!
To answer your question since the answer is pretty simple, your code is missing category in subject.
subject.setCategory(category);
Now this might cause you an exception, so make sure you save category before you persist subject.
Cheers!
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 have to models i.e paymentRecon and waybill. One paymentRecon can have set of waybills under him. But don't want to tightly coupled waybill with it. So i create models like below:
paymentRecon
#SuppressWarnings("serial")
#Entity
public class PaymentReconciliation extends BaseEntity {
#Column
private String parentId;
#Column
private BigDecimal grossAmount;
#Column
private String currency;
#Column
private Integer totalNumberOfPackages;
#Column
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "PayReconWaybillMap", joinColumns = {
#JoinColumn(name = "reconId") }, inverseJoinColumns = {
#JoinColumn(name = "waybillId") })
private Set<WayBill> waybill = new HashSet<WayBill>();
}
wabill
#SuppressWarnings("serial")
#Entity
public class PaymentReconciliation extends BaseEntity {
#Column(nullable = false, unique = true)
private String barCode;
#Column
private String consigneeName;
#Column
#JsonIgnore
private String countryCode;
}
Now, i have three tables.
So after developing thing's over it. i need to get waybills which are not attached to any paymentRecon. Can anybody help me how to get this.
You can map the "join table" on a new entity PayReconWaybillMap, using a #ManyToOne on PaymentReconciliation to refer to this middle entity:
#ManyToOne
private PayReconWaybillMap pwm;
And in the PayReconWaybillMap entity:
#OneToMany
private List<PaymentReconciliation> pr;
#OneToMany
private List<WayBill> wb;
So, you can use the JOIN, like this:
SELECT wb FROM WayBill wb
LEFT JOIN PayReconWaybillMap pwm ON pwm.waybillId = wb.waybillId
WHERE pwm IS NULL
I've done the necessary changes to my models outlined here. However, I don't know what to put on my join table entity.
Note that my join table has a surrogate key , and two extra columns (date and varchar).
What I've got so far is:
User.java
#Entity
#Table (name = "tbl_bo_gui_user")
#DynamicInsert
#DynamicUpdate
public class User implements Serializable {
private String id;
private String ntName;
private String email;
private Set<GroupUser> groupUsers = new HashSet<GroupUser>(0);
// Constructors and some getters setters omitted
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.user", cascade=CascadeType.ALL)
public Set<GroupUser> getGroupUsers() {
return groupUsers;
}
public void setGroupUsers(Set<GroupUser> groupUsers) {
this.groupUsers = groupUsers;
}
}
Group.java
#Entity
#Table (name = "tbl_bo_gui_group")
#DynamicInsert
#DynamicUpdate
public class Group implements Serializable {
private String id;
private String groupName;
private String groupDesc;
private Set<GroupUser> groupUsers = new HashSet<GroupUser>(0);
// Constructors and some getters setters omitted
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.group", cascade=CascadeType.ALL)
public Set<GroupUser> getGroupUsers() {
return groupUsers;
}
public void setGroupUsers(Set<GroupUser> groupUsers) {
this.groupUsers = groupUsers;
}
}
The problem is that I don't know what to put on my join table entity. Here it is.
GroupUser.java
#Entity
#Table (name = "tbl_bo_gui_group_user")
#DynamicInsert
#DynamicUpdate
#AssociationOverrides({
#AssociationOverride(name = "pk.user",
joinColumns = #JoinColumn(name = "id")),
#AssociationOverride(name = "pk.group",
joinColumns = #JoinColumn(name = "id")) })
public class GroupUser implements Serializable {
private String id;
private User userId;
private Group groupId;
private Date dateCreated;
private String createdBy;
// constructors and getters and setters for each property
// What now? ? No idea
}
user to group would be a Many-To-Many relation. Now, you are splitting that up into Two One-To-Many Relations. Therefore your Mapping Entity simple needs to complete the Many-To-Many relation, by using Many-To-One:
public class GroupUser implements Serializable {
private String id;
#ManyToOne
private User userId;
#ManyToOne
private Group groupId;
private Date dateCreated;
private String createdBy;
}
See also this example: Mapping many-to-many association table with extra column(s) (The Answer with 38 upvotes)