SpelEvaluationException while creating key on #Cacheable in spring cache - java

I am trying to cache the data using #Cacheable for a method which don’t have parameters/arguments.
Following is my repository code:
public interface FooRepository extends JpaRepository<Foo, Integer>{
#Cacheable(value = "fooTypes",key = "#root.target.getType().getName()")
List<Foo> findAll();
}
and the Foo entity
#Entity
#Table(name = "table_name”)
public class Foo implements Serializable
{
private int id;
private FooType type;
public FooType getType(){
return this.type;
}
}
#Entity
Public class FooType {
private String name;
publc String getName(){
return this.name;
}
}
Now when I try to call the findAll() method, following exception is thrown:
Uncaught runtime error: org.springframework.expression.spel.SpelEvaluationException: EL1004E:(pos 13): Method call: Method getType () cannot be found on com.sun.proxy.$Proxy162 type
Can someone advice how to configure my cache using FooType field name as key?

Related

Spring Runtime Error,No identifier specified for entity:

New to java and spring. Tried creating an app but was unsuccessful.I have the following entity and controller in my app. But at runtime i get an error. I posted snippets for easier reading.
staff.java
#Data
#Entity
public class Staff {
private int staff_id;
private String staff_name;
private String staff_email;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "staff")
private List<VisitRequest> visitRequest;
public String getStaff_name(){
return staff_name;
}
public void setStaff_name(String staff_name){
this.staff_name = staff_name;
}
public String getStaff_email(){
return staff_email;
}
public void setStaff_email(String staff_email){
this.staff_email = staff_email;
}
public int getStaff_id(){
return staff_id;
}
public void setStaff_id(int staff_id){
this.staff_id = staff_id;
}
}
StaffController.java
#Controller
#RestController
#RequestMapping("/staff/")
public class StaffController{
#Autowired
protected StaffRepository staffRepository;
#GetMapping("/Staff")
public List<Staff> getAllStaff() {
return staffRepository.findAll();
}
#GetMapping("/staff/{Staff_id}")
public ResponseEntity<Staff> getStaffById(#PathVariable(value = "Staff_id") Long Staff_id)
throws ResourceNotFoundException{
Staff staff = staffRepository.findById(Staff_id)
.orElseThrow(() -> new ResourceNotFoundException("Employee not Found"));
return ResponseEntity.ok().body(staff);
And the error that is thrown at runtime is
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfigura
tion.class]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: No identifier specified for entity: com.Vportal.data.model.Staff
Please advice on what to do.
Your Staff entity lacks a member with an #Id annotation. This could be added to staff_id like follows:
#Data
#Entity
public class Staff {
#Id
private int staff_id;
....
}

Java JPA Preventing Proxies from calling db

I have a spring boot (1.5.4.RELEASE) project using Java 8. I have an entity and it's related domain class like this:
#Entity
#Table(name = "Foo", schema = "dbo")
public class FooEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "Id")
private int id;
#Column(name="Name")
private String name;
#Column(name="Type")
private String type;
#Column(name="Color")
private String color;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "Car")
private Car car;
//getter and setter
}
public class Foo {
private int id;
private String name;
private String type;
private String color;
private Car car;
//Constructors and getters
}
I want to create a repository that fetches this Foo object from the DB but only fetching the complex fields if the user asks for them to prevent unnecessary join statements. The repo looks like this:
import static com.test.entities.QFooEntity.fooEntity;
import static com.test.entities.QCarEntity.carEntity;
#Repository
public class FooRepository {
private final JPAQuery<FooEntity> query = createQuery().from(fooEntity);
public FooRepository getFooByName(String name) {
query.where(fooEntity.name.eq(name));
return this;
}
public FooRepository withCar() {
query.leftJoin(fooEntity.car, carEntity).fetchJoin();
return this;
}
public Foo fetch() {
FooEntity entity = query.fetchOne();
return FooMapper.mapEntityToDomain().apply(entity);
}
}
So a barebones call for a Foo object will return the Entity with values for all the fields except for the car field. If the user wants car information then they have to explicitly call withCar.
Here is the mapper:
public class FooMapper {
public static Function<FooEntity, Foo> mapEntityToDomain() {
return entity -> {
return new Foo(e.getId(), e.getName(), e.getType(), e.getColor(), e.getCar());
};
}
}
The problem is when you do e.getCar() if the value is not there (i.e. there's a proxy present) JPA will go out and fetch it for you. I don't want this to be the case. It will just grab the values and map them to the domain equivalent if it's not there then null.
One solution that I've heard (and tried) is calling em.detach(entity); however, this doesn't work as I intended because it throws an exception when you try to access getCar and I've also heard this is not best practice.
So my question is what is the best way to create a repo using a builder pattern on a JPA entity and not have it call the DB when trying to map.
You could create a utility method that will return null if the given object is a proxy and is not initialized:
public static <T> T nullIfNotInitialized(T entity) {
return Hibernate.isInitialized(entity) ? entity : null;
}
Then you can call the method wherever you need it:
return new Foo(e.getId(), e.getName(), e.getType(), e.getColor(), nullIfNotInitialized(e.getCar()));
Just map it to a new object and leave out the Car relation, this is the standard approach. You can use MapStruct and just ignore the car field during mapping: http://mapstruct.org/documentation/stable/reference/html/#inverse-mappings
Just don't map the car... Map a field holding the ID and use another method to get the actual Car. I would use a distinctive method name, to differentiate it from the other getters.
class FooEntity {
#Column
private int carId;
public int getCarId() {
return carId;
}
public void setCarId(int id) {
this.carId = id;
}
public Car fetchCar(CarRepository repo) {
return repo.findById(carId);
}
}
You can write query on top of JPA
#Query("select u from Car c")
import org.springframework.data.repository.CrudRepository;
import com.example.model.FluentEntity;
public interface DatabaseEntityRepository extends CrudRepository<FooEntity , int > {
}
As you said
I don't want this to be the case. It will just grab the values and map them to the domain equivalent, if it's not there then null.
Then you just set it to null, because the field car will always not be there.
Otherwise, if you mean not there is that the car not exists in db, for sure a subquery(call the proxy) should be made.
If you want to grab the car when call Foo.getCar().
class Car {
}
class FooEntity {
private Car car;//when call getCar() it will call the proxy.
public Car getCar() {
return car;
}
}
class Foo {
private java.util.function.Supplier<Car> carSupplier;
public void setCar(java.util.function.Supplier<Car> carSupplier) {
this.carSupplier = carSupplier;
}
public Car getCar() {
return carSupplier.get();
}
}
class FooMapper {
public static Function<FooEntity, Foo> mapEntityToDomain() {
return (FooEntity e) -> {
Foo foo = new Foo();
foo.setCar(e::getCar);
return foo;
};
}
}
Make sure you have the db session ,when you call Foo.getCar()
You could try adding state to your repository and influence the mapper. Something like this:
import static com.test.entities.QFooEntity.fooEntity;
import static com.test.entities.QCarEntity.carEntity;
#Repository
public class FooRepository {
private final JPAQuery<FooEntity> query = createQuery().from(fooEntity);
private boolean withCar = false;
public FooRepository getFooByName(String name) {
query.where(fooEntity.name.eq(name));
return this;
}
public FooRepository withCar() {
query.leftJoin(fooEntity.car, carEntity).fetchJoin();
withCar = true;
return this;
}
public Foo fetch() {
FooEntity entity = query.fetchOne();
return FooMapper.mapEntityToDomain(withCar).apply(entity);
}
}
In your mapper, you then include a switch to enable or disable car lookups:
public class FooMapper {
public static Function<FooEntity, Foo> mapEntityToDomain(boolean withCar) {
return e -> {
return new Foo(e.getId(), e.getName(), e.getType(), e.getColor(), withCar ? e.getCar() : null);
};
}
}
If you then use new FooRepository().getFooByName("example").fetch() without the withCar() call, e.getCar() should not be evaluated inside FooMapper
You may want to use the PersistentUnitUtil class to query if an attribute of entity object is already loaded or not. Based on that you may skip the call to corresponding getter as shown below. JpaContext you need to supply to user entity bean mapper.
public class FooMapper {
public Function<FooEntity, Foo> mapEntityToDomain(JpaContext context) {
PersistenceUnitUtil putil = obtainPersistentUtilFor(context, FooEntity.class);
return e -> {
return new Foo(
e.getId(),
e.getName(),
e.getType(),
e.getColor(),
putil.isLoaded(e, "car") ? e.getCar() : null);
};
}
private PersistenceUnitUtil obtainPersistentUtilFor(JpaContext context, Class<?> entity) {
return context.getEntityManagerByManagedType(entity)
.getEntityManagerFactory()
.getPersistenceUnitUtil();
}
}

Spring Data - no property found

I have Entity
#Entity
#Table(name = "messages")
public class Message {
...
#Column(name = "isVisibleForSender")
private boolean isVisibleForSender;
}
and Spring Data repository
#Repository
#Transactional
public interface MessageCRUDRepository extends JpaRepository<Message, Long> {
...
public boolean getVisibleForRecipient(boolean isVisibleForRecipient);
}
When compiling, the program throws me an exception Caused by: org.springframework.data.mapping.PropertyReferenceException: No property getIsVisibleForRecipient found for type Message!
After seeing your comments, you have the wrong method name:
#Entity
#Table(name = "messages")
public class Message {
#Column(name = "isVisibleForRecipient")
private boolean visibleForRecipient;
}
And for the repo:
interface MessageCRUDRepository extends JpaRepository<Message, Long> {
List<Message> findByVisibleForRecipient(Boolean bool);
}
Make sure you have getters and setters for the visibleForRecipient field in the message object

Spring BeanUtils couldn't instantiate generic class

I am trying to implement something for converting between my Entities and DTO.
I have base class for my DTOs (called Models):
public class BaseModel<Entity> implements Model<Entity> {
#Override
public Entity toEntity(Class<Entity> entityClass) {
Entity entityInstance = BeanUtils.instantiate(entityClass);
BeanUtils.copyProperties(this, entityInstance);
return entityInstance;
}
}
But following test doesn't passes:
public class BaseModelTest {
#Entity
public class SomeEntity {
#Id
private Long id;
private String name;
public SomeEntity() {
}
public SomeEntity(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
}
#Test
public void toEntity_givenEntityClass_shouldCreateNewInstance() throws Exception {
//given
BaseModel<SomeEntity> model = new BaseModel();
//when
SomeEntity entity = model.toEntity(SomeEntity.class);
//then
assertNotNull(entity);
}
}
I got exception: (despite the fact, under debugger I see all ctors):
org.springframework.beans.BeanInstantiationException: Failed to instantiate [package.BaseModelTest$SomeEntity]: Is it an abstract class?; nested exception is java.lang.InstantiationException: package.BaseModelTest$SomeEntity
Caused by: java.lang.InstantiationException: package.BaseModelTest$SomeEntity
Caused by: java.lang.NoSuchMethodException: package.BaseModelTest$SomeEntity.<init>()
Currently to create a new SomeEntity instance you need an instance of the enclosing BaseModelTest class. SomeEntity should be an inner static class. Replace:
public class SomeEntity {
with
public static class SomeEntity {
BTW. There is no point in having a DTO class if it maps 1-1 to a model class, it does not add any value, it is only boilerplate code.

Generic Hibernate Dao with Interface model object

I meet some problems with genericDao and model Interface.
I implemented the following GenericHibernateDao :
#Repository
public abstract class GenericHibernateDao<T, PK extends Serializable> implements IGenericDao<T, PK> {
#Autowired
private SessionFactory sessionFactory;
private Class<T> type;
#SuppressWarnings({ "unchecked", "rawtypes" })
public GenericHibernateDao() {
Type t = getClass().getGenericSuperclass();
ParameterizedType pt = (ParameterizedType) t;
type = (Class) pt.getActualTypeArguments()[0];
}
public void setSessionFactory(SessionFactory sessionFactory){
this.sessionFactory = sessionFactory;
}
#Transactional
public T add(T obj){
getSession().persist(obj);
return obj;
}
#Transactional
#SuppressWarnings("unchecked")
public T getById(PK id){
T result = (T) getSession().load(type, id);
return result;
}
#Transactional
#SuppressWarnings("unchecked")
public List<T> list(){
Criteria crit = getSession().createCriteria(type);
return (List<T>) crit.list();
}
...
This works perfectly fine for "normal" objects.
I'm trying to use Interface as type T (ex: IBattery):
public class BatteryDao extends GenericHibernateDao<IBattery, Integer> implements IBatteryDao {
}
-
public interface IBattery {
public int getId();
public double getLevel();
public void setLevel(double _level);
}
-
#Entity
public class SimulationBattery implements IBattery {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(unique = true, nullable = false)
private int id;
#Column(unique = false, nullable = false)
private double level;
#Override
public int getId() {
return id;
}
#Override
public double getLevel() {
return level;
}
#Override
public void setLevel(double _level) {
level = _level;
}
I instanciate IBattery through Spring applicationContext file to load a SimulationBattery implementation.
It works for persist, list (with criteria) but fail with "getById" cause of load, sending :
org.hibernate.MappingException: Unknown entity: ***.***.****.IBattery
That's right cause only implementation (SimulationBattery) are mapped in hibernate.cfg.xml, but I don't understand why I can add, list, but not load...
Someone has explanation ?
Thank you.
Fabien.
(I'm using Hibernate, Spring and Java8)
When you persist an entity, you pass a concrete entity instance to Hibernate. So Hibernate receives an instance of SimulationBattery, for example, and thus knows the type of the entity you're persisting: SimulationBattery.
When you list, you rely on the polymorphic feature of Hibernate: you ask Hibernate to return all the entity instances of IBattery. Hibernate knows all the concrete entity classes that implement this interface (SimulationBattery and ProductionBattery, for example). So it loads them all from the database, and returns them.
But when you ask for one specific entity by ID, all Hibernate knows is that the entity is one of the entities that implement IBattery, and that its ID is the one you pass (42 for example). That is not sufficient. You might want the SimulationBattery 42, or the ProductionBattery 42, and Hibernate doesn't know. Hence the failure.

Categories

Resources