Dropwizard:"error processing your request" for retriving data from database - java

When i tries to open
http://localhost:8080/tailor/orders
which should return all the orders in database.but it's generating error
{"message":"There was an error processing your request. It has been logged (ID fe49a13e76c59894)."}
I'm unable to trace the problem what's causing this.
I'm using dropwizard for restful web service and hibernate from dropwizard for sqlight database.
Class:
Resources class:
#Path("/tailor")
#Produces(MediaType.APPLICATION_JSON)
public class TailorResource {
OrderDAO orderdao;
public TailorResource(OrderDAO odao) {
this.orderdao = odao;
}
#GET
#Path("/orders")
#Produces(MediaType.APPLICATION_JSON)
public List<OrderModel> getAllOrders() {
return orderdao.findAll();
}
#GET
#Path("/orders/{id}")
#Produces(MediaType.APPLICATION_JSON)
public OrderModel getOrderById(#PathParam("id") int id) {
return orderdao.findById(id);
}
}
OrderDAO class:
public class OrderDAO extends AbstractDAO<OrderModel>{
public OrderDAO(SessionFactory sessionFactory) {
super(sessionFactory);
}
public OrderModel findById(int id) {
return get(id);
}
public OrderModel create(OrderModel o) {
return persist(o);
}
public List<OrderModel> findAll() {
return list(namedQuery("com.yammer.dropwizard.tailor.model.OrderModel.findAll"));
}}
Order Class:
#NamedQueries({
#NamedQuery(
name = "com.yammer.dropwizard.tailor.model.OrderModel.findAll",
query = "SELECT o FROM OrderModel o"
),
#NamedQuery(
name = "com.yammer.dropwizard.tailor.model.OrderModel.findById",
query = "SELECT o FROM OrderModel o WHERE o.ID = :ID"
)
})
#Entity
#Table(name = "Order")
public class OrderModel {
#Id
#GeneratedValue
#Column(name = "o_id")
int ID;
#Column(name = "o_shirt_quantity")
int shirtQuantity;
#Column(name = "o_longshirt_quantity")
int longshirtQuantity;
#Column(name = "o_trouser_quantity")
int trouserQuantity;
#Column(name = "o_coat_quantity")
int coatQuantity;
#Column(name = "o_deliverydate")
Date deliveryDate;
#Column(name = "o_orderdate")
Date orderDate;
#Column(name = "o_shirt_price")
Double shirtPrice;
#Column(name = "o_longshirt_price")
Double longshirtPrice;
#Column(name = "o_trouser_price")
Double trouserPrice;
#Column(name = "o_coat_price")
Double coatPrice;
#Column(name = "o_totalamount")
Double totalAmount;
#Column(name = "o_discount")
Double discount;
#Column(name = "o_advancedpayment")
Double advancedPayment;
#Column(name = "o_remainingpayment")
Double remainingPayment;
#Column(name = "o_orderstatus")
int orderStatus;
}
Database configuration class:
public class databaseConfiguration extends Configuration {
#Valid
#NotNull
#JsonProperty
DataSourceFactory dbconfigurations = new DataSourceFactory();
public DataSourceFactory getDataSourceFactory() {
//return dbconfigurations;
Map<String,String> s=new HashMap<String,String>();
s.put("hibernate.dialect","Hibernate.SQLightDialect.SQLiteDialect");
dbconfigurations.setProperties(s);
return dbconfigurations;
}
}
Main service Class:
public class TailorApplication extends Application<databaseConfiguration> {
public static void main(String[] args) throws Exception {
new TailorApplication().run(args);
}
private final HibernateBundle<databaseConfiguration> hibernate = new HibernateBundle<databaseConfiguration>(CustomerModel.class) {
{
#Override
public DataSourceFactory getDataSourceFactory(databaseConfiguration configuration) {
return configuration.getDataSourceFactory();
}
};
#Override
public void initialize(Bootstrap<databaseConfiguration> bootstrap) {
// TODO Auto-generated method stub
bootstrap.addBundle(hibernate);
}
#Override
public void run(databaseConfiguration configuration, Environment environment)
throws Exception {
final OrderDAO odao = new OrderDAO(hibernate.getSessionFactory());
environment.jersey().register(new TailorResource(odao));
}
}
YML file:
dbconfigurations:
# the name of your JDBC driver
driverClass: org.sqlite.JDBC
# the username
user:
# the password
password:
url: jdbc:sqlite:TailorDB.db
Help please?

Make sure you filled the application class with the entities:
private final HibernateBundle<AppConfiguration> hibernateBundle = new HibernateBundle<AppConfiguration>(
//
//
//ADD ENTITIES HERE
//
//
Person.class
,Product.class
) {
#Override
public DataSourceFactory getDataSourceFactory(
AppConfiguration configuration) {
return configuration.getDataSourceFactory();
}
};

On the face of it, looks like you have missed the #UnitOfWork annotation on getAllOrders.
Having said that, the error you have shared is the external messaging that DW provides by default. Instead you should look at your web service logs for the exact error and precise stacktrace. If you run your service in terminal with java -jar path/to/shaded.jar server my.yml you should see elaborate error on the console. Please share that so that community can help better.

Related

MongoDB repository save() do the insert instead of update

I am writing a PUT request API with spring and mongodb. But the save() inserts a new object instead of update the current one.
#Document("Test")
public class Expense {
#Field(name = "name")
private String expenseName;
#Field(name = "category")
private ExpenseCategory expenseCategory;
#Field(name = "amount")
private BigDecimal expenseAmount;
public Expense( String expenseName, ExpenseCategory expenseCategory, BigDecimal expenseAmount) {
this.expenseName = expenseName;
this.expenseCategory = expenseCategory;
this.expenseAmount = expenseAmount;
}
public String getExpenseName() {
return expenseName;
}
public void setExpenseName(String expenseName) {
this.expenseName = expenseName;
}
public ExpenseCategory getExpenseCategory() {
return expenseCategory;
}
public void setExpenseCategory(ExpenseCategory expenseCategory) {
this.expenseCategory = expenseCategory;
}
public BigDecimal getExpenseAmount() {
return expenseAmount;
}
public void setExpenseAmount(BigDecimal expenseAmount) {
this.expenseAmount = expenseAmount;
}
}
This is my reporsitory class
public interface ExpenseRepository extends MongoRepository<Expense, String> {
}
This is my Service class which shows how to update the class.
#Service
public class ExpenseService {
private final ExpenseRepository expenseRepository;
public ExpenseService(ExpenseRepository expenseRepository) {
this.expenseRepository = expenseRepository;
}
public void updateExpense(String id, Expense expense){
Expense savedExpense = expenseRepository.findById(id)
.orElseThrow(() -> new RuntimeException(
String.format("Cannot Find Expense by ID %s", id)));
savedExpense.setExpenseName(expense.getExpenseName());
savedExpense.setExpenseAmount(expense.getExpenseAmount());
savedExpense.setExpenseCategory(expense.getExpenseCategory());
expenseRepository.save(savedExpense);
}
}
This is my controller
#RestController
#RequestMapping("/api/expense")
public class ExpenseController {
private final ExpenseService expenseService;
public ExpenseController(ExpenseService expenseService) {
this.expenseService = expenseService;
}
#PutMapping("/{id}")
public ResponseEntity<Object> updateExpense(#PathVariable String id, #RequestBody Expense expense){
expenseService.updateExpense(id, expense);
return ResponseEntity.ok().build();
}
}
As shown in mongodb compass, mongodb auto generates an _id field for every object. So I do not define a id field or use #id annotation to define a primary for the collection. However, in the service class, expenseRepository.findById(id) retrieves the desired object and update it. Why does save() do the insert instead of update? Many thanks.
JPA Can't find the existing entry as no id field id set. You need to add an id field and set generation type to auto.
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;

Trigger #OneToMany lazy fetch

I have an ExampleRequest entity that can optionally have one or more ExampleRequestYear. It's currently configured this way (unrelated fields and gettters/setters omitted for brevity, please let me know if you need anything else):
#Entity
#Table(name = "EXAMPLE_REQUEST")
#SequenceGenerator(name = "EXAMPLE_REQUEST_ID_SEQ", sequenceName = "EXAMPLE_REQUEST_ID_SEQ", allocationSize = 1)
#Cacheable(false)
public class ExampleRequest implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "EXAMPLE_REQUEST_ID_SEQ")
#Column(name="EXAMPLE_REQUEST_ID", nullable = false)
private Long exampleRequestId;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "exampleRequest")
private List<ExampleRequestYear> exampleRequestYearList;
public ExampleRequest() {
}
public List<ExampleRequestYear> getExampleRequestYearList() {
if(this.exampleRequestYearList == null){
this.exampleRequestYearList = new ArrayList<ExampleRequestYear>();
}
return this.exampleRequestYearList;
}
public void setExampleRequestYearList(List<ExampleRequestYear> exampleRequestYearList) {
this.exampleRequestYearList = exampleRequestYearList;
}
public ExampleRequestYear addExampleRequestYear(ExampleRequestYear exampleRequestYear) {
getExampleRequestYearList().add(exampleRequestYear);
exampleRequestYear.setExampleRequest(this);
return exampleRequestYear;
}
public ExampleRequestYear removeExampleRequestYear(ExampleRequestYear exampleRequestYear) {
getExampleRequestYearList().remove(exampleRequestYear);
exampleRequestYear.setExampleRequest(null);
return exampleRequestYear;
}
}
#Entity
#Table(name = "EXAMPLE_REQUEST_YEAR")
#IdClass(ExampleRequestYearPK.class)
public class ExampleRequestYear implements Serializable {
#Id
#Column(nullable = false)
private Integer year;
#Id
#ManyToOne
#JoinColumn(name = "EXAMPLE_REQUEST_ID", referencedColumnName = "EXAMPLE_REQUEST_ID")
private ExampleRequest exampleRequest;
public ExampleRequestYear() {
}
public void setExampleRequest(ExampleRequest exampleRequest) {
this.exampleRequest = exampleRequest;
}
public ExampleRequest getExampleRequest() {
return exampleRequest;
}
}
Part of the code was auto-generated by the IDE and I'm still wrapping my head around JPA so there're probably design mistakes all around.
My app works (apparently) when I create a new ExampleRequest:
ExampleRequest exampleRequest = new ExampleRequest();
ExampleRequestYear exampleRequestYear = new ExampleRequestYear(2020);
request.addExampleRequestYear(exampleRequestYear);
However, I can't figure out how to edit an existing ExampleRequest because I'm unsure on how I'm meant to retrieve the linked entities. According to articles I've read, lazy fetching should be automatic, yet when I try this:
ExampleRequest exampleRequest = employeeRequestsController.getExampleRequestById(123);
System.out.println(exampleRequest.getExampleRequestYearList().size());
... I get a null pointer exception upon .size() because the getter runs but neither initialises an empty list, nor retrieves items from DB:
public List<ExampleRequestYear> getExampleRequestYearList() {
if(this.exampleRequestYearList == null){
// Field is null and conditional is entered
this.exampleRequestYearList = new ArrayList<ExampleRequestYear>();
// After initialisation, field is still null!
}
return this.exampleRequestYearList;
}
Also, switch to FetchType.EAGER solves this particular problem entirely. What am I missing?
Further details regarding app design. The Resource classes that handle HTTP requests interact with a set of Controller classes like this:
#Stateless(name = "ISomeActionController", mappedName = "ISomeActionController")
public class SomeActionController implements ISomeActionController {
#EJB
private IFooDAO fooDao;
#EJB
private IBarDAO barDao;
#Override
public ExampleRequest getExampleRequestById(Long exampleRequestId) {
return fooDao.getEntityById(exampleRequestId);
}
}
It's in the DAO classes where EntityManager is injected an used:
#Local
public interface IGenericDAO<T> {
public T persistEntity(T o);
public T persistEntityCommit(T o);
public void removeEntity(T o);
public void removeEntity(long id);
public T mergeEntity(T o);
public List<T> getEntitiesFindAll();
public List<T> getEntitiesFindAllActive();
public T getEntityById(Object id);
}
public interface IFooDAO extends IGenericDAO<ExampleRequest> {
public void flushDAO();
public ExampleRequest getExampleRequestById(Long exampleRequestId);
}
#Stateless(name = "IFooDAO", mappedName = "IFooDAO")
public class FooDAO extends GenericDAO<ExampleRequest> implements IFooDAO {
public FooDAO() {
super(ExampleRequest.class);
}
#Override
public void flushDAO(){
em.flush();
}
#Override
public ExampleRequest getExampleRequestById(Long exampleRequestId){
String sql = "...";
Query query = em.createNativeQuery(sql, ExampleRequest.class);
//...
}
}

How to send only the ID the of main nested objects in the body request in spring boot

I'm creating eCommerce for merchants using spring boot with JPA.
I have an issue while creating the order service.
I want to only pass the ID of the nested objects in the request body instead of sending the full nest objects because the size will be extremely big.
Here is my code.
Merchant can do many orders
Order
#Entity
#Table(name = "Orders")
#XmlRootElement
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Order extends BasicModelWithIDInt {
#Basic(optional = false)
#Column(name = "Quantity")
private Integer quantity;
#Basic(optional = false)
#Size(min = 1, max = 150)
#Column(name = "Notes")
private String notes;
#JoinColumn(name = "ProductID", referencedColumnName = "ID")
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JsonIgnoreProperties
private Product productID;
#JoinColumn(name = "MerchantID", referencedColumnName = "ID")
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Merchent merchent;
#JoinColumn(name = "OrderSatusID", referencedColumnName = "ID")
#ManyToOne(optional = false, fetch = FetchType.EAGER)
private OrderStatus orderStatus;
// Getters and Setters
}
Order Holder
public class OrderHolder {
#NotNull
private Order order;
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
}
OrderRepo
public interface OrderRepo extends JpaRepository<Order, Integer> {
}
Order Controller
#RestController
#RequestMapping(value = "order", produces = MediaType.APPLICATION_JSON_VALUE)
public class OrderRestController extends BasicController<OrderHolder>{
#Autowired
private OrderRepo orderRepo;
#PostMapping("create")
public ResponseEntity<?> create(#RequestBody #Valid OrderHolder orderHolder, Principal principal) throws GeneralException {
log.debug( "create order {} requested", orderHolder.toString());
Order order = new Order();
order = orderHolder.getOrder();
System.out.println("###############"+order);
try {
order = orderRepo.save(order);
log.info( "Order {} has been created", order );
} catch (Exception e) {
log.error( "Error creating Order: ", e );
e.printStackTrace();
throw new GeneralException( Errors.ORDER_CREATION_FAILURE, e.toString() );
}
return ResponseEntity.ok( order );
}
}
I need request body to look like the below instead of including the full Merchant and Product objects inside the request.
You can make use of JsonView to return only id of product and merchant
public class OrderView {}
...
public class Product{
#Id
#JsonView(OrderView.class)
private Integer id
private String otherFieldWithoutJsonView
...
}
and then in your controller
#PostMapping("create")
#JsonView(OrderView.class) // this will return the product object with one field (id)
public ResponseEntity<?> create(#RequestBody #Valid OrderHolder orderHolder, Principal principal) throws GeneralException {
...
}
hope this can help you
Just have a separate contract class.
public class OrderContract {
private int merchantID;
private String notes;
....
//getter, setters
}
public class OrderHolder {
#NotNull
private OrderContract orderContract;
public OrderContract getOrderContract() {
return orderContract;
}
public void setOrder(OrderContract orderContract) {
this.orderContract = orderContract;
}
}
And before making a call to the Repository , translate from OrderContract to Order.
I would like to share something regarding this.
I have searched a lot on internet and tried lot of things, but the solution given here suited well for this scenario.
https://www.baeldung.com/jackson-deserialization
You need to create a Custom-deserializer for your model by extending StdDeserializer from com.fasterxml.jackson.databind.deser.std.StdDeserializer, where you just want to pass id's and not the whole object in the request.
I have given below example for User Model with Address object.
User(long userId, String name, Address addressId)
Address(long addressId, String wholeAddress)
Writing Deserializer for User class
public class UserDeserializer extends StdDeserializer<User> {
public User() {
this(null);
}
public User Deserializer(Class<?> vc) {
super(vc);
}
#Override
public User deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
JsonNode node = p.getCodec().readTree(p);
long id = 0;
long addressId = (Long) ((IntNode) node.get("addressId")).numberValue().longValue();
return new User(id, name, new Address(addressId, null)
}
Now you have to use
#JsonDeserialize(using = UserDeserializer.class)
public Class User {
...
}
POST request
Before custom deserialization
{
"name" : "Ravi",
"addressId" : { "id" : 1}
}
After custom Deserialization
{
"name" : "Ravi",
"addressId" : 1
}
Also while GET /user/:id call you will get the whole obj like
{
"name" : "Ravi",
"addressId" : { "id" : 1, "wholeAddress" : "Some address"}
}

Cannot POST A COLUMN RESTFUL API

I have made restful API Using java hibernate jersery Framework.
I have to post data I have done it but I'm missing with one of the column that is MealTypeName.
Here is my DAO Class:
public class MealTypeDAO {
public void addMealType( MealType bean) {
Session session = SessionUtil.getSession();
Transaction tx = session.beginTransaction();
addMealType(session, bean);
tx.commit();
session.close();
}
private void addMealType(Session session, MealType bean){
MealType mealType = new MealType();
mealType.setMealTypename(bean.getMealTypename());
mealType.setModifiedon(bean.getModifiedon());
mealType.setModifiedby(bean.getModifiedby());
session.save(mealType);
}
Here is my resource class:
public class MealTypeResource {
#POST
#Path("/create")
#Consumes("application/json")
public Response addMealType(MealType meal){
meal.setMealTypename(meal.getMealTypename());
meal.setModifiedon(meal.getModifiedon());
meal.setModifiedby(meal.getModifiedby());
MealTypeDAO dao = new MealTypeDAO();
dao.addMealType(meal);
return Response.ok().build();
}
#GET
#Produces("application/json")
public Response getMealType() {
MealTypeDAO dao = new MealTypeDAO();
List mealTypes = dao.getMealType();
String json = new Gson().toJson(mealTypes);
return Response.ok().entity(json.toString()).build();
}
This is my entity class:
public class MealType {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int MealTypeId;
#Column
private String MealTypename;
#Column
private int modifiedby;
#Column
private String modifiedon;
public int getMealTypeId() {
return MealTypeId;
}
public void setMealTypeId(int mealTypeId) {
MealTypeId = mealTypeId;
}
public String getMealTypename() {
return MealTypename;
}
public void setMealTypename(String mealTypename) {
MealTypename = mealTypename;
}
public int getModifiedby() {
return modifiedby;
}
public void setModifiedby(int modifiedby) {
this.modifiedby = modifiedby;
}
public String getModifiedon() {
return modifiedon;
}
public void setModifiedon(String modifiedon) {
this.modifiedon = modifiedon;
}
MySQL DB:
CREATE TABLE `mealtype`(`Mealtypeid` int(11) NOT NULL AUTO_INCREMENT,`MealTypename` varchar(20) DEFAULT NULL,`modifiedby` int(11) NOT NULL,`modifiedon` datetime NOT NULL,PRIMARY KEY (`Mealtypeid`)) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
Now M posting these data in JSON FORMAT From POSTMAN:
{"MealTypeId":14,"MealTypename":"adsdf","modifiedby":1,"modifiedon":"2000-01-01 00:00:00"}
And M getting these data:
{"MealTypeId":14,"modifiedby":1,"modifiedon":"2000-01-01 00:00:00"}
MealTypename is missing. How so? Can someone help me out?
You are using names with the first letter in the upper case MealTypename — this is a reason.
The getter with name getMealTypename is used for a JSON property mealTypename (not MealTypename):
public String getMealTypename() {
return MealTypename;
}
You need to specify a JSON property name:
#JsonProperty("MealTypename") — for Jackson
#SerializedName("MealTypename") — for Gson
You need to put this annotation to the field or getter of the class which you mapping to JSON (MealType).
And use the standard Java naming convention.
public class MealType {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int mealTypeId;
#Column
private String mealTypename;
}
And this looks really strange:
meal.setMealTypename(meal.getMealTypename());
meal.setModifiedon(meal.getModifiedon());
meal.setModifiedby(meal.getModifiedby());

Is this a good practise to check for DUPLICATE value with spring-boot, CrudRepository, MySQL

I have this repository:
public interface AccountsRepository extends CrudRepository<Account, Long> {
}
Then I have this service class:
public class AccountService {
private AccountsRepository accountsRepository;
public AccountService(AccountsRepository accountsRepository) {
this.accountsRepository = accountsRepository;
}
public Account createNewAccount() {
Account account = new Account();
account = tryStoreAccountElseRepeatWithDifferentIdentifier(account);
return account;
}
private Account tryStoreAccountElseRepeatWithDifferentIdentifier(Account account) {
account.setIdentifier(IdentifierGenerator.generateString(6));
try {
return accountsRepository.save(account);
} catch (DataIntegrityViolationException e) {
return tryStoreAccountElseRepeatWithDifferentIdentifier(account);
}
}
}
Unit test:
public class AccountServiceUnitTests {
AccountService fixture;
AccountsRepository mockAccountRespository;
#Before
public void setup() {
mockAccountRespository = mock(AccountsRepository.class);
fixture = new AccountService(mockAccountRespository);
}
#Test
public void repeatCreateAccountWhenIdentifierIsDuplicateValue() {
Account account = new Account();
account.setId(123L);
account.setIdentifier("ABCDEF");
when(mockAccountRespository.save(any(Account.class)))
.thenThrow(DataIntegrityViolationException.class)
.thenThrow(DataIntegrityViolationException.class)
.thenThrow(DataIntegrityViolationException.class)
.thenReturn(account);
Account newAccount = fixture.createNewAccount();
assertEquals(account, newAccount);
}
}
Entity:
#Entity
#Table(name = "accounts")
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "identifier", unique = true)
private String identifier;
#Column(name = "username", unique = true)
private String username;
// getter, setter shortened
}
Thing is, I want to store a new Account into the database. Some columns have index UNIQUE. So when you try to insert data MySQL throws an exception if there is a DUPLICATE value.
Since the save(Entitiy) method does not throw a documented exception I tried what would happen and saw that a DataIntegrityViolationException is thrown in case I try to add duplicate value.
So my idea was basically try recursively to insert a new row till no exception is thrown. See: tryStoreAccountElseRepeatWithDifferentIdentifier
Is this way of checking for duplicate value good practise? Or is there a "built-in" solution I don't know?

Categories