shadyyx
shadyyx

Reputation: 16055

Java spring boot rest service struggle

After few years I got back to Java to try to implement RESTful WS using Java. I have found plenty of tutorials and articles about implementing it using Spring and it's Spring boot extension on top of Maven. After first problems with setting all up I finally have a WS that is running and is accessible (e.g. from CLI using curl).

Unfortunately I am getting java.lang.IllegalArgumentException exceptions, with these two messages:

java.lang.IllegalArgumentException: Parameter with that position [1] did not exist

and

java.lang.IllegalArgumentException: Parameter with that name [cart_id] did not exist

Here's my shortened Entity class:

@Entity
@Table(name = "cart", catalog = "table1", schema = "")
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "Cart.findAll", query = "SELECT c FROM Cart c"),
    @NamedQuery(name = "Cart.findByCartId", query = "SELECT c FROM Cart c WHERE c.cartId = :cartId"),
    @NamedQuery(name = "Cart.findByBarcode", query = "SELECT c FROM Cart c WHERE c.barcode = :barcode"),
    @NamedQuery(name = "Cart.findByCartTypeId", query = "SELECT c FROM Cart c WHERE c.cartTypeId = :cartTypeId"),
    @NamedQuery(name = "Cart.findByCartStyleId", query = "SELECT c FROM Cart c WHERE c.cartStyleId = :cartStyleId"),
    @NamedQuery(name = "Cart.findByCartName", query = "SELECT c FROM Cart c WHERE c.cartName = :cartName"),
    @NamedQuery(name = "Cart.findByNotificationId", query = "SELECT c FROM Cart c WHERE c.notificationId = :notificationId"),
    @NamedQuery(name = "Cart.findByCartContentId", query = "SELECT c FROM Cart c WHERE c.cartContentId = :cartContentId"),
    @NamedQuery(name = "Cart.findByStationId", query = "SELECT c FROM Cart c WHERE c.stationId = :stationId"),
    @NamedQuery(name = "Cart.findByUserId", query = "SELECT c FROM Cart c WHERE c.userId = :userId"),
    @NamedQuery(name = "Cart.findByStatus", query = "SELECT c FROM Cart c WHERE c.status = :status")})
public class Cart implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Basic(optional = false)
    @Column(name = "cart_id")
    private Long cartId;

    @Size(max = 50)
    private String barcode;

    @Column(name = "notification_id")
    private Integer notificationId;

    // other properties, getters and setters follow ...
}

Here the repository:

@RepositoryRestResource
public interface CartDAO extends CrudRepository<Cart, Long> {

    /**
     * Returns cart by it's ID.
     * @param cartId
     * @return Cart
     */
    public Cart findByCartId(@Param("cart_id") Long cartId);

    /**
     * Returns cart by it's barcode (EAN).
     * @param barcode
     * @return Cart
     */
    public Cart findByBarcode(String barcode);

    /**
     * Returns all the carts belonging to notification (by notification ID).
     * @param notificationId
     * @return List of Carts
     */
    public List<Cart> findByNotificationId(@Param("notification_id") Integer notificationId);

}

And here's the controller:

@RestController
@RequestMapping("/carts")
public class CartController {

    @Autowired
    private CartDAO cartDao;

    @RequestMapping(value="/id/{cartId}", method = RequestMethod.GET)
    public Cart getCartById(@PathVariable Long cartId) {
        return cartDao.findByCartId(cartId);
//        return cartDao.findOne(cartId);
    }

    @RequestMapping(value="/ean/{barcode}", method = RequestMethod.GET)
    public Cart getCartByBarcode(@PathVariable String barcode) {
        return cartDao.findByBarcode(barcode);
    }

    @RequestMapping(value="/notification/{notificationId}", method = RequestMethod.GET)
    public List<Cart> getCartByNotification(@PathVariable Integer notificationId) {
        return cartDao.findByNotificationId(notificationId);
    }
}

If I call the WS with cmd curl -g localhost:8080/carts/id/1 or curl -g localhost:8080/carts/notification/1 I got the exception with the second msg (for name [cart_id] or [notification_id]). If I call the cmd curl -g localhost:8080/carts/ean/2550000017884 I got the exception with the first msg (for position [1]).

The DB table looks like:

cart
===========================================
cart_id            INT (PK, auto_increment)
notification_id    INT (FK)
barcode            VARCHAR(13) (NOT NULL, UNIQUE)
...

However if I switch comment for the two lines in CartController.getCartById() (i.e. let return cartDao.findOne(cartId); to be invoked instead of return cartDao.findByCartId(cartId);), then I finally can see a valid response without exception.

But what am I doing wrong here? Obviously I am missing something (and all the tutorials as well).

PS: I have created all the entities using the persistance unit - I have let the PU to connect to my DB and to create all the entities for me. I liked to work this way back in the days as PU can handle very quickly changes on both sides and mirror them on the other. With the help of PU I also have lot of @NamedQueries which could be of help but since all the spring-boot tutorials are working with JPA repository I don't know either how to call those named queries from Persistence...

EDIT: adding the dependencies part of my pom.xml:

<dependencies>
    <!-- Get the dependencies of a web application -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- DB -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-rest</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-commons</artifactId>
        <type>jar</type>
    </dependency>
</dependencies>

Upvotes: 1

Views: 549

Answers (1)

M. Deinum
M. Deinum

Reputation: 124441

The fact that the @Param works (and not giving an error) indicates that a pre defined query is executed. Without a query the @Param would fail.

Spring Data uses a strategy to determine which query to execute by default it uses CREATE_IF_NOT_FOUND lookup strategy. It first tries to find a pre-defined query, this can be either a @Query on the method or a @NamedQuery for JPA (in the latter case it is looking for a <entity-name>.<method-name> named query). If neither of these applies it will create a query based on the method.

In your case you have a named query with the name Cart.findByCartId (and the same applies to the other methods as well). Spring Data detects this query and will execute it. Due to your @Param it tries to find a parameter named cart_id but the name is actually cartId (see the named query).

To solve your issue either 1. Fix the @Param annotations in your method to point to the correct parameter names 2. Remove the @Param and @NamedQuerys 3. Switch Spring Data JPA to the CREATE lookup strategy and remove the @Param.

Upvotes: 2

Related Questions