简体   繁体   中英

Spring boot 3 Questions: Why it can't to call API with Postman and Frontend call API then get empty array?

I created an API, it's called search stores feature. .

MySql Script

When I Use this SQL Script runs in MySql. It will show the valid result

SET @productName = null;
SET @BranchName = null;
SET @minPrice = 0;
SET @maxPrice = 10000;
SET @quantity = 1000;

SELECT * FROM stores s 
INNER JOIN products p 
ON p.id = s.product_id 
INNER JOIN branches b 
ON b.id = s.branch_id 
WHERE (p.name LIKE CONCAT('%', @productName , '%') OR @productName IS NULL) 
  AND (b.name LIKE  CONCAT('%', @branchName , '%')  OR @branchName IS NULL) 
  AND (@minPrice <= p.price AND p.price <= @maxPrice) 
  AND s.quantity <= @quantity 
ORDER BY p.name, b.name, quantity;

.

Click image to see the result of MySQL

MySQL and valid result image

Log from StoreService.java and Hibernate

Click to see Postman before call API

When I call it by Frontend it happens the log in StoreService.java is...

productName: null, minPrice: 0.0, maxPrice: 15000.0, branchName: null, 1000
Hibernate: SELECT * FROM stores s INNER JOIN products p ON p.id = s.product_id INNER JOIN branches b ON b.id = s.branch_id WHERE (p.name LIKE CONCAT('%', ? , '%') OR ? IS NULL)  AND (b.name LIKE  CONCAT('%', ? , '%')  OR ? IS NULL)  AND (? <= p.price AND p.price <= ?)  AND s.quantity <= ?  ORDER BY p.name
Stores: []

.

Error Log when I call API by Postman

Postman Params image
When call it by Postman will get

java.lang.StackOverflowError: null
    at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:944) ~[mysql-connector-java-8.0.19.jar:8.0.19]
    at com.mysql.cj.jdbc.ClientPreparedStatement.executeQuery(ClientPreparedStatement.java:1003) ~[mysql-connector-java-8.0.19.jar:8.0.19]
    at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52) ~[HikariCP-3.4.2.jar:na]
    at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeQuery(HikariProxyPreparedStatement.java) ~[HikariCP-3.4.2.jar:na]
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:57) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]
    at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.getResultSet(AbstractLoadPlanBasedLoader.java:390) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]
    at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeQueryStatement(AbstractLoadPlanBasedLoader.java:163) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]
    at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:104) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]
    at org.hibernate.loader.collection.plan.AbstractLoadPlanBasedCollectionInitializer.initialize(AbstractLoadPlanBasedCollectionInitializer.java:87) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]
    at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:707) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]
    at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:76) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]
    at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:108) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]
    at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:2145) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]
    at org.hibernate.collection.internal.AbstractPersistentCollection$4.doWork(AbstractPersistentCollection.java:589) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]
    at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:264) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]
    at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]
    at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]
    at org.hibernate.collection.internal.PersistentSet.hashCode(PersistentSet.java:458) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]
    at com.g_able.back_g_exam.entity.Product.hashCode(Product.java:12) ~[classes/:na]
    at com.g_able.back_g_exam.entity.Store.hashCode(Store.java:13) ~[classes/:na]
    at java.util.HashMap.hash(HashMap.java:339) ~[na:1.8.0_172]
    at java.util.HashMap.put(HashMap.java:612) ~[na:1.8.0_172]
    at java.util.HashSet.add(HashSet.java:220) ~[na:1.8.0_172]
    at java.util.AbstractCollection.addAll(AbstractCollection.java:344) ~[na:1.8.0_172]
    at org.hibernate.collection.internal.PersistentSet.endRead(PersistentSet.java:355) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]
    at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollection(CollectionLoadContext.java:239) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]
    at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:224) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]
    at org.hibernate.engine.loading.internal.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:198) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]
    at org.hibernate.loader.plan.exec.process.internal.CollectionReferenceInitializerImpl.endLoading(CollectionReferenceInitializerImpl.java:154) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]
    at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.finishLoadingCollections(AbstractRowReader.java:267) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]
    at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.finishUp(AbstractRowReader.java:218) ~[hibernate-core-5.4.10.Final.jar:5.4.10.Final]

.

My Springboot code:

StoreController.java

@GetMapping("/api/v1/stores/advance-search")
  public List<Store> findStoresByFilter(
      @RequestParam(required = false) String productName, @RequestParam(required = false) String branchName,
      @RequestParam(required = false, defaultValue = "0") Integer minPrice,
      @RequestParam(required = false, defaultValue = "999999") Integer maxPrice,
      @RequestParam(required = false, defaultValue = "3000") Integer quantity) {

    ProductParams productParams = ProductParams.builder().name(productName).minPrice(minPrice).maxPrice(maxPrice).build();
    Branch branch = Branch.builder().name(branchName).build();

    List<Store> stores = storeService.findStoresByFilter(productParams, branch, quantity);
    System.out.println("Stores: " + stores);
    return stores;

}

