#PostMapping not working - Java17 Spring Boot [closed] - java

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 days ago.
Improve this question
I've got a problem in my 'SpringBootRestApi' app with #PostMapping. This is the only annotation I struggle with (#Get, #Put and #Delete are working properly). I receive status 500 (error: Internal Server Error) all the time. What am I doing wrong?
Here is my #PostMapping:
#PostMapping
public Product insert(#RequestBody Product product) {
product.setProductId(null);
productRepo.save(product);
return product;
}
It is in my REST API Class called "ProductApi"
#RestController
#RequestMapping("/products")
public class ProductApi {
private ProductRepo productRepo;
public ProductApi(ProductRepo productRepo) {
this.productRepo = productRepo;
}
Just in case I'm pasting my Repository Class too
#Repository
public interface ProductRepo extends JpaRepository<Product, Integer> {
// Ignore these:
// List<Product> findByProductName(String name);
// List<Product> findByProductNameContainingIgnoringCase(String name);
// List<Product> findByProductNameContainingIgnoringCaseAndPriceBetween(String name, BigDecimal min, BigDecimal max);
// List<Product> findByPriceBetween(BigDecimal min, BigDecimal max);
}
I've created my DB Table in PGadmin4 (PostgreSQL).
There is my Model Class called Product:
#Entity
#Table(name="products")
#NamedQuery(name="Product.findAll", query="SELECT p FROM Product p")
public class Product implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="product_id", insertable=false, updatable=false)
private Integer productId;
#Column(name="product_name")
private String productName;
private BigDecimal price;
private BigDecimal vat;
private String description;
public Product() {
}
// public Product(Integer productId, String productName, BigDecimal price, BigDecimal vat, String description) {
// this.productId = productId;
// this.productName = productName;
// this.price = price;
// this.vat = vat;
// this.description = description;
// }
public Integer getProductId() {
return this.productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public String getProductName() {
return this.productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public BigDecimal getPrice() {
return this.price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public BigDecimal getVat() {
return this.vat;
}
public void setVat(BigDecimal vat) {
this.vat = vat;
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
}
Here is what logs are saying:
2023-02-12T11:16:27.581+01:00 WARN 29916 --- [nio-8081-exec-5] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: 23502 2023-02-12T11:16:27.581+01:00 ERROR 29916 --- [nio-8081-exec-5] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: null value in column "product_id" of relation "products" violates not-null constraint
Details: Failing row contains (null, XDDD, 69.69, 0.23, XDXDXD).
2023-02-12T11:16:27.583+01:00 ERROR 29916 --- [nio-8081-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [product_id" of relation "products]] with root cause
org.postgresql.util.PSQLException: ERROR: null value in column "product_id" of relation "products" violates not-null constraint
Details: Failing row contains (null, XDDD, 69.69, 0.23, XDXDXD).
Even when I put this in comment (at my #PostMapping): //product.setProductId(null); I still receive the same error.
I thought when I #Post something I don't need to type {id} - this is useless. How am I or someone needs to know what "productId" the next record needs? This should be incremented.

org.postgresql.util.PSQLException: ERROR: null value in column
"product_id" of relation "products" violates not-null constraint
The error message suggests that the "product_id" column in your "products" table cannot contain a null value, but you are trying to insert a null value for that column.
Try passing valid value, not null.

I can see in your code that you manually set the productId to null. since you are using auto generation strategy, please do not set the product Id to null manually. So remove that line product.setProductId(null).
Here is a sample project in my github which might help you do crud correctly. there are a lot of samples for CRUD operations in my git hub account.
https://github.com/alimjz/product-REST

Related

Vaadin - Internal error Please notify the administrator. Take note of any unsaved data, and click here or press ESC to continue

I am developing an application in Vaadin where I have a view for Suppliers.
I am getting the error Internal error Please notify the administrator. Take note of any unsaved data, and click here or press ESC to continue. due to the calling of the method categoriesToString in my View.
View
public SupplierView(CrmService service) {
this.service = service;
addClassName("supplier-view");
setSizeFull();
configureGrid();
form = new SupplierForm(service.findAllCategories(null));
form.setWidth("25em");
form.addListener(SupplierForm.SaveEvent.class, this::saveSupplier);
form.addListener(SupplierForm.DeleteEvent.class, this::deleteSupplier);
form.addListener(SupplierForm.CloseEvent.class, e -> closeEditor());
FlexLayout content = new FlexLayout(grid, form);
content.setFlexGrow(2, grid);
content.setFlexGrow(1, form);
content.setFlexShrink(0, form);
content.addClassNames("content", "gap-m");
content.setSizeFull();
add(getToolbar(), content);
updateList();
closeEditor();
grid.asSingleSelect()
.addValueChangeListener(event -> editSupplier(event.getValue()));
}
private void configureGrid() {
grid.addClassNames("supplier-grid");
grid.setSizeFull();
grid.setColumns("name", "address");
grid.addColumn(supplier -> supplier.categoriesToString())
.setHeader("Categories");
grid.getColumns().forEach(col -> col.setAutoWidth(true));
}
}
The said method belongs to the class Supplier that can be seen below. This class has a Set of Category that I am trying to go trough in order to return all categories present in it.
Supplier
#Entity
public class Supplier extends AbstractEntity {
#NotEmpty
private String name;
#NotEmpty
private String address;
#OneToMany(mappedBy = "supplier")
private Set<Category> categories = new LinkedHashSet<>();
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return this.address;
}
public void setAddress(String address) {
this.address = address;
}
public Set<Category> getCategories() {
return categories;
}
public void setCategories(Set<Category> categories) {
this.categories = categories;
}
public String categoriesToString(){
String allCategories = "";
for (Category c : this.categories)
{
allCategories += c.getDescription();
}
return allCategories;
}
}
In order to do some testing I tried to make the method just return "test" and in that scenario the word was indeed added to "Categories" column and no error was shown.
Category class
#Entity
public class Category extends AbstractEntity{
#NotBlank
private String description;
#ManyToOne
#JoinColumn(name = "supplier_id")
#JsonIgnoreProperties({"categories"})
private Supplier supplier;
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
Why is this happening? Am I iterating through the set in a incorrect way?
You have not specified the fetch mode for your Entity, so it is defaulting to Lazy mode. Your current logic will only work with Eager mode (that would fetch the "categories" one-to-many relationship), but this is bound to create performance issues down the road as your data grows.
I wouldn't use logic inside my entities either (your categoriesToString method), but that is a different topic altogether.
You can either specify Eager loading on your entity as:
#OneToMany(fetch = FetchType.EAGER, mappedBy = "supplier")
private Set<Category> categories = new LinkedHashSet<>();
Or perform an additional query to fetch the related categories. Or even better, create a POJO or Spring Data Projection to fetch/map the appropriate data to. You can check this link for more info on that matter.

#Transactional does not work as expected, because the "save" method is needed to save to the database

#Transactionalshould itself reflect the changes made to the entity in the database.
I'm creating an application where the client can create a Car entity that looks like this (the update method is later used by PUT, do not pay attention to the brand property):
#Entity
#Table(name = "cars")
public class Car {
#Id
#GeneratedValue(generator = "inc")
#GenericGenerator(name = "inc", strategy = "increment")
private int id;
#NotBlank(message = "car name`s must be not empty")
private String name;
private LocalDateTime productionYear;
private boolean tested;
public Car() {
}
public Car(#NotBlank(message = "car name`s must be not empty") String name, LocalDateTime productionYear) {
this.name = name;
this.productionYear = productionYear;
}
#ManyToOne
#JoinColumn(name = "brand_id")
private Brand brand;
public int getId() {
return id;
}
void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public LocalDateTime getProductionYear() {
return productionYear;
}
public void setProductionYear(LocalDateTime productionYear) {
this.productionYear = productionYear;
}
public boolean isTested() {
return tested;
}
public void setTested(boolean tested) {
this.tested = tested;
}
public Brand getBrand() {
return brand;
}
void setBrand(Brand brand) {
this.brand = brand;
}
public Car update(final Car source) {
this.productionYear = source.productionYear;
this.brand = source.brand;
this.tested = source.tested;
this.name = source.name;
return this;
}
}
In my application, the client can create a new Car or update an existing one with the PUT method.
My controller:
#RestController
public class CarController {
private Logger logger = LoggerFactory.getLogger(CarController.class);
private CarRepository repository;
public CarController(CarRepository repository) {
this.repository = repository;
}
//The client can create a new resource or update an existing one via PUT
#Transactional
#PutMapping("/cars/{id}")
ResponseEntity<?> updateCar(#PathVariable int id, #Valid #RequestBody Car source) {
//update
if(repository.existsById(id)) {
repository.findById(id).ifPresent(car -> {
car.update(source); //it doesn`t work
//Snippet below works
//var updated = car.update(source);
//repository.save(updated);
});
return ResponseEntity.noContent().build();
}
//create
else {
var result = repository.save(source);
return ResponseEntity.created(URI.create("/" + id)).body(result);
}
}
}
When I create a new Car, it works. However as described in the code, when there is no save method the entity is not changed although I get the status 204 (no content). When there is a save method, it works fine.
Do you know why this is so?
One of the users asked me for a Brand entity. I haven't created any Brand object so far but essentially Car can belong to a specific Brand in my app. So far, no Car belongs to any Brand. Here is this entity:
#Entity
#Table(name = "brands")
public class Brand {
#Id
#GeneratedValue(generator = "i")
#GenericGenerator(name = "i", strategy = "increment")
private int id;
#NotBlank(message = "brand name`s must be not empty")
private String name;
private LocalDateTime productionBrandYear;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "brand")
private Set<Car> cars;
#ManyToOne
#JoinColumn(name = "factory_id")
private Factory factory;
public Brand() {
}
public int getId() {
return id;
}
void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public LocalDateTime getProductionBrandYear() {
return productionBrandYear;
}
public void setProductionBrandYear(LocalDateTime productionBrandYear) {
this.productionBrandYear = productionBrandYear;
}
public Set<Car> getCars() {
return cars;
}
public void setCars(Set<Car> cars) {
this.cars = cars;
}
public Factory getFactory() {
return factory;
}
public void setFactory(Factory factory) {
this.factory = factory;
}
}
I tried your entities with same use case locally and found out everything is working fine, I am writing here my findings and configurations so that you can verify what's going on wrong for you.
So, when I issue a PUT call providing id but Car entity doesn't exist into table, it gets created and I receive 201 response (I guess you are getting the same)
you can see that row with value got inserted into table as well
and these are the query logs printed
- [nio-8080-exec-8] org.hibernate.SQL: select count(*) as col_0_0_ from car car0_ where car0_.id=?
[nio-8080-exec-8] org.hibernate.SQL: select car0_.id as id1_1_0_, car0_.brand_id as brand_id5_1_0_, car0_.name as name2_1_0_, car0_.production_year as producti3_1_0_, car0_.tested as tested4_1_0_ from car car0_ where car0_.id=?
[nio-8080-exec-8] org.hibernate.SQL: insert into car (brand_id, name, production_year, tested) values (?, ?, ?, ?)
Now, let's come to updating the same entity, when issued PUT request for same id with changed values notice that values changes in table and update queries in log
You can see that got same 204 response with empty body, let's look the table entry
So changes got reflected in DB, let's look at the SQL logs for this operation
select count(*) as col_0_0_ from car car0_ where car0_.id=?
[nio-8080-exec-1] org.hibernate.SQL: select car0_.id as id1_1_0_, car0_.brand_id as brand_id5_1_0_, car0_.name as name2_1_0_, car0_.production_year as producti3_1_0_, car0_.tested as tested4_1_0_, brand1_.id as id1_0_1_, brand1_.name as name2_0_1_, brand1_.production_year as producti3_0_1_ from car car0_ left outer join brand brand1_ on car0_.brand_id=brand1_.id where car0_.id=?
[nio-8080-exec-1] org.hibernate.SQL: update car set brand_id=?, name=?, production_year=?, tested=? where id=?
So, I am not sure, how you verified and what you verified but your entities must work, I have used same controller function as yours
#RestController
class CarController {
private final CarRepository repository;
public CarController(CarRepository repository) {
this.repository = repository;
}
#PutMapping("/car/{id}")
#Transactional
public ResponseEntity<?> updateCar(#PathVariable Integer id, #RequestBody Car source) {
if(repository.existsById(id)) {
repository.findById(id).ifPresent(car -> car.update(source));
return ResponseEntity.noContent().build();
}else {
Car created = repository.save(source);
return ResponseEntity.created(URI.create("/" + created.getId())).body(created);
}
}
}
Possible differences from your source code could be as follow:
I used IDENTITY generator to generate the PRIMARY KEY, instead of the one you have on your entity as it was easy for me to test.
I provided ObjectMapper bean to serialize/deserialize the request body to Car object to support Java 8 LocalDateTime conversion, you may have your way to send datetime values, so that it converts to Car Object.
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
// And Object mapper bean
#Bean
public static ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
return mapper;
}
However, these differences should not matter.
application.properties
To print query logs to verify if queries are fired or not
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.username=test
spring.datasource.password=test
spring.datasource.jpa.show-sql=true
spring.jpa.open-in-view=false
logging.level.org.hibernate.SQL=DEBUG
The fact that you are updating the car object doesn't mean it updates the value in the DB. You always need to call repository.save() method to persist your changes in the DB.

Could not determine type for: org.json.JSONObject, at table: ordersinfo, for columns: [org.hibernate.mapping.Column(items)]

My entity class in springboot
Order.java:
package com.demo.orderservice.model;
import java.math.BigInteger;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import org.json.JSONObject;
#Entity
#Table(name = "ordersinfo")
public class Order {
#Id
#Column(columnDefinition = "bigint")
#GeneratedValue(strategy=GenerationType.AUTO)
private BigInteger orderId;
#Column(nullable = false)
private String userId;
#Column(columnDefinition = "jsonb",nullable = false)
private JSONObject items;
#Column(nullable = false)
private String branch_id;
#Column(precision=8, scale=2,nullable = false)
private float price;
#Column(columnDefinition = "varchar(255) default 'CASH'",nullable = false)
private String payment;
#Column(columnDefinition = "timestamptz default current_timestamp",nullable = false)
private String time;
public BigInteger getOrderId() {
return orderId;
}
public void setOrderId(BigInteger orderId) {
this.orderId = orderId;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public JSONObject getItems() {
return items;
}
public void setItems(JSONObject items) {
this.items = items;
}
public String getBranch_id() {
return branch_id;
}
public void setBranch_id(String branch_id) {
this.branch_id = branch_id;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public String getPayment() {
return payment;
}
public void setPayment(String payment) {
this.payment = payment;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
#Override
public String toString()
{
JSONObject obj = new JSONObject();
obj.put("orderid", orderId);
obj.put("userid", userId);
obj.put("items", items);
obj.put("branchid",branch_id);
obj.put("price", price);
obj.put("payment",payment);
obj.put("time",time);
return obj.toString();
}
}
When i am running my springboot application.I am getting the following error.How to resolve it?
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-08-06 14:30:34.254 ERROR 17024 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: Could not determine type for: org.json.JSONObject, at table: ordersinfo, for columns: [org.hibernate.mapping.Column(items)]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1778) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.1.8.RELEASE.jar:5.1.8.RELEASE]
I want an attribute item with jsonb data type in my postgres db.
#Column(columnDefinition = "jsonb",nullable = false)
private String items;
keeping this in my entity class does not make any problem but while displaying entity class object it is being represented as String,but i want it to be represented as JSON object only .
Exactly happened like this..
{
"orderId": 4,
"userId": "PN250023",
"items": "{\"items\": [{\"item_id\": \"ITM124\", \"quantity\": \"2\", \"unit_price\": \"120\"}, {\"item_id\": \"ITM126\", \"quantity\": \"1\", \"unit_price\": \"123\"}]}",
"branch_id": "BR123099",
"price": 363.0,
"payment": "CASH",
"time": "2019-08-06 11:14:54.51044+05:30"
}
How to solve this issue?
Another issue while using POST API is
org.postgresql.util.PSQLException: ERROR: column "items" is of type jsonb but expression is of type character varying
Hint: You will need to rewrite or cast the expression.
Position: 96
How to solve this issue?
Hibernate does not have OOB support for jsonb type in postgres.
You will have to implement a Hibernate UserType. Sadly, org.json.JSONObject isn't
Check out these links:
https://stackoverflow.com/a/37946530/1187254
https://thoughts-on-java.org/persist-postgresqls-jsonb-data-type-hibernate/

Unexpected RollBackException on merge in hibernate (RESOLVED)

I'm having UnexpectedRollBack exception on a simple merge method with hibernate.
I'm using JSP and controller structure.
it should be a simple update, but it returns an exception.
I have this:
User.java
public class user{
private Integer id;
String name;
String active;
}
#Id
#Column(name="ID", unique = true, nullable = false, etc...)
public Integer getId(){return this.id}
#Column(name = "NAME", nullable = true, length = 9)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
#Column(name = "ACTIVE", nullable = true, length = 9)
public String getActive() {
return this.active;
}
public void setActive(String active) {
this.active= active;
}
Controller:
Public class UpdateUserController{
//Here's the main issue
User userToUpdate = findUserById("1");
//user properly recovered from the DB
userToUpdate.setActive("N");
//UserService is the class that contains the merging method
userService.merge(userToUpdate);
System.out.println(userToUpdate.getActive());
}
service class:
#Transactional(propagation = Propagation.REQUIRED)
public user merge(User detachedInstance) {
try {
User result = entityManager.merge(detachedInstance);
return result;
} catch (RuntimeException re) {
throw re;
}
}
It's a simple update of a String, only one cel in a row, this shouldn't return an exception but it does.
I've seen on some posts that the problem may be caused by the method try to update an element that is currently null, that may cause hibernate returns RollBackException by default.
I've checked and there is nothing null at the moment of the merge.
Thank you for the help.
RESOLVED:
The element name in data base was varchar (5)
On my code was trying to update a name with 7 char long, this caused the RollbackException.

Hibernate NamedQuery, set values

Good evening! I am trying to set values from my query to wrapper class TestWrapper
TestWrapper class:
package com.bionic.wrappers;
public class TestWrapper {
private String name;
private int duration;
public TestWrapper(){
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getDuration() {
return duration;
}
public void setDuration(int duration) {
this.duration = duration;
}
}
Here is my query:
#NamedQuery(name = "getAvailableTestsNames",
query = "SELECT test.testName, test.duration FROM Result result JOIN result.test test JOIN result.user user where user.id = :userId"
and DAO class:
public List<TestWrapper> getAvailableTestsNames(long id){
Query query = em.createNamedQuery("getAvailableTestsNames");
query.setParameter("userId", id);
return (List<TestWrapper>)query.getResultList();
}
I get an exeption and i see that values won't set appropriate here:
public static Set<TestDTO> convertAvailableTestsToDTO(List<TestWrapper> tests){
Set<TestDTO> testDTOs = new HashSet<>();
for (TestWrapper test : tests){
TestDTO testDTO = new TestDTO(test.getName(), test.getDuration());
testDTOs.add(testDTO);
}
return testDTOs;
}
I get an expeption:
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to com.bionic.wrappers.TestWrapper
Thank you!
I don't have enough context but in the getAvailableTestsNames meth. looks like you're doing a query that returns scalar results by returning "test.testName, test.duration" where you probably just want to return a List of TestWrapper so the query should just be " from XXX" , you can omit the select field1,field2 ... hibernate does that for you.
See section 11.4.1.3. Scalar results of https://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html/ch11.html#objectstate-querying vs. 11.4.1. Executing queries
Hope this helps
Aa.

Categories