For some reason I don't get results when running this from method.
#SuppressWarnings("unchecked")
public Object[] getPointRaiting(Long id) {
EntityManager em = createEntityManager();
em.getTransaction().begin();
Query allPointsQuery = em
.createQuery("Select AVG(r.RATING) from Ratings r WHERE r.POINT_ID = :point");
allPointsQuery.setParameter("point", id);
Object[] rating = (Object[]) allPointsQuery.getSingleResult();
em.getTransaction().commit();
em.close();
closeEntityManager();
return rating;
}
SQL should be correct as it executes in HSQL db manager and returns the correct value. But java function stops running at query. It does'nt throw any errors just stops. I'm out of ideas, where should I look? (Other similiar methods with count and select all work correctly).
Using HSQLDB and Hibernate.
Found that the following error was thrown:
org.hibernate.QueryException: could not resolve property: RATING of: kaart.entities.Ratings [Select AVG(r.RATING) from kaart.entities.Ratings r WHERE r.POINT_ID = :point]
But this does not solve it for me as the RATING property is defined in table and in entity...
#Entity #Table(name = "RATINGS")
public class Ratings implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
private Point point;
#ManyToOne
private User user;
#Column(name = "RATING")
private int rating;
private static final long serialVersionUID = 1L;
public Ratings() {
super();
}
public Ratings(Point point, User user, int rating) {
this.point = point;
this.user = user;
this.rating = rating;
}
/*all getters and setters here*/}
#Entity
#Table(name = "POINT")
public class Point implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "point")
private List<Category> pointsByCategory;
#OneToMany(mappedBy = "point")
private List<Ratings> pointRatings;
#Column(name = "NAME")
private String name;
#Column(name = "LOCATION")
private String location;
#Column(name = "DESCRIPTION")
private String description;
#Column(name = "LINK")
private String link;
#ManyToOne
private User user;
private static final long serialVersionUID = 1L;
public Point() {
super();
}
public Point(String name, String location, String description, String link, User user) {
this.name = name;
this.location = location;
this.description = description;
this.link = link;
this.user = user;
} /* getters and setters*/
You can only pass JP-QL inside em.createQuery().
But seems you are using native SQL with values like r.RATING, r.POINT_ID, which may not be in the Java entity. Replace it with equivalent java entity variable, could be pointId
em.createQuery("Select AVG(r.RATING) from Ratings r WHERE r.POINT_ID = :point");
If you want to use native sql, you can use em.createNativeQuery().
Most likely this problem is caused by caps-locked property names: RATING, POINT_ID.
Try replacing them with the ones that you use in Ratings class, probably:
Select AVG(r.rating) from Ratings r WHERE r.point.id = :point_id
Related
first time poster here. I am having an issue using a custom finder query with spring/jpa.
I have a simple ecommerce project using ORM with 4 tables in database. Customer accounts, list of products, current cart, and transaction history.
The problem I am having is using the specific finder methods.
In order to ensure the value of each Order No be incrementally inserted upon checkout(save and flush a list of all items in current cart to transaction history) I have to find that specific value by account Id, and ++ for every checkout, before setting it to save and Flush.
Can someone point out what I am doing wrong?
cart Dao with finder methods:
public void deleteAllByAccountId(int accountId);
public void deleteByBookIdAndAccountId(int bookId, int accountId);
public List<CartEntity> findAllByAccountId(int accountId);
#Query ("SELECT MAX(orderNo) FROM current_cart WHERE accountid = :#{customer_accounts.accountid}")
public CartEntity findByAccountId(#Param("accountid") int accountId);
cart Entity:
#Table(name = "current_cart")
public class CartEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY) // Id is the primary auto generated key
#Column(name ="orderno")
private int orderNo;
#Column(name = "accountid")
private int accountId;
#Column(name = "cost")
private int bookCost;
#Column(name = "quantity")
private int quantity;
#Column(name = "booktitle")
private String bookTitle;
#Column(name = "bookid")
private int bookId;
#Table(name = "customer_accounts")
public class AccountEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY) // Id is the primary auto generated key
#Column(name = "accountid")
private int accountId;
#Column(name = "email")
private String email;
#Column(name = "password")
private String password;
#Column(name = "firstname")
private String firstname;
#Column(name = "lastname")
private String lastname;
checkout logic:
public void Checkout(int accountId) {
List<CartEntity> FetchedCartEntities = cartDao.findAllByAccountId(accountId);
List<TransactionHistoryEntity> transactionsToCopy = new ArrayList<TransactionHistoryEntity>();
for (CartEntity cartEntity : FetchedCartEntities) {
TransactionHistoryEntity transaction = new TransactionHistoryEntity();
BeanUtils.copyProperties(cartEntity, transaction);
transactionsToCopy.add(transaction);
}
transactionHistoryDao.saveAllAndFlush(transactionsToCopy);
cartDao.deleteAllByAccountId(accountId);
}
Thank you guys
Problem
I am trying to store an object in my Postgres database. This consists of the Order.class, (List) OrderDevice.class, and a Department.class.
The important thing is that the OrderDevices are always stored new in the DB, but a Department may already exist.
When I try to save the object to my database using save I get the following error message: (shown below)
I get the error message "detached entity passed to persist: com.niclas.model.OrderDevice" if the department does not exist yet, if the department exists the error message looks like this: "detached entity passed to persist: com.niclas.model.Department".
Solution attempts
This solution cannot be used because I do not use bidirectional mapping.
(I don't want to use a bidirectional mapping because I want to access the departments without an order.)
I also tried to change the Cascade types to MERGE like in this solution
I also tried using #Transactional on the method
I also tried to save the children in the database first and then the parent like this:
departmentRepository.save(order.getDepartment()); orderDeviceRepository.saveAll(order.getDevices()); orderRepository.save(order);
I hope I have described my good enough and I am happy about suggestions for solutions
Error.log
The log can be viewed here. (The formatting did not work here)
Order.class
#Entity
#Table(name = "orders")
public class Order extends AuditModel {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO) //TODO better config for GenerationType
private long id;
#Column(name = "order_id")
private String orderId;
#Column(name = "department_id")
private long departmentId;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "department", referencedColumnName = "id")
private Department department;
#JsonProperty("deviceList")
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "order_id", referencedColumnName = "order_id")
private List<OrderDevice> devices;
#JsonProperty("forename")
#Column(name = "sender_forename")
private String senderForename;
#JsonProperty("surname")
#Column(name = "sender_surname")
private String senderSurname;
#Column(name = "notes", columnDefinition = "TEXT")
private String notes;
#Column(name = "month")
private int month;
#Column(name = "year")
private int year;
public Order() {
}
... Getter/Setters
}
OrderDevice.class
#Entity
#Table(name = "order_devices")
public class OrderDevice extends AuditModel{
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO) //TODO better config for GenerationType
private long id;
#Column( name = "order_id", insertable = false, updatable = false )
private String orderId;
#Column(name = "device_id")
private long deviceId;
#Column(name = "device_name")
private String deviceName;
#Column(name = "priceName")
private String priceName;
#Column(name = "price")
private double price;
#Column(name = "count")
private int count;
public OrderDevice() {
}
... Getters/Setters
}
Department.class
#Entity
#Table(name = "departments")
public class Department {
//TODO add Form Validation
//TODO better Naming for From Attributes on Frontend and Backend
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO) //TODO better config for GenerationType
private long id;
#Column(name = "department_name")
private String department;
#Column(name = "contact_person_forename")
private String forename;
#Column(name = "contact_person_surname")
private String surname;
#Column(name = "contact_person_mail")
private String mail;
#Column(name = "street")
private String street;
#Column(name = "house_number")
private String houseNumber;
#Column(name = "location")
private String location;
#Column(name = "postal_code")
private int postalCode;
#Column(name = "country")
private String country;
#Column(name = "auto_send_invoice")
private boolean autoSend;
#Column(name = "registered")
private boolean registered;
public Department() {
}
... Getter/Setters
}
OrderController.class
#Slf4j
#RestController
public class OrderController {
private final DepartmentRepository departmentRepository;
private final OrderRepository orderRepository;
private final OrderDeviceRepository orderDeviceRepository;
public OrderController(OrderRepository orderRepository, DepartmentRepository departmentRepository,
OrderDeviceRepository orderDeviceRepository) {
this.orderRepository = orderRepository;
this.departmentRepository = departmentRepository;
this.orderDeviceRepository = orderDeviceRepository;
}
#PostMapping("/orders/add")
public ResponseEntity<Order> addDepartment(#RequestBody Order order) throws JsonProcessingException {
order.setOrderId(order.generateOrderId());
DateTime dateTime = new DateTime();
order.setMonth(dateTime.getMonthOfYear());
order.setYear(dateTime.getYear());
order.getDevices().forEach(orderDevice -> {
orderDevice.setOrderId(order.getOrderId());
});
//departmentRepository.save(order.getDepartment());
//orderDeviceRepository.saveAll(order.getDevices());
orderRepository.save(order);
return new ResponseEntity<>(order, HttpStatus.CREATED);
}
Update
If the objects are created in this way, no error will occur and the order will be successfully saved in the database.
However, I don't understand why it works this way and not via ObjectMapper. Does anyone know why?
#PostMapping("/orders/add")
public ResponseEntity<Order> addDepartment(#RequestBody JsonNode jsonNode) throws JsonProcessingException {
Order order = new Order();
JsonNode departmentJson = jsonNode.get("department");
Department department;
if ( departmentJson.get("id").canConvertToInt() ) {
department = departmentRepository.findDepartmentById(departmentJson.get("id").asInt());
} else {
department = new Department();
department.setDepartment(departmentJson.get("department").asText());
department.setForename(departmentJson.get("forename").asText());
department.setSurname(departmentJson.get("surname").asText());
department.setMail(departmentJson.get("mail").asText());
department.setStreet(departmentJson.get("street").asText());
department.setHouseNumber(departmentJson.get("houseNumber").asText());
department.setLocation(departmentJson.get("location").asText());
department.setPostalCode(departmentJson.get("postalCode").asInt());
department.setCountry(departmentJson.get("country").asText());
department.setAutoSend(departmentJson.get("autoSend").asBoolean());
department.setRegistered(departmentJson.get("registered").asBoolean());
}
order.setDepartment(department);
order.setOrderId(order.generateOrderId());
order.setDepartmentId(department.getId());
List<OrderDevice> orderDevices = new ArrayList<>();
JsonNode devices = jsonNode.get("deviceList");
for (JsonNode node : devices) {
//TODO replace this mess with objectMapper
if (node.has("count") && node.get("count").asInt() != 0){
OrderDevice device = new OrderDevice();
device.setOrderId(order.getOrderId());
device.setDeviceId(node.get("id").asLong());
device.setDeviceName(node.get("deviceName").asText());
device.setPriceName(node.get("priceName").asText());
device.setPrice(node.get("price").asDouble());
device.setCount(node.get("count").asInt());
orderDevices.add(device);
}
}
order.setDevices(orderDevices);
order.setSenderForename(jsonNode.get("forename").asText());
order.setSenderSurname(jsonNode.get("surname").asText());
order.setNotes(jsonNode.get("notes").asText());
DateTime dateTime = new DateTime();
order.setMonth(dateTime.getMonthOfYear());
order.setYear(dateTime.getYear());
orderRepository.save(order);
return new ResponseEntity<>(order, HttpStatus.CREATED);
}
You can try to use instead of orderRepository.save(order) use orderRespostiory.saveOrUpdate(order).
How to find a reference when we have composite key(two or more columns) to pass on as second parameter to the JPA entityManager.find(Object.class, compositeKey)?
My try-
I have created an Arraylist and added the values forming compositeKey it and then passing this list to the find method.
For ex: In my situation, userid and projectid together is the key for the UserProject table and these two have been added to the arraylist named as list, which will be passed as a second parameter to the entityManager find method as shown below:
List<Integer> list = new ArrayList<Integer>();
list.add(userProjectDO.getUserid());
list.add(userProjectDO.getProjectid());
UserProject userProject = em.find(UserProject.class,list);
But this always return as null, even though userid and projectId exists on the table. Has anyone been into similar issue? Solution?
JPA's EntityManager#find doesn't accept arrays as key but Object. Since you are talking about composite key you should implement your key in a separate class which will represent the composite key by listing all the key separate properties. You can achieve this using EmbeddedId for instance.
For example:
You should define the composite key class and annotate with #Embeddable:
public class UserProjectKey implements Serializable{
private String userId;
private String projectId;
//constructors, getters, setters
}
and use it as #EmbeddedId in your entity.
To search by the key you can do:
UserProjectKey key = new UserProjectKey("userIdExample", "projectIdExample");
em.find(UserProject.class, key);
I have found another approach i.e. writing namedQuery to search the table. Posting the implementation just in case it helps anyone.
final Query query = em.createNamedQuery("UserProject.findByAll");
UserProject Entity class:
#Entity
#Table(name = "userproject", schema = "public")
#NamedQueries({ #NamedQuery(name = "UserProject.findByAll", query = "SELECT a FROM UserProject a where a.userid = :userid and a.projectid = :projectid"),
#NamedQuery(name = "UserProject.findByUserId", query = "SELECT a FROM UserProject a where a.userid = :userid"),
#NamedQuery(name = "UserProject.findById", query = "SELECT a FROM UserProject a where a.id = :id" )})
public class UserProject implements Serializable {
private static final long serialVersionUID = 1L;
#Id #GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#Column(name = "userid")
private Integer userid;
#Column(name = "projectid")
private Integer projectid;
#Column(name = "created")
private Timestamp created;
#Column(name = "modified")
private Timestamp modified;
#Column(name = "modifiedbyid")
private Integer modifiedbyid;
#Column(name = "role")
private String role;
public Integer getId() {
return id;
}
public void setId(final Integer id) {
this.id = id;
}
public Integer getUserid() {
return userid;
}
public void setUserid(final Integer userid) {
this.userid = userid;
}
public void setProjectid(final Integer projectid) {
this.projectid = projectid;
}
public Timestamp getCreated() {
return created;
}
public void setCreated(final Timestamp created) {
this.created = created;
}
public Timestamp getModified() {
return modified;
}
public void setModified(final Timestamp modified) {
this.modified = modified;
}
public Integer getModifiedbyid() {
return modifiedbyid;
}
public void setModifiedbyid(final Integer modifiedbyid) {
this.modifiedbyid = modifiedbyid;
}
public String getRole() {
return role;
}
public void setRole(final String role) {
this.role = role;
}
}
And finally set the query parameters i.e. compositeKey values(userid,projectid) as :
final Query query = em.createNamedQuery("UserProject.findByAll");
query.setParameter("userid",userProjectDO.getUserid());
query.setParameter("projectid",userProjectDO.getProjectid());
List<UserProject> userProjectList = query.getResultList();
userProjectList would contain the row which matches the compositeKey (userId,projectId)
One advantage I see with this approach is that I can write N number of named queries inside the entity class as per the need/requirement. For ex: If we need to work on a view created out of this table. It can be easily achieved by first creating the view and then write another named query to work on it.
I have below entities and #ManyToMany mapping between the two.
#Entity
#Table(name = "user")
public class User implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 5340562707217344212L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userId;
private String userName;
private String password;
private String firstName;
private String lastName;
private String emailId;
private Date createdDate;
private Byte status;
private Date lastModifiedDate;
#ManyToMany
#JoinTable(name = "user_products_mapper",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "product_id")
)
private List<Products> products = new ArrayList<Products>();
public void addProducts(Products product){
this.products.add(product);
}
#Entity
#Table(name="products")
public class Products implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 1895580713896126954L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long productId;
private String productName;
private String description;
private double minBalance;
public Long getProductId() {
return this.productId;
}
public void setProductId(Long productId) {
this.productId = productId;
}
public String getProductName() {
return this.productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
My question is :
1)I already have these entities persisted. (User and Products)
2)User and Products are already exists in databasae and relation between them is #ManyToMany
3)I want to insert records ONLY IN JoinTable (user_products_mapper) since the rows are already present for User and Products.
4)One way to achieve this is to use UNIDIRECTIONAL mapping as I have above and just call
User.addProducts
5) Before doing this User and Products entities will be fetched like below :
User user = this.userDao.findOne(userId);
if(user == null){
//throw exception
}
Products product = this.productDao.findOne(productId);
6)Is there any way of doing this ?
When using Hibernate/JPA you do not control the actual queries being run, but you are not supposed to either.
What you would do to connect a user and a product would be something like:
#Transactional
public void addProductToUser(final Long userId, final Long productId) {
final User user = em.find(User.class, userId);
final Product product = em.find(Product.class, productId);
user.addProduct(product);
}
This will result in more queries than just inserting a row (since you will fetch both Product and User), but what you gain is simple and plain Java code. The cost of the extra queries in most cases is well within what is acceptable.
i am trying to create a bidirectional one to many relationship.
#Entity
#XmlRootElement
#NamedQueries({
#NamedQuery(name = Company.FIND_ALL, query = "select c from Company
})
public class Company {
public static final String FIND_ALL = "Company.findAll";
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String email;
private String name;
private String phoneNumber;
#OneToMany(mappedBy = "company")
private List<Place> places;
private long millisSince1970;
private boolean deleted;
public Company() {
}
#PrePersist
public void addMillisPrePersist() {
millisSince1970 = Instant.now().getEpochSecond();
deleted = false;
}
#PreUpdate
public void addMillisPreUpdate() {
millisSince1970 = Instant.now().getEpochSecond();
}
}
Place class:
#Entity
#XmlRootElement
#NamedQueries({
#NamedQuery(name = Place.FIND_ALL, query = "select p from Place p")
})
public class Place {
public static final String FIND_ALL = "Place.findAll";
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private Type type;
private String email;
private String name;
private String city;
private String address;
private String phoneNumber;
private String latitude;
private String longitude;
private String workingHours;
#ManyToOne
#JoinColumn(name = "company_id", referencedColumnName = "id", nullable = false)
private Company company;
#OneToMany(mappedBy = "place")
private List<SpecialOffer> specialOffers;
#OneToMany(mappedBy = "place")
private List<Event> events;
private long millisSince1970;
private boolean deleted;
public Place() {
}
#PrePersist
public void addMillisPrePersist() {
millisSince1970 = Instant.now().getEpochSecond();
deleted = false;
}
#PreUpdate
public void addMillisPreUpdate() {
millisSince1970 = Instant.now().getEpochSecond();
}
}
And here is simple resource:
#GET
#Path("{companyId}")
#Produces({MediaType.APPLICATION_JSON})
public Company getCompany(#PathParam("companyId") int id) {
return entityManager.find(Company.class, id);
}
In my database i have Company and Place tables, in the Place table i have a foreign key column named company_id, so when i try to get some Company which has some corresponding Place glassfish returns http status 500 internal server error without any exception, and server logs are empty, thus i can not debug or understand the cause of this problem. If i try to get the company which doesn't have any places it returns it without any problem. So what am i doing wrong?
P.S. i think my question is similar to this one Glassfish: HTTP 500 Internal Server Error without any exception which unfortunately doesn't have any responses