I have a 3 models and 1 table to Many to many relationship on my project
this:
#Embeddable
#Getter
#Setter
public class ProductWarehouseId implements Serializable {
#Column(name = "warehouse_Id")
private Long warehouseId;
#Column(name = "product_Id")
private Long productId;
#Column(name = "user_Id")
private Long userId;
public ProductWarehouseId() {
}
public ProductWarehouseId(Long warehouseId, Long productId, Long userId) {
this.warehouseId = warehouseId;
this.productId = productId;
this.userId = userId;
}
}
---------------------------------------------------
#Entity
#NoArgsConstructor
#Getter
#Setter
public class ProductWarehouse {
#EmbeddedId
ProductWarehouseId productWarehouseId;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("productId")
#JoinColumn(name = "product_id")
ProductEntity product ;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("warehouseId")
#JoinColumn(name = "warehouse_id")
WarehouseEntity warehouse ;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("userId")
#JoinColumn(name = "user_id")
UserEntity userEntity;
#Column(name = "stockAmount")
private Long stockAmount;
#Column(name = "transctionDate")
#Temporal(TemporalType.TIMESTAMP)
private Date transactionDate = new Date();
public ProductWarehouse(ProductEntity product, UserEntity user) {
this.product = product;
this.userEntity = user;
}
}
********************************************************
#Getter
#Setter
#Entity
#RequiredArgsConstructor
public class ProductEntity extends BaseEntity{
#OneToMany(mappedBy = "product",cascade = CascadeType.ALL)
private Set<ProductWarehouse> productWarehouses;
//And more veriables
}
------------------------------------
#Getter
#Setter
#Entity
public class WarehouseEntity extends BaseEntity{
#OneToMany(mappedBy = "warehouse",cascade = CascadeType.ALL)
private Set<ProductWarehouse> productWarehouses = new HashSet<>();
//and more veriables
}
When i trying to select list from product_warehouse table to make changes, i have some Exceptions.
I want to transfer the products between warehouses using fromId and toId
I using this method in service class:
#Override
#Transactional
public void transfer(Long fromId, Long toId) {
WarehouseEntity warehouseEntity = warehouseCRUDRepository.getOne(fromId);
WarehouseEntity warehouseEntity1 = warehouseCRUDRepository.getOne(toId);
if (warehouseEntity.getStatus().equals(WarehouseStatus.ACTIVE) && warehouseEntity1.getStatus().equals(WarehouseStatus.ACTIVE)){
Collection<ProductWarehouse> productWarehouses = em
.createNativeQuery("select c from product_warehouse c where c.warehouse_id =:fromId")
.setParameter("fromId",fromId)
.getResultList();
for (ProductWarehouse p : productWarehouses){
p.getProductWarehouseId().setWarehouseId(toId);
p.setWarehouse(warehouseCRUDRepository.getOne(toId));
}
}
}
And the Exception is :
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is javax.persistence.PersistenceException: org.hibernate.MappingException: No Dialect mapping for JDBC type: 2002] with root cause.
Can you hep me please.
I am sorry for my English, and thank you.
ProductWarehouse is already having Warehouse in it, I don't understand why are you again setting it up by fetching it from DB inside the for loop.
I don't see any necessity of the for loop in that method, also as you described above you haven't defined many to many relationship anywhere. while building the relationship you can use the join table as explained here
if you need more information, please share more details of your need and errors you are facing.
Related
I got a lot of answers and advice, tried changing my code according to these tips.
But, unfortunately, these tips helped only partially.
Now, when creating a new project and creating a new user, I can add the desired user to the set of projects, and the required project will be added to the set of users.
But the relationship between the desired project and the desired user will not appear in the project_user table.
Please help find the answer.
Entity Project
#Data
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "project")
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String name;
#Column
private String description;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "project_users",
joinColumns = #JoinColumn(name = "project_id"),
inverseJoinColumns = #JoinColumn(name = "users_id"))
private Set<User> projectUserSet = new HashSet<>();
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "project_task",
joinColumns = #JoinColumn(name = "project_id"),
inverseJoinColumns = #JoinColumn(name = "task_id"))
private Set<Task> projectTaskSet = new HashSet<>();
public void addUserToProject(User user){
this.projectUserSet.add(user);
user.getUserProjectsSet().add(this);
}
public void addTasksToProject(Task task){
this.projectTaskSet.add(task);
task.getTasksProjectSet().add(this);
}
//constructors, hashCode, equals, toString
}
Entity User
#Data
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String firstName;
#Column
private String lastName;
#ManyToMany(mappedBy = "projectUserSet", cascade = CascadeType.ALL)
private Set<Project> userProjectsSet = new HashSet<>();
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "users_task",
joinColumns = {#JoinColumn(name = "users_id")},
inverseJoinColumns = {#JoinColumn(name = "task_id")})
private Set<Task> userTasksSet = new HashSet<>();
public void addTaskToUser(Task task) {
this.userTasksSet.add(task);
task.getTasksUserSet().add(this);
}
//constructors, hashCode, equals, toString
}
project and user initialization
Project project1 = new Project("Project1", "Project1");
User user1 = new User("User1", "User1");
project1.addUserToProject(user1);
With code shown below, table project_user is populated, verified using H2 console. In order to avoid stack overflow, I had to modify method Project#addTaskToUser as shown below.
Please note that only code relevant to the question, is included.
Normally, issue should be described by some tests. In this case, I added a CommandLineRunner that runs at startup.
CascadeType.ALL is not recommended for many-to-many relations, hence I changed this in code shown below.
Tested using H2 in-memory db.
Project class
#Data
#Entity
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
#ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinTable(name = "project_users",
joinColumns = #JoinColumn(name = "project_id"),
inverseJoinColumns = #JoinColumn(name = "users_id"))
private Set<MyUser> projectUserSet = new HashSet<>();
public void addUserToProject(User user) {
this.projectUserSet.add(user);
}
}
Project repo
public interface ProjectRepo extends JpaRepository<Project, Long> { }
User class
// cannot use #Data here because it will cause cyclic ref and stack overflow when accessing userProjectsSet
#Getter
#Setter
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
#ManyToMany(mappedBy="projectUserSet")
private Set<Project> userProjectsSet = new HashSet<>();
}
User repo
public interface UserRepo extends JpaRepository<User, Long> {}
In app class
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#Bean
CommandLineRunner run(ProjectRepo projectRepo, EntityManagerFactory entityManagerFactory) {
return args -> {
var testUser = new User();
testUser.setFirstName("first-name");
testUser.setLastName("last-name");
var project = new Project();
project.setName("project-name");
project.setDescription("project-description");
project.addUserToProject(testUser);
projectRepo.save(project);
// get saved user and print some properties
var userInDb = entityManagerFactory.createEntityManager().find(User.class, testUser.getId());
System.out.println(userInDb.getFirstName()); // prints "first-name"
System.out.println(userInDb.getUserProjectsSet().size()); // prints "1"
};
}
}
Good Evening,
I am relatively new to using Hibernate, and I am running into the following error:
"message": "org.springframework.web.util.NestedServletException: Request processing failed;
nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist:
com.company.project.data.relational.models.ListsItems; nested exception is org.hibernate.PersistentObjectException:
detached entity passed to persist: com.company.project.data.relational.models.ListsItems",
I have a JSON object being sent from the front-end that has a nested object. I am trying to get the the nested items in a separate table in MySQL, with a relationship using the original objects ID.
Here's an example of the JSON:
{
"name":"Test",
"type":"App Id List",
"listItems":
[
{
"id":1,
"name":"Test",
"value":" 1"
},
{
"id":2,
"name":"NEW TEST",
"value":" 2"
}
]
}
Here is my Lists model:
#Entity
#Getter
#Setter
#NoArgsConstructor
#Table(name = "lists")
public class Lists implements Serializable, OperationalEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(columnDefinition = "char", nullable = false)
private String guid;
private String name;
private String type;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "listItems", orphanRemoval = true)
#Cascade({org.hibernate.annotations.CascadeType.ALL, })
private Set<ListsItems> listItems;
private Date created;
private Date updated;
}
And here is my ListsItems model:
#Getter
#Setter
#Entity
#Table(name = "lists_items")
#NoArgsConstructor
public class ListsItems implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private String value;
#NaturalId
#ManyToOne(optional = false, fetch = FetchType.EAGER)
#JoinColumn(name = "lists_id", referencedColumnName = "id")
private Lists listItems;
}
Here is the save function:
#PostMapping(value = "/add")
#PreAuthorize("hasRole('ADMIN')")
public #ResponseBody WebResponse<W> create(#RequestBody W webModel) {
D dbModel = asDbModel(webModel);
dbModel.setGuid(UUID.randomUUID().toString());
return WebResponse.success(createWebModelFromDbModel(getDatabaseEntityRepository().save(dbModel)));
}
Any ideas on what might be causing this error? I've searched a bit but nothing I've tried from any other solutions have worked out.
Thanks in advance!
- Travis W.
The answer was to make the following changes to ListItems:
#JsonIgnore // this import will be from jackson
#NaturalId
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "lists_id", referencedColumnName = "id")
private Lists list;
And the following to Lists:
#OneToMany(fetch = FetchType.EAGER, mappedBy = "list", orphanRemoval = true)
#Cascade({org.hibernate.annotations.CascadeType.ALL, })
private Set<ListsItems> listItems;
I also needed to iterate over the results:
#Override
protected Lists asDbModel(WebLists webModel) {
Lists dbModel = new Lists();
dbModel.setId(webModel.getId());
dbModel.setName(webModel.getName());
dbModel.setType(webModel.getType());
dbModel.setListItems(webModel.getListItems());
for(ListsItems item : webModel.getListItems()) {
item.setList(dbModel);
}
return dbModel;
}
I try to save object Run to database. I defined relation between Run and City. One city could have many runs. I got problem with city_id. Is null.
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause
java.sql.SQLIntegrityConstraintViolationException: Column 'city_id' cannot be null
My entieties and controller:
City
#Entity
#Getter
#Setter
#Builder
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "cities")
public class City {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "city_id")
private long id;
#OneToMany(mappedBy = "city", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Run> runs = new ArrayList<>();
private String name;
}
Run
#Entity
#Builder
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "runs")
public class Run {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "name_run")
private String nameRun;
#Column(name = "distance")
private double distance;
#Column(name = "date")
private Date date;
#Column(name = "my_time")
private String myTime;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "city_id", nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
#JsonIgnore
private City city;
}
Controller
#CrossOrigin
#RestController
#RequestMapping("/api/")
public class RunController {
private RunRepository runRepository;
private RunService runService;
public RunController(RunRepository runRepository, RunService runService) {
this.runRepository = runRepository;
this.runService = runService;
}
#GetMapping("runs")
public ResponseEntity<List<Run>> getRuns() {
return runService.getRuns();
}
#PostMapping("runs")
public ResponseEntity addRun(#RequestBody Run run) {
return new ResponseEntity<>(runRepository.save(run), HttpStatus.OK);
}
}
I would like to save the run in DB.
My test request looks like :
{
"nameRun": "test",
"distance":"5.0",
"date":"2020-12-12",
"myTime":"50:40",
"city":"test1"
}
Result from evaluate expresion in Intelijj:
Why the City = null? Is here error in mapping?
Can you try with this json but you need to pass city id in json.
{
"nameRun": "test",
"distance": "5.0",
"date": "2020-12-12",
"myTime": "50:40",
"city": {
"id": 1,
"name": "test1"
}
}
Thanks
First of all, use Long for id please. It is better to add #Entity annotation too.
#Entity
public class City {
#Id
#GeneratedValue
private Long id;
#OneToMany(mappedBy = "city", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Run> runs = new ArrayList<>();
}
#Entity
public class Run {
#Id
#GeneratedValue
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
private City city;
}
You need to set city_id when you save Run.
The simplest way to do that is just create a fake transient City and set id to it.
City city = new City();
city.setId(1L);
Run run = new Run();
run.setCity(city);
repository.save(run);
Obviously you should have a city with id 1L in the database.
Other options are
Use something like session.load() Hibernate analogue with Spring repository to create City without loading it from datatbase.
Load City entity entirely by id.
if you wanna save any run class,
Run run = new Run();
City city = new City();
city.getRuns().add(run);
runRepository.save(run);
if you wanna save any run class, first you need to insert to (Arraylist) runs variable of city class like city.getRuns().add(run) after filling run then you can runRepository.save(run).
Also my samples are here. You can look at myclasses.
First class is called Patient .
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
#ToString
#Table(name = "aapatient")
public class Patient {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "AA_PATIENT_SEQ")
#SequenceGenerator(sequenceName = "AA_PATIENT_SEQ", allocationSize = 1, name = "AA_PATIENT_SEQ")
#Column(name = "patientid")
private Long patientid;
private String name;
private String lastname;
#OneToMany(mappedBy = "patient", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Problem> problems;
}
Second Class called Problem is this one.
#Data
#AllArgsConstructor
#NoArgsConstructor
#ToString
#Entity
#Table(name="aaproblem")
public class Problem{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "AA_PATIENT_SEQ")
#SequenceGenerator(sequenceName = "AA_PATIENT_SEQ", allocationSize = 1, name = "AA_PATIENT_SEQ")
#Column(name = "problemid")
private Long problemid;
private String problemName;
private String problemDetail;
#Temporal(TemporalType.TIMESTAMP)
Date creationDate;
#NotNull
#ManyToOne(optional = true, fetch = FetchType.LAZY)
#JoinColumn(name = "patient_id")
private Patient patient;
}
#JsonIgnoreProperties doesn't work properly
I'm writing a one to many relation, one property can have multiply propertySale, I try to use #JsonIgnoreProperties to avoid infinite recursion, but for some reason it doesn't work when I try to save a propertySale. Counld someone please tell me where I do wrong?
In Property.java
#Data
#Getter
#Entity
#Table(name = "Property")
public class Property {
#Id
#GeneratedValue
private Long id;
...
#OneToMany(mappedBy="property", cascade = CascadeType.ALL, targetEntity = PropertySale.class)
#JsonIgnoreProperties("property")
private Set<PropertySale> propertySales = new HashSet<>();
...
public void addPropertySale(PropertySale propertySale){
this.propertySales.add(propertySale);
}
}
In PropertySale.java
#Data
#Getter
#Entity
#Table(name = "PropertySale")
public class PropertySale {
#Id
#GeneratedValue
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "property_id", referencedColumnName = "id")
#JsonIgnoreProperties("propertySales")
private Property property;
...
In PropertySaleServiceImp.java
#Service
public class PropertySaleServiceImp implements PropertySaleService{
#Autowired
private PropertySaleRepository propertySaleRepository;
#Autowired
private PropertyRepository propertyRepository;
#Override
public ResponseEntity<PropertySale> savePropertySale(PropertySale propertySale) {
Optional<Property> existPropertyOpt = this.propertyRepository.findById(propertySale.getProperty().getId());
if(existPropertyOpt.isPresent()){
Example<PropertySale> propertySaleExample = Example.of(propertySale);
Optional<PropertySale> existPropSale = this.propertySaleRepository.findOne(propertySaleExample);
if(existPropSale.isPresent()){
throw new PropertySaleAlreadyExistException();
}else{
Property existProperty = existPropertyOpt.get();
propertySale.setProperty(existProperty);
existProperty.addPropertySale(propertySale);
this.propertyRepository.save(existProperty);
return new ResponseEntity<>(propertySale, HttpStatus.CREATED);
}
}else{
throw new PropertyNotFoundException(propertySale.getProperty().getId());
}
}
I get
org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.StackOverflowError
when the line this.propertyRepository.save(existProperty);
is executed.
I have this entity
#Entity
#Table(name = "REPORT_TASCK")
#Data
public class ReportTasck {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
public Long id;
#Column(name = "type")
public String type;
#Column(name = "status")
public Integer status;
#Column(name = "type")
#OneToMany(mappedBy = "reportTasck", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
public List<Bill> bills;
}
and
#Entity
#Table(name = "BIIL")
#Data
public class Bill {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
public Long id;
#Column(name = "neme")
public String neme;
#Column(name = "status")
public Integer status;
#ManyToOne()
#JoinColumn(name = "REPORT_TASCK_ID")
public ReportTasck reportTasck;
}
then I tried fill it and save
ReportTasck reportTasck = new ReportTasck();
reportTasck.setStatus(0);
reportTasck.setType("standart");
List<Bill> bills = new ArrayList<>();
for (BillDto billDto : all) {//640 items
Bill bill = new Bill();
bill.setStatus(0);
bill.setNeme(billDto.getBill());
bill.setReportTasck(reportTasck);
bills.add(bill);
}
reportTasck.setBills(bills);
reportTasckRepository.save(reportTasck);
but when start this line reportTasck.setBills(bills); I get error in debbug mode Method threw 'java.lang.StackOverflowError' exception. Cannot evaluate entity.ReportTasck.toString()
and when I tried save I get this error Method threw 'org.springframework.dao.DataIntegrityViolationException' exception.
could not execute statement; SQL [n/a]; constraint [null]
I dont understand why I get this error and how fix