Generic Hibernate Dao with Interface model object - java

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.

Related

Am I missing something if I use my entity class without #Id in Spring Data JDBC?

I am new to spring.
I just tried successfully using an entity class without #Id in Spring Data JDBC
Custom query was added in my repository for retrieving data from 2 mysql tables and returning an entity having the joined table data.
If I plan to use only custom queries, am I missing anything here?
Here's my entity class without #Id or #Entity:
public class Item
{
private long id;
private String code;
private String itemName;
private String groupName;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
}
Repository layer:
#Repository
public interface ItemRepository extends PagingAndSortingRepository<Item, Long>
{
#Query("SELECT a.id, a.code, a.name AS item_name,
b.name as group_name from item a, item_group b
WHERE a.group_id = b.id AND a.id=:id")
Item findItemById(#Param("id") Long id);
}
Service layer:
#Service
public class ItemServiceImpl implements ItemService
{
private final ItemRepository itemRepository;
public ItemServiceImpl(ItemRepository itemRepository)
{
this.itemRepository = itemRepository;
}
#Override
#Transactional(readOnly=true)
public Item findItemById(Long id)
{
return itemRepository.findItemById(id);
}
}
My updated main Configuration class in response to answer of Jens:
#SpringBootApplication
#EnableJdbcRepositories
public class SpringDataJdbcApplication extends AbstractJdbcConfiguration
{
public static void main(String[] args)
{
SpringApplication.run(SpringDataJdbcApplication.class, args);
}
#Bean
#ConfigurationProperties(prefix="spring.datasource")
public DataSource dataSource()
{
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
return dataSourceBuilder.build();
}
#Bean
NamedParameterJdbcOperations namedParameterJdbcOperations(DataSource dataSource)
{
return new NamedParameterJdbcTemplate(dataSource);
}
#Bean
PlatformTransactionManager transactionManager()
{
return new DataSourceTransactionManager(dataSource());
}
}
If you don't get any exceptions you should be fine. There shouldn't be anything in Spring Data JDBC that silently breaks when the id is not specified.
The problem is though: I don't consider it a feature that this works, but just accidental behaviour. This means it might break with any version, although replacing these methods with custom implementations based on a NamedParameterJdbcTemplate shouldn't be to hard, so the risk is limited.
The question though is: Why don't you add the #Id annotation, after all your entity does have an id. And the whole idea of a repository conceptually requires an id.
If it's working and you really don't want to use the annotations, you can do it. But I think that it's unnecessary complication. You can expect errors that would not be there if you had used the annotations and code will be harder to debug. If you are new in Spring I recommend to use annotations. But after all it depend on you how will you design your applications. For sure advantage of approach without annotations is higher control about database.

Is it possible in Hibernate to override entity field name in a child entity?

I'm writing an app using Spring Boot, Hiberane and Spring Data.
I have two tables in the db: tableA and tableB.
They have some common fields but their id's,name's are different, also I've created a basic model for them to contain some common fields, right now it looks something like this:
// BaseModel
#MappedSuperclass
public abstract class BaseModel implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name="common_field_1")
private String commonField1;
#Column(name="common_field_2")
private String commonField2;
#Column(name="common_field_3")
private String commonField3;
}
// ExactModel 1
#Entity
#Table(name="table1" ,schema="schema")
public class ExactModel1 extends BaseModel {
#Id
#Basic(fetch=FetchType.EAGER)
#Column(name="exact_model_id_1", nullable=false)
private long exactModel1Id;
private String exactField1;
}
// ExactModel 2
#Entity
#Table(name="table2" ,schema="schema")
public class ExactModel2 extends BaseModel {
#Id
#Basic(fetch=FetchType.EAGER)
#Column(name="exact_model_id_2", nullable=false)
private long exactModel2Id;
private String exactField2;
}
And I have some generic logic which implements some general crud logic which works for classes which extend BaseModel:
public abstract class BaseServiceImpl<M extends BaseModel, R extends BaseRepository<M>> implements BaseService<M, Long> {
private final R repository;
public BaseServiceImpl(R repository) {
this.repository = repository;
}
#Override
public M save(M model) {
return repository.save(model);
}
#Override
public List<M> saveAll(List<M> models) {
return repository.saveAll(models);
}
#Override
public M findById(Long id) {
return repository.getOne(id);
}
#Override
public List<M> findAllById(List<Long> ids) {
return repository.findAllById(ids);
}
#Override
public List<M> findAll() {
return repository.findAll();
}
#Override
public M update(M model) {
return repository.save(model);
}
#Override
public List<M> updateAll(List<M> models) {
return repository.saveAll(models);
}
#Override
public void delete(M model) {
repository.delete(model);
}
#Override
public void delteById(Long id) {
repository.deleteById(id);
}
#Override
public void deleteInBatch(List<M> models) {
repository.deleteInBatch(models);
}
#Override
public Long countModels() {
return repository.count();
}
}
The thing is now I need to get somehow the id of the entity I work with in this generic logic, but there is no id field in BaseModel, so I can't just use baseModel.getId().
The question: is it possible to define a mock id field in BaseModel and override it in the child classes, so I can use this id in the generic methods but Hibernate fills the actual ids on the runtime for me?

Inferred type 'S' for type parameter 'S' is not within its bound; should extend 'ua.com.store.entity.Country