.

StoreService.java

public List<Store> findStoresByFilter(ProductParams productParams, Branch branch, int quantity) {
    System.out.println("productName: " + productParams.getName() + ", minPrice: " + productParams.getMinPrice() + ", maxPrice: "
        + productParams.getMaxPrice() + ", branchName: " + branch.getName() + ", " + quantity);
    return storeRepository.searchStoresByFilter(productParams.getName(), branch.getName(),
        productParams.getMinPrice(), productParams.getMaxPrice(), quantity);
}

.

StoreRepository.java

@Query(nativeQuery = true,
      value = "SELECT * " +
          "FROM stores s " +
          "INNER JOIN products p " +
          "ON p.id = s.product_id " +
          "INNER JOIN branches b " +
          "ON b.id = s.branch_id " +
          "WHERE (p.name LIKE CONCAT('%', :productName , '%') OR :productName IS NULL) " +
          " AND (b.name LIKE  CONCAT('%', :branchName , '%')  OR :branchName IS NULL) " +
          " AND (:minPrice <= p.price AND p.price <= :maxPrice) " +
          " AND s.quantity <= :quantity " +
          " ORDER BY p.name")
  List<Store> searchStoresByFilter(@Param("productName") String productName, @Param("branchName") String branchName,
                                 @Param("minPrice") double minPrice, @Param("maxPrice") double maxPrice, @Param("quantity") int quantity);

Notice:

I am being flagged in my POJO classes that they have warnings about @Table and @JoinColumn from javax.persistence package

Cannot resolve table 'table_name' in @Table and Cannot resolve column 'product_id'
Cannot resolve column 'branch_id' in @JoinColumn

Store.java (Pojo)

Product.java (Pojo)

Branch.java (Pojo)

I want to know the root causes

  1. Why can I not get a valid result when I call API from Frontend?
  2. Why can I not call API from Postman?
  3. Why Annotation from javax.persistenct cannot resolve table_name and column_name ?

Finally I need solutions to fix these problems

Thanks a lot for all your help

I am being flagged in my Entity classes that they have warnings about @Table from javax.persistence Cannot resolve table 'table_name' in @Table and Cannot resolve column 'product_id' Cannot resolve column 'branch_id' in @JoinColumn

That's the reason hibernate cannot actually map table, you need to fix mapping before anything else, so hibernate knows which table to use. Currently its looking for 'table_name' in sql which is not your store or product table. We do not know if your mappings are correct as your entity class code is not shared here, but it's likely that annotation or xml file has wrong mapping name from the error itself, ultimately crashing whole request.

Edit: kindly check if these libraries are included in your classpath : hibernate-annotations.jar lib/hibernate-comons-annotations.jar lib/ejb3-persistence.jar

Also I'd suggest you to use hibernate query language instead of native queries as they are much more extensible when you want to switch underlying database.

Root cause is, hibernate is database abstraction layer aka DBAL, whole purpose of this library is to build queries on behalf of your code and execute them. You are trying to use it as native sql wrapper, and sql queries would give result to hibernate but since mapping is wrong it cannot parse and store data in your Entities, while it's useful in some cases, hibernate itself provides much more roburst functionalities such as lazy loading, entity relations to name a few.

You can read full documentation here to use such functionalities. Getting started guide

Hibernate Query Language Refrence

I already found how to fixed Postman cannot finish this API and Frontend get an empty array.

I just add @ToString(exclude = "stores") and @EqualsAndHashCode(exclude = "stores") into Entity who have association as Many to Many .

The current code in entities are:

Product.java

@Data
@ToString(exclude = "stores")
@EqualsAndHashCode(exclude = "stores")
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "products")
@Builder(toBuilder = true)
public class Product {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  @Column(nullable = false, length = 50)
  private String name;

  private String detail;

  private double price;

  @JsonIgnore
  @OneToMany(mappedBy = "product")
  private Set<Store> stores;

}

Branch.java

@Data
@ToString(exclude = "stores")
@EqualsAndHashCode(exclude = "stores")
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "branches")
@Builder(toBuilder = true)
public class Branch {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  @Column(nullable = false, length = 60)
  private String name;

  @JsonIgnore
  @OneToMany(mappedBy = "branch")
  private Set<Store> stores;
}

But I don't understand why it errors yet. Could you explain it ?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM