I created Entity class:
package order;
import javax.persistence.*;
import java.time.LocalDate;
import java.util.UUID;
#Entity
#Table(name = "bo_order")
public class Order {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
private String login;
private Long internalNumber;
private String food;
#Column(name="data_ins")
private LocalDate dateOfOrder;
private String orderNumber;
public Order(){}
public Order(String login)
{
this.login = login;
}
public Order(String login, Long internalNumber, String food, LocalDate dateOfOrder) {
this.login = login;
this.internalNumber = internalNumber;
this.food = food;
this.dateOfOrder = dateOfOrder;
}
public void setOrderNumber(String orderNumber) {
this.orderNumber = orderNumber;
}
public String generateOrderNumber()
{
return UUID.randomUUID().toString();
}
#Override
public String toString() {
return "Order{" +
"id=" + id +
", login='" + login + '\'' +
", internalNumber=" + internalNumber +
", food='" + food + '\'' +
", dateOfOrder=" + dateOfOrder +
'}';
}
}
added Repository to this:
package order;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import java.util.List;
public interface OrderRepository extends CrudRepository<Order, Long>{
List<Order> findByLogin(String login);
#Query(value="SELECT id, login FROM bo_order", nativeQuery = true)
public List<Order> findAllPurchasers();
}
and try to display values from #Query in Grid:
package purchasers;
import com.vaadin.annotations.Title;
import com.vaadin.server.VaadinRequest;
import com.vaadin.spring.annotation.SpringUI;
import com.vaadin.ui.*;
import order.Order;
import order.OrderRepository;
import org.springframework.beans.factory.annotation.Autowired;
#SpringUI(path = "/allpurchasers")
#Title("All today's purchasers")
public class AllPurchasersGUI extends UI {
#Autowired
private final OrderRepository orderRepository;
final Grid<Order> grid;
public AllPurchasersGUI(OrderRepository pr) {
this.orderRepository = pr;
grid = new Grid<>(Order.class);
grid.setSizeFull();
}
#Override
protected void init(VaadinRequest vaadinRequest) {
setContent(grid);
listPurchasers();
}
private void listPurchasers()
{
grid.setItems(orderRepository.findAllPurchasers());
}
}
but I got an error org.springframework.dao.InvalidDataAccessResourceUsageException: could not execute query; SQL [SELECT id, login FROM bo_order]; nested exception is org.hibernate.exception.SQLGrammarException: could not execute query and then
Caused by: org.postgresql.util.PSQLException: The column name data_ins was not found in this ResultSet.
I know data_ins is not in ResultSet because I don't want it there. I can display all values in Grid from bo_order with findAll but I want just id and login. How can I achive this? I also tried to make List<Object> instead of List<Order> but then I got problem with displaying then.
TL;DR: Add grid.setColumns("id", "login") to the end of the listPurchasers() method or include data_ins in the list of database columns in the #Query annotation.
When you create a Grid with the Grid(Class<T> beanType) constructor, one column will be added for every getter in the bean type. This will lead to an error unless data for all those getters are also loaded from the database.
In this case, the #Query annotation defines that data should only be fetched from the database for the columns id and login. The exception you get refers to the database column data_ins, which seems to be mapped to the dateOfOrder bean property, but I don't see any code that would remove the automatically added column for that property.
The easiest way of ensuring only desired columns are used by your grid is to use the setColumns method. That method works like setColumnOrder, except that it also removes any column that isn't included as a parameter.
Alternatively, you can also change your #Query annotation to also include the data_ins property from the database if you actually want that data to be shown as a column in the grid.
Grid takes parameters from Order class, so if there were many getters then they were displayed in columns. I did:
private void listPurchasers()
{
List<Order> allOrders = (List<Order>) orderRepository.findAll();
grid.setItems(allOrders);
grid.setColumnOrder("id", "login", "dateOfOrder");
grid.removeColumn("food");
}
so it seems to be very easy to add/remove columns to display.
Related
I'm using spring and MySQL as database to ORM. Im trying to display entity properties in grid in one of my Views. Item Id is passed by Url, and Items are set after constructor. In this scenario I'm trying to display audits that given enterprise had in the past. When navigating to given view, exception is beeing thrown:
There was an exception while trying to navigate to 'EnterpriseView/151' with the root cause 'java.lang.IllegalArgumentException: Multiple columns for the same property: auditId
What does it mean, as when I'm checking columns in database there in only one auditId in audit Table?
There are my classes:
import com.sun.istack.NotNull;
import javax.persistence.*;
#Entity
#Table
public class Audit {
private int auditId;
private Trip trip;
private User user;
private Enterprise enterprise;
public Audit() {
}
public Audit(Enterprise enterprise) {
this.enterprise = enterprise;
}
#Id
#GeneratedValue
#NotNull
#Column(unique = true)
public int getAuditId() {
return auditId;
}
#ManyToOne
#JoinColumn(name = "TRIPS_ID")
public Trip getTrip() {
return trip;
}
#ManyToOne
#JoinColumn(name = "USER_ID")
public User getUser() {
return user;
}
#ManyToOne
#JoinColumn(name = "ENTERPRISE_ID")
public Enterprise getEnterprise() {
return enterprise;
}
public void setAuditId(int auditId) {
this.auditId = auditId;
}
public void setTrip(Trip trip) {
this.trip = trip;
}
public void setUser(User user) {
this.user = user;
}
public void setEnterprise(Enterprise enterprise) {
this.enterprise = enterprise;
}
}
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.*;
import com.wtd.assistant.frontend.dao.AuditDao;
import com.wtd.assistant.frontend.dao.EnterpriseDao;
import com.wtd.assistant.frontend.domain.Audit;
import com.wtd.assistant.frontend.domain.Enterprise;
import java.util.List;
import java.util.Optional;
#Route("EnterpriseView")
public class EnterpriseView extends VerticalLayout implements HasUrlParameter<String>, AfterNavigationObserver{
private EnterpriseDao enterpriseDao;
private AuditDao auditDao;
private Grid<Audit> grid;
private List<Audit> auditsList;
private Optional<Enterprise> enterprise;
private String enterpriseId;
public EnterpriseView(EnterpriseDao enterpriseDao, AuditDao auditDao) {
this.enterpriseDao = enterpriseDao;
this.auditDao = auditDao;
this.grid = new Grid<>(Audit.class);
VerticalLayout layout = new VerticalLayout();
layout.add(grid);
grid.addColumns( "auditId" );
}
#Override
public void setParameter(BeforeEvent event, String parameter) {
enterpriseId = parameter;
System.out.println("setParameter(), enterpriseId: " + enterpriseId);
}
#Override
public void afterNavigation(AfterNavigationEvent event) {
enterprise = enterpriseDao.findById(Integer.valueOf(enterpriseId));
System.out.println("EnterpriseId: " + enterprise.get().getEnterpriseId());
auditsList = enterprise.get().getAudits();
grid.setItems(auditsList);
}
}
I tried renaming auditId property but obviously that didn't bring any result
Kind regards
Kiemoon
In the constructor of the EnterpriseView you have this code:
grid.addColumns( "auditId" );
Thats where your duplicate is comming from
I've been trying to expose some services through the Spring JPARepository interface
So I created a StudentRepo like this:
package edu.university.management.dao;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.rest.core.annotation.RestResource;
import edu.university.management.model.Student;
import edu.university.management.model.StudentsPerDepartment;
public interface StudentRepository extends JpaRepository<Student, Long> {
#RestResource(path = "/byDeptOrdered")
//#Query("select s from Student s where s.department.name = 'Informatique' order by s.entryDate desc")
public List<Student> findByDepartment_NameOrderByEntryDateDesc(String deptName);
#RestResource(path = "/topStudents")
#Query("select s"
+ " from Student s"
+ " where s.mark = ("
+ "select max(s.mark)"
+ " from Student s"
+ ")")
public List<Student> topStudents();
#RestResource(path = "/studentsPerDept")
#Query("select new edu.university.management.model.StudentsPerDepartment(d.name, count(s))"
+ " from Department d"
+ " left join"
+ " d.students s"
+ " group by d.name")
public List<StudentsPerDepartment> studentCountPerDepartment();
}
For the last service studentCountPerDepartment I used a class based projection to avoid returning an array of Objects,
StudentsPerDepartment:
package edu.university.management.model;
public class StudentsPerDepartment {
private String departmentName;
private Long studentCount;
public StudentsPerDepartment(String departmentName, Long studentCount) {
super();
this.departmentName = departmentName;
this.studentCount = studentCount;
}
public String getDepartmentName() {
return departmentName;
}
public Long getStudentCount() {
return studentCount;
}
}
But when i invoke the service i get this error:
org.springframework.data.mapping.MappingException: Cannot get or create PersistentEntity for type edu.university.management.model.StudentsPerDepartment! PersistentEntities knows about 2 MappingContext instances and therefore cannot identify a single responsible one. Please configure the initialEntitySet through an entity scan using the base package in your configuration to pre initialize contexts.
Any ideas guys?
Thank you.
I'm trying to use auditing to save dateCreated and dateUpdated in my objects, but since I set ID manually, there's some additional work.
Following Oliver Gierke's suggestion in DATAMONGO-946
I'm trying to figure out how to correctly implement it.
As original poster in Jira task above, I've downloaded example from here https://github.com/spring-guides/gs-accessing-data-mongodb.git and modified it a bit:
package hello;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.domain.Persistable;
import java.util.Date;
public class Customer implements Persistable<String> {
#Id
private String id;
#CreatedDate
private Date createdDate;
#LastModifiedDate
private Date lastModifiedDate;
private String firstName;
private String lastName;
private boolean persisted;
public Customer() {
}
public Customer(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public void setPersisted(boolean persisted) {
this.persisted = persisted;
}
#Override
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#Override
public boolean isNew() {
return !persisted;
}
#Override
public String toString() {
return String.format(
"Customer[id=%s, createdDate=%s, lastModifiedDate=%s, firstName='%s', lastName='%s']",
id, createdDate, lastModifiedDate, firstName, lastName);
}
}
and
package hello;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.mongodb.config.EnableMongoAuditing;
#SpringBootApplication
#EnableMongoAuditing
public class Application implements CommandLineRunner {
#Autowired
private CustomerRepository repository;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(String... args) throws Exception {
repository.deleteAll();
// create a customer
Customer c = new Customer("Alice", "Smith");
c.setId("test_id");
// save a customer
repository.save(c);
// fetch all customers
System.out.println("Customers found with findAll():");
System.out.println("-------------------------------");
for (Customer customer : repository.findAll()) {
System.out.println(customer);
}
System.out.println();
// create another customer with same id
c = new Customer("Bob", "Smith");
c.setId("test_id");
c.setPersisted(true);
repository.save(c);
// fetch all customers
System.out.println("Customers found with findAll():");
System.out.println("-------------------------------");
for (Customer customer : repository.findAll()) {
System.out.println(customer);
}
System.out.println();
}
}
and a result of execution is this:
Customers found with findAll():
-------------------------------
Customer[id=test_id, createdDate=Wed Feb 24 00:43:47 WITA 2016, lastModifiedDate=Wed Feb 24 00:43:47 WITA 2016, firstName='Alice', lastName='Smith']
Customers found with findAll():
-------------------------------
Customer[id=test_id, createdDate=null, lastModifiedDate=Wed Feb 24 00:43:47 WITA 2016, firstName='Bob', lastName='Smith']
createdDate becomes null after object update.
What am I missing here? And how to correctly implement Persistable to make auditing work properly?
Your code is working as expected. After you've implemented Persistable you can see that #CreatedDate annotation is working.
Sure that createdDate is null on the second call of save because the object already exists in the database and you updated it with createdDate = null. As you can see from the documentation for #CreatedDate:
#CreatedDate annotation. This identifies the field whose value is set
when the entity is persisted to the database for the first time.
So not to overwrite your createdDate with null on the second call you should retrieve your customer from the database with c = repository.findOne("test_id"); and then update it.
Add #EnableMongoAuditing to the main method in your spring boot application.
The simplest solution is to add a version property (annotated with #Version) to your Customer class and leave it uninitialized. this will assign the value of 0 to any newly created object which in turn tells spring that this is a new object.
#Version private Long version;
Note: that this version will be automatically incremented upon each modifications on this object
I have created a bean:
package beans;
import java.util.List;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import mazeJPA.Forumcategory;
/**
* Session Bean implementation class ForumBean
*/
#Stateless
#LocalBean
public class ForumBean
{
#PersistenceContext(unitName = "mazeEJB")
private EntityManager em;
/**
* Default constructor.
*/
public ForumBean(){}
public List<Forumcategory> getCategories()
{
return em.createNamedQuery
(
"Forumcategory.findAll", Forumcategory.class
).getResultList();
}
}
and its entity class
package mazeJPA;
import java.io.Serializable;
import javax.persistence.*;
/**
* The persistent class for the forumcategory database table.
*
*/
#Entity
#Table(name="forumcategory")
#NamedQuery(name="Forumcategory.findAll", query="SELECT f FROM Forumcategory f")
public class Forumcategory implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int cid;
private String desc;
private String name;
public Forumcategory() {
}
public int getCid() {
return this.cid;
}
public void setCid(int cid) {
this.cid = cid;
}
public String getDesc() {
return this.desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
When I call getCategories() from my servlet I get an sql exception:
Internal Exception:
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an
error in your SQL syntax; check the manual that corresponds to your
MySQL server version for the right syntax to use near 'DESC, NAME FROM
forumcategory' at line 1 Error Code: 1064 Call: SELECT CID, DESC, NAME
FROM forumcategory Query: ReadAllQuery(name="Forumcategory.findAll"
referenceClass=Forumcategory sql="SELECT CID, DESC, NAME FROM
forumcategory")
My table is:
cid INT NOT NULL, name VARCHAR(255) NOT NULL, desc TEXT NOT NULL,
PRIMARY KEY (cid)
I don't know why this is happening. Have I got something wrong with the mapping? I have only recently added this to my project is that anything to do with it i.e. do I have to generate all the tables at once?
DESC is a reserved sql word, so you have to scape it, using desc, for example
SELECT CID, `DESC`, NAME FROM ...
DESC is a reserved keyword in most SQL. Seems that your JPA provider doesn't automatically escape these for you (surround in quotes). Some JPA implementations (e.g DataNucleus JPA) do that for you.
There is a little problem in replying on ajax requests. Initially, I have simplest restful service, based on spring boot MVC.
model:
import javax.persistence.*;
import java.util.*;
#Entity
#Table(name = "testmodel")
public class TestModel
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)//Postgresql sequence generator
private long id;
#Column(name = "name")
private String name;
#Column(name = "content")
private String content;
//Constructor
public TestModel()
{
}
//Id getter
public long getId()
{
return this.id;
}
//Name getter-setter
public String getName()
{
return this.name;
}
public void setName(String name)
{
this.name = name;
}
//Content getter-setter
public String getContent()
{
return this.content;
}
public void setContent(String content)
{
this.content = content;
}
}
DAO for model:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.*;
public interface TetsModelDAO extends JpaRepository<Samples, Long>
{
#Query("SELECT s FROM TestModel s WHERE LOWER(s.name) LIKE LOWER(:entry) ORDER BY s.name")
List<TestModel> fetchByNameEntry(#Param("entry") String entry);
}
Controller:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
#RestController
#RequestMapping("/")
public class TestController
{
#Autowired
private TetsModelDAO testmodel;
#RequestMapping("/name")
public List<TestModel> getDatasetsByNameEntry(#RequestParam("entry") String entry)
{
return testmodel.fetchByNameEntry("%"+entry+"%");
}
}
Client-side ajax request:
$.ajax(
{
url : "/name?entry=", //get all records
method: "GET"
})
This example works perfectly - stringified reply looks like standart json structure:
{"id":"1", "name":"John", "content":"blablabla1"}
{"id":"2", "name":"Sam", "content":"blablabla2"}
{"id":"3", "name":"Ken", "content":"blablabla3"}
However, when I tried to define fileds in JPQL query explicitly (fetch, say, only id and name fields), I get wrong result in reply.
DAO with modified query (other code without changes):
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.*;
public interface TetsModelDAO extends JpaRepository<Samples, Long>
{
#Query("SELECT s.id, s.name FROM TestModel s WHERE LOWER(s.name) LIKE LOWER(:entry) ORDER BY s.name")
List<TestModel> fetchByNameEntry(#Param("entry") String entry);
}
In this case reply looks like this:
1, John, 2, Sam, 3, Ken
How to resolve this problem gracefully (without creating "helper classes")?
You can return DTO directly from Repository:
public interface TetsModelDAO extends JpaRepository<Samples, Long>
{
#Query("SELECT new mypackage.TestDto(s.id, s.name) FROM TestModel s WHERE LOWER(s.name) LIKE LOWER(:entry) ORDER BY s.name")
List<TestDto> fetchByNameEntry(#Param("entry") String entry);
}
where TestDto contains only required fields:
package mypackage;
public class TestDto {
private final long id;
private final String name;
public TestDto(long id, String name) {
this.id = id;
this.name = name;
}
public long getId() {
return id;
}
public String getName() {
return name;
}
}
Your query doesn't return instances of TestModel. It returns arrays of objects (i.e. a List<Object[]>), each array containing the ID and the name of a found TestModel. The correct query is
SELECT s FROM TestModel s WHERE LOWER(s.name) LIKE LOWER(:entry) ORDER BY s.name
You'd better implement automated tests to check that your DAO queries return what they should.