I have a Product model that has some basic attributes,
package models;
import java.util.*;
import javax.persistence.*;
import play.db.ebean.*;
import play.data.validation.*;
#Entity
public class Product extends Model {
public String name;
public String description;
public static Finder<Long,Item> find = new Finder<Long,Item>(
Long.class, Item.class
);
}
a controller function that uses find to attain and pass a List<Product> to the desired view,
package controllers;
import play.*;
import play.mvc.*;
import views.html.*;
import models.Product;
public class Application extends Controller {
public static Result allProducts() {
return ok(product_page.render(Product.find.all()));
}
}
and the stated view that iterates over the list and displays these properties.
#(products: List[Product])
<h1>Here are the products:</h1>
<ul>
#for(product <- products) {
<li>#product.getName()</li>
<ul>
<li>#product.getDescription()</li>
</ul>
}
</ul>
Everything looks fine (at least to me)... but the compiler tells me this:
value getName is not a member of models.Product
What am I doing wrong? (the route is fine, application.conf is setup correctly, evolutions are correct as well...)
Use:
<li>#product.description</li>
BTW: Your finder should use as a second type the class of the current model
public static Finder<Long,Product> find = new Finder<Long,Product>(
Long.class, Product.class
);
Product does not have getter methods
Related
I am a Hibernate beginner. I did a couple of simple tutorials and am trying to write a simple shop backend. Everything works as it should, but I am seeing strange things in my unit tests. When I save an entity, then retrieve it using findById(), it seems that I am simply getting the same object I called save() on, without even retrieving actual values from the database:
package com.bo.learnjava.shop1.repository;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "PRODUCTS")
public class Product {
#Id
#GeneratedValue
#Column(name="ID")
long id;
#Column(name="NAME")
String name = "";
#Column(name="PRICE_CENTS")
int priceCents = 0;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPriceCents() {
return priceCents;
}
public void setPriceCents(int priceCents) {
this.priceCents = priceCents;
}
public long getId() {
return id;
}
}
package com.bo.learnjava.shop1.repository;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface ProductRepository extends PagingAndSortingRepository<Product,Long> {
}
package com.bo.learnjava.shop1.repository;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
#DataJpaTest
public class ProductRepositoryTest {
#Autowired
ProductRepository repo;
#Test
void testProductRepository() {
Product p=new Product();
p.setName("Milk");
p.setPriceCents(134);
repo.save(p);
// Modify the value to check that repo.findById() actually retrieves *saved* data
p.setPriceCents(9999);
Optional<Product> productFromRepo=repo.findById(p.getId());**
// I expect productFromRepo to contain the values I called save() with
// (price == 134). But productFromRepo.get() returns exactly the same Java object
// as p (with price == 9999), so no actual data was retrieved from the database - why?
assertTrue(productFromRepo.isPresent());
System.out.println("productFromRepo.priceCents="+productFromRepo.get().getPriceCents()); // Outputs 9999!
assertEquals(134,productFromRepo.get().getPriceCents()); // THIS FAILS!!!
}
}
Why does Hibernate behave like that, and how do I test that stuff I write to the database via Hibernate actually gets retrieved back from the database?
Additionaly to comment about first level cache.
If you are extending JpaRepository you can use
repo.saveAndFlush(p);
or
repo.save(p); repo.flush();
to immediately save data in DB.
After it - repo.findById(p.getId()); will return updated data.
I've got a question regarding MongoDB Java driver and POJOs/serialization.
I'd like to build up my Java classes as they are represented in the MongoDB collection and then use the (new) POJO feature of MongoDB for fetching data. See: http://mongodb.github.io/mongo-java-driver/3.8/driver-async/getting-started/quick-start-pojo/ and http://mongodb.github.io/mongo-java-driver/3.8/bson/pojos/
Right now this only works if I have two different classes, like
User.class
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.bson.codecs.pojo.annotations.BsonId;
import org.bson.codecs.pojo.annotations.BsonProperty;
public class User {
#BsonId
#BsonProperty("_id")
UUID id;
private List<UserSession> sessions = new ArrayList<>();
}
UserSession.class
import java.time.Instant;
public class UserSession {
Instant start;
Instant end;
}
But as my collection looks more like the following…
{
_id: XYZ,
sessions: {
{start: XYZ, end: XYZ},
{start: XYZ, end: XYZ},
{start: XYZ, end: XYZ}
}
}
… I'd like to have a class that looks like this:
User.class
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.time.Instant;
import org.bson.codecs.pojo.annotations.BsonId;
import org.bson.codecs.pojo.annotations.BsonProperty;
public class User {
#BsonId
#BsonProperty("_id")
UUID id;
private List<Session> sessions = new ArrayList<>();
public class Session {
Instant start;
Instant end;
}
}
This makes sense as every unique sessions belongs directly to a user and with being a nested class I'd be able to access fields of the parent User object from within the Session object.
The problem is that the Java driver now complains about not having an empty/no arguments constructor for my Session class ("By default all POJOs must include a public or protected, empty, no arguments, constructor.").
My CodecProvider is like following:
CodecProvider codecProvider = PojoCodecProvider.builder()
.register(User.class, User.Session.class);
Anyone here having an idea how to solve this issue?
Really appreciate your help!
Thanks a lot!
Side note: The code snippets above are just short examples how my code kinda looks like. They are not the full code I'm using so there might be syntax errors in it.
Have two entities with same simple names in different packages, referenced to same table name but different schemes (physically different tables). Code compiles with no errors. Executes correctly if the behavior with these tables was not triggered. But error org.hibernate.QueryException: could not resolve property description occurs when there is a call to repository with data for home.
Questions:
where is the case described in the documentation?
is there a workaround which will exclude renaming of entity classes?
First entity: package home, table is under default schema (specified in entity manager):
package com.example.domain.home;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.io.Serializable;
#Entity
public class Data implements Serializable {
private static final long serialVersionUID = 1L;
#Id
public String id;
public String description;
}
Second entity: package work, same simple name, same table name, but different schema:
package com.example.domain.work;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
#Entity
#Table(name = "DATA", schema = "WORK")
public class Data implements Serializable {
private static final long serialVersionUID = 1L;
#Id
public String id;
}
Repository to find data from home:
package com.example.domain.home;
import org.springframework.data.jpa.repository.JpaRepository;
public interface DataRepository extends JpaRepository<Data, Long> {
Data findTopByDescription(String description);
}
Repository to find data from work, need to specify name, otherwise spring don't want to autowire correctly:
package com.example.domain.work;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository("workDataRepository")
public interface DataRepository extends JpaRepository<Data, Long> {
}
Consume one of the repository:
package com.example.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.domain.home.DataRepository;
#Service
public class HomeService {
#Autowired
private DataRepository dataRepository;
public void test(){
dataRepository.findTopByDescription("Test");
}
}
Have not found any related information in spring data nor hibernate documentation.
If there is any other information that will be useful, please, leave a comment.
There are three relevant name-like values for an entity class:
the fully qualified class name: You are fine on this one since it includes the package name.
the table name: You are fine again since the schema makes them distinct.
the entity name: That one is used in JPQL queries and (I guess) in Maps internally to hold metadata. This is by default the same as the simple class name. But you can change it using the #Entity annotation to (almost) whatever you like.
I'm trying a simple view in Play framework 2.5 but I keep getting this compile-time error java.util.List<models.Vehicle> cannot be converted to java.lang.String
I already tried this answer
scala.collection.immutable.List<String> ls = JavaConverters.asScalaBufferConverter(scripts).asScala().toList();
but there is no .toList() function, it is not recognised.
This is my code:
Aplication.java
package controllers;
import models.Vehicle;
import play.mvc.Controller;
import play.mvc.Result;
import scala.collection.JavaConverters;
public class Application extends Controller {
public Result index() {
// this does not work
// scala.collection.immutable.List<Vehicle> ls = JavaConverters.asScalaBufferConverter(Vehicle.finder.all()).asScala().toList();
return ok(views.html.index.render(Vehicle.finder.all())); //here is the error
}
}
index.scala.html
#(vehicles: java.util.List[Vehicle])
#main("Welcome to Play") {
<header>
<hgroup>
<h1>Dashboard</h1>
<h2>Vehicles</h2>
</hgroup>
</header>
<ul>
#for(vehicle <- vehicles) {
<li>#vehicle.getModel</li>
}
</ul>
}
relevant code in Vehicle.java
package models;
import javax.persistence.*;
import java.util.List;
#Entity
public class Vehicle extends BaseEntity {
public static Finder<Long, Vehicle> finder = new Finder<Long, Vehicle>(Vehicle.class){};
#Column(nullable = false)
String model;
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
}
Any help would be highly appreciated.
Okay, so in case anyone finds this in the future, try
activator clean compile
in your project folder.
Doing only compile did not help at all.
I am using the Play Framework in it's current version and my model classes extend play.db.jpa.JPABase.
Today I tried to make an often used type of query generic and define a static helper method to construct it.
I wrote the following:
import play.db.jpa.Model;
import play.libs.F;
public class GenericQueries {
public static <T extends Model> F.Option<T> firstOption(
Class<T> clazz,
String query,
Object... parameters){
final T queryResult = T.find(query,parameters).first();
return (queryResult == null) ?
F.Option.<T>None() :
F.Option.Some(queryResult);
}
}
However, I get the following error:
Execution exception
UnsupportedOperationException occured : Please annotate your JPA model with #javax.persistence.Entity annotation.
I debugged into the method, at runtime T seems to be correctly set to it's corresponding Model class. I even see the annotation.
I suspect some class enhancing voodoo by the play guys responsible for this, but I am not entirely sure.
Any ideas?
Update: added Model class as Requested
Here is a shortened Version of one of the Model classes I use.
package models;
import org.apache.commons.lang.builder.ToStringBuilder;
import play.data.validation.Required;
import play.db.jpa.Model;
import play.modules.search.Field;
import play.modules.search.Indexed;
import javax.persistence.Column;
import javax.persistence.Entity;
import java.util.Date;
#Entity #Indexed
public class FooUser extends Model {
#Required
public Date firstLogin;
#Field
#Required(message = "needs a username")
#Column(unique = false,updatable = true)
public String name;
#Field
public String description;
#Required
public boolean isAdmin;
#Override
public String toString(){
return new ToStringBuilder(this)
.append("name", name)
.append("admin", isAdmin)
.toString();
}
}
In Play entites should extend play.db.jpa.Model and use #Entity annotation (class level).
For what you say I understand that you are extending play.db.jpa.JPABase.
This may be the reason of the issue, as Play (as you point) dynamically enhances classes and it may be clashing with your inheritance.
EDIT: I tested the issue
The problem is that Play is not enhancing the object T. This means that the find method called id the one of GenericModel (parent of Model) whose implementation is to throw an exception with the message.
The enhancer seems to detect only the classes with #Entity.
Even the solution of mericano1 doesn't work, the enhancer doesn't pick it. So I feel you won't be able to use that method in Play.
The best way to do that is to use a base class that extends play.db.jpa.Model with just the static methods that will be shared by the subclasses.
Add the #Entity annotation to the base class and no class fields.
import play.db.jpa.Model;
import play.libs.F;
public class BaseModel extends Model {
public static <T extends Model> F.Option<T> firstOption(
Class<T> clazz,
String query,
Object... parameters){
final T queryResult = T.find(query,parameters).first();
return (queryResult == null) ?
F.Option.<T>None() :
F.Option.Some(queryResult);
}
}
And then
#Entity
public class FooUser extends BaseModel {
....
}