I have a problem with my CountryServiceImpl,when I want realize method findOne in CountryServiceImpl it tells me "Inferred type 'S' for type parameter 'S' is not within its bound; should extend 'ua.com.store.entity.Country".
I wanted to fix by myself, but I don't understand what this means.
Could you please help me with this issue.
Thank you.
#Entity
#Getter
#Setter
#NoArgsConstructor
#ToString
public class Country {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String countryName;
#OneToMany(mappedBy = "country")
private Set<Brand> brands = new HashSet<Brand>();
}
public interface CountryDAO extends JpaRepository<Country, Integer> {
#Query("from Country c where c.countryName=:name")
Country findByCountryName(#Param("name") String name);
}
public interface CountryService {
void save(Country country);
void delete(Country country);
List<Country> findAll();
Country findOne(int id);
Country findByCountryName(String name);
}
#Service
public class CountryServiceImpl implements CountryService {
#Autowired
private CountryDAO dao;
#Override
public void save(Country country) {
dao.save(country);
}
#Override
public void delete(Country country) {
dao.delete(country);
}
#Override
public List<Country> findAll() {
return dao.findAll();
}
#Override
public Country findOne(int id) {
return dao.findOne(id);
}
#Override
public Country findByCountryName(String name) {
return dao.findByCountryName(name);
}
}
Spring documentation defines methods getOne as follows
<S extends T> Optional<S> findOne(Example<S> example)
In your method your input parameter is 'id' of type int but not bounded to interface Example.
To find an entity with it 'id' you can use the method
Optional<T> findById(ID id)
According to your implementation you may write it
#Override
public Country findOne(int id) {
return dao.findById(id);
}
It is possible to be relevant about spring-boot version. I meet the same issue when my spring-boot version is 2.0.1.RELEASE. But after change the spring-boot version to the 1.5.9.RELEASE, it is resolved.
A 100% working solution is following:
#Override
public Country findOne(int id) {
return dao..findById(id).orElse(null);
}
I just solved this problem in my program. I don't use the findOne method, I was just saving initial data with the save method. It turns out that I had copied my repo interface from another repo interface, and forgot to update the object in "extends JpaRepository<Object, Long>" to the new object.
You need to change
from
public T getOne(ID id) {
return repository.getOne(id);
}
To
public Optional<T> getOne(ID id) {
return repository.findById(id);
}
This Worked for me...
#Override
public Country findOne(int id) {
return dao.findById(id).orElse(null);
}
I got the same problem.
ua.com.store.entity.Country -> It's like adding an external entity
You must import the correct directory of the entity "Counrty" that you will use in your project.
e.g
import com.RomanSyhock.Entity.Country;

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();
}
}

How to implement generic business logic methods from JPA database entity class?

I want suggestion regarding a scenario I've been thinking of doing if possible. Suppose I have some JPA database entity class like:
#Entity
public class Person {
#Column(name = "ID")
private Long id;
#Column(name = "FIRST_NAME")
private String firstName;
#Column(name = "LAST_NAME")
private String lastName;
public String getFirstName(){
return this.firstName;
}
public void setFirstName(String firstName){
this.firstName = firstName;
}
public String getLastName(){
return this.lastName;
}
public void setLastName(String lastName){
this.lastName = lastName;
}
}
I am using EJB services. I can use separate business logic methods to make CRUD operation over these entities. Is it possible to use a generic template CRUD operations for these entity classes? Like if I want to create new person I will provide the Person entity class and fields to set as parameter and my generic method will create a new Person record and will do the same job for Read, Update and Delete operation as well.
Any respective example will be highly appreciated.
Thank You
Using EJB and JPA
You can consider an abstract class for the service layer:
public abstract class AbstractFacade<E extends Serializable,
PK extends Serializable> {
private final transient Class<E> entityClass;
public AbstractFacade(final Class<E> entityClass) {
this.entityClass = entityClass;
}
protected abstract EntityManager getEntityManager();
public void create(final E entity) {
final EntityManager entityManager = getEntityManager();
entityManager.persist(entity);
}
public final E find(final PK id) {
return getEntityManager().find(entityClass, id);
}
// Other common operations
}
And a particular service:
#Stateless
public class PersonFacade extends AbstractFacade<Person, Long> {
#PersistenceContext(unitName = "MyPU")
private EntityManager em;
#Override
protected EntityManager getEntityManager() {
return em;
}
public PersonFacade() {
super(Person.class);
}
// Other methods of this service
}
Using Spring and Hibernate
You could have a abstract base class for common DAO methods.
public abstract class AbstractDAO<E extends Serializable,
PK extends Serializable> {
private final transient Class<E> entityClass;
public AbstractDAO(final Class<E> entityClass) {
this.entityClass = entityClass;
}
protected abstract EntityManager getEntityManager();
public final E find(final PK id) {
return getEntityManager().find(entityClass, id);
}
// Another common methods
}
In every DAO implementation, you can put particular methods for that DAO.
#Repository
public final class PersonDAO extends AbstractDAO<Person, Long> {
#Autowired
private transient EntityManagerFactory emf;
public PersonDAO() {
super(Person.class);
}
#Override
protected EntityManager getEntityManager() {
return emf.createEntityManager();
}
// particular methods for this DAO
}
What about if the user not exists? Put this logic in the service layer.
#Service
public final class PersonService {
private static final Logger LOG = LoggerFactory.getLogger(PersonService.class);
#Autowired
private transient PersonDAO personDAO;
public Person findPerson(final Long id) {
return personDAO.find(id);
}
}
If you are using Spring then use Spring Data which will do all this for you.
http://docs.spring.io/spring-data/jpa/docs/1.4.2.RELEASE/reference/html/repositories.html#repositories.core-concepts

Categories

Resources