How can I override a javax.persistence.AttributeConverter - java

I have the problem that want to override an AttributeConverter in a subclass of an entity, but the defined converter in the subclass does not get called. According to the AttributeConverter documentation this should be the way, but it does not work for me. What am I doing wrong?
#Entity
#org.hibernate.annotations.DynamicUpdate(value = true)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING)
#DiscriminatorValue("ORDER")
public class Order implements Serializable
{
...
#Column(name = "PRODUCT_SERIALIZED", updatable = false)
#Convert(converter = ProductConverter.class)
protected Product product;
...
}
#Entity
#DiscriminatorValue("CUSTOMER_ORDER")
#Convert(attributeName = "product", converter = CustomerProductConverter.class)
public class CustomerOrder extends Order
{
...

As it seems #Convert is not suitable to override an existing converter for a field of a superclass. I solved it in a different way. I injected a conversionService via CDI into the AttributeConverter of the superclass which I then can specialized.
#Converter
public class ProductConverter implements AttributeConverter<Product, String>
{
ProductConverterService converterBean = null;
#Override
public String convertToDatabaseColumn(Product attribute)
{
return getConverterService().convertToDatabaseColumn(attribute);
}
#Override
public Product convertToEntityAttribute(String dbData)
{
return getConverterService().convertToEntityAttribute(dbData);
}
public ProductConverterService getConverterService()
{
if (converterBean == null)
{
//since ProductConverter is obiously not managed via CDI
converterBean = CDI.current().select(ProductConverterService.class).get();
}
return converterBean;
}
}

Related

Spring Data Repository not working with Generics

I'm playing around with generics and Spring Data repository. I created a simple project with almost zero configuration, entities are in subpackage of main class.
Entity
#Data
#ToString(callSuper = true)
#Entity
public class Person extends GenericEntity {
private String name;
}
#Data
#MappedSuperclass
public class GenericEntity {
#Id
#GeneratedValue
private Integer id;
#CreationTimestamp
#Column(name = "TMS_INSERIMENTO")
private LocalDateTime tmsInserimento;
#UpdateTimestamp
#Column(name = "TMS_AGGIORNAMENTO")
private LocalDateTime tmsAggiornamento;
}
Repository
public interface GenericRepository<T extends GenericEntity> extends JpaRepository<T, Integer> {
}
Service
public List<Person> findAllPeople() {
return genericRepository.findAll();
}
Call to findAll() throws the following exception:
org.springframework.dao.InvalidDataAccessApiUsageException: Not an entity: class com.example.demot.entity.GenericEntity; nested exception is java.lang.IllegalArgumentException: Not an entity: class com.example.demot.entity.GenericEntity
Try with the following
#Data
#MappedSuperclass
public class GenericEntity <T extends GenericEntity> {
...
}
And then
#Data
#ToString(callSuper = true)
#Entity
public class Person extends GenericEntity<Person> {
...
}
And then you need the generic repository which should return generic entities not specific persons
public interface GenericRepository extends JpaRepository<GenericEntity, Integer> {
}
which can be called in service as
public List<GenericEntity> findAllGenericEntities() {
return genericRepository.findAll();
}
And then you can also have a person repository
public interface PersonRepository extends JpaRepository<Person, Integer> {
}
which can be called in service as
public List<Person> findAllPersons() {
return personRepository.findAll();
}

How to ignore LAZY types dynamically on SpringBoot app?

I have a class:
#Entity
#Table(name = "restaurants")
public class Restaurant extends AbstractNamedEntity implements Serializable {
#OneToMany(fetch = FetchType.LAZY, mappedBy = "restaurant")
private Set<Meal> meals = Collections.emptySet();
//other fields, getters, setters, constructors
}
I'm getting my data with Spring Data:
#Repository
public interface RestaurantRepository extends CrudRepository<Restaurant, Integer> {
}
I have REST-controller, which produces JSON data:
#RestController
#RequestMapping(value = RestaurantController.REST_URL, produces = MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8")
public class RestaurantController {
static final String REST_URL = "/restaurants";
#Autowired
private RestaurantRepository repository;
#GetMapping("{id}")
public List<Restaurant> getOne(#PathVariable Integer id) {
return repository.findById(id);
}
}
How to avoid including that LAZY data (set of Meals) to get them to a SQL request?
As I know I need to write a custom JacksonObjectMapper, but I don't know how to do it
You can use #JsonIgnore annotation in order to ignore the mapping of a field. Then you should do this:
#JsonIgnore
#OneToMany(fetch = FetchType.LAZY, mappedBy = "restaurant")
private Set<Meal> meals = Collections.emptySet();
UPDATED
Based what you want to do "Ignore field dynamically when getting one or not getting alls" you can use #NamedEntityGraphs annotation to specific what fields you want to join, then by using #NamedEntityGraph you specify the path and boundaries for a find operation or query and you should use in your custom Repository the #EntityGraph annotation who allows to customize the fetch-graph based what you want to do.
So you should add the following code:
#Entity
#Table(name = "restaurants")
#NamedEntityGraphs({
#NamedEntityGraph(name="Restaurant.allJoins", includeAllAttributes = true),
#NamedEntityGraph(name="Restaurant.noJoins")
})
public class Restaurant extends AbstractNamedEntity implements Serializable {
}
#Repository
public interface RestaurantRepository extends CrudRepository<Restaurant, Integer> {
#EntityGraph(value = "Restaurant.allJoins", type = EntityGraphType.FETCH)
#Override
List<Restaurant> findAll();
#EntityGraph(value = "Restaurant.noJoins", type = EntityGraphType.FETCH)
#Override
Optional<Restaurant> findById(Integer id);
}

get by id in hibernate returns null when id is defined in the parent entity

I have 3 entities, all mapped to the same base table, like this:
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(
discriminatorType = DiscriminatorType.STRING,
name = "disc_type"
)
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "unique_id", nullable = false)
private long id;
}
#DiscriminatorValue(value = DiscType.Values.ONE)
public class Child1 extends Parent {
}
#DiscriminatorValue(value = DiscType.Values.TWO)
public class Child2 extends Parent {
}
What doesn't work for me is when I try get by id for the child entities - I would have expected the following code to work (and adding the matching disc_type filter), but unfortunately - this returns null:
return sessionFactory.getCurrentSession().get(Child1.class, id);
While this returns (as expected) the Parent object:
return sessionFactory.getCurrentSession().get(Parent.class, id);
I need to return a Child1 entity, and converting the Parent to Child1 seems like a pretty bad solution. Ideas on how to get over this?
I have tried following code and it works (hope this helps):
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(
discriminatorType = DiscriminatorType.STRING,
name = "disc_type"
)
#Entity
#Table(name = "parent")
#Data
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "unique_id", nullable = false)
private long id;
}
#Entity
#DiscriminatorValue(value = "ONE")
public class Child1 extends Parent {
}
#Entity
#DiscriminatorValue(value = "TWO")
public class Child2 extends Parent {
}
public interface EntityRepository extends CrudRepository<Parent, Long> {
}
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Component
#AllArgsConstructor
public class ApplicationRunner implements CommandLineRunner {
private final EntityRepository repository;
#Override
public void run(String... args) throws Exception {
repository.save(new Child1());
repository.save(new Child2());
Parent one = repository.findById(1L).get();
System.out.println(one);
Parent two = repository.findById(2L).get();
System.out.println(two);
}
}
}
Output:
Hibernate: insert into parent (unique_id, disc_type) values (null, 'ONE')
Hibernate: insert into parent (unique_id, disc_type) values (null, 'TWO')
Hibernate: select parent0_.unique_id as unique_i2_2_0_, parent0_.disc_type as disc_typ1_2_0_ from parent parent0_ where parent0_.unique_id=?
Parent(id=1)
Hibernate: select parent0_.unique_id as unique_i2_2_0_, parent0_.disc_type as disc_typ1_2_0_ from parent parent0_ where parent0_.unique_id=?
Parent(id=2)
Update:
public interface Child1Repository extends CrudRepository<Child1, Long> {
}
#Component
#AllArgsConstructor
public class ApplicationRunner implements CommandLineRunner {
private final EntityRepository repository;
private final Child1Repository child1Repository;
#Override
public void run(String... args) throws Exception {
repository.save(new Child1());
repository.save(new Child2());
Parent one = repository.findById(1L).get();
System.out.println(one);
Parent two = repository.findById(2L).get();
System.out.println(two);
Child1 child1 = child1Repository.findById(1L).get();
System.out.println(child1);
}
}

Mapping AbstractEntity to SubClassDTO - Single Table Strategy

I'm trying to mapp Entity (TrainingEntity) to DTO, where one of the fields is a Set with ManyToMany reference to an AbstractEntity (CoachEntity) divided by Single Table into two subclasses: ExternalCoach and InternalCoach.
Both subclasses have different data, therefore require two different mappers.
#Entity
#Table(name = "TRAINING")
public class TrainingEntity extends AbstractEntity {
#ManyToMany()
#JoinTable(name = "TRAINING_EMPLOYEE", joinColumns = { #JoinColumn(name = "TRAINING_ID") }, inverseJoinColumns = {
#JoinColumn(name = "COACH_ID") })
private Set<CoachEntity> coachEntities;
#Column(nullable = false)
private TrainingType trainingType;
......some data....
}
Abstract Coach Entity
#Entity
#Table(name = "COACH")
#DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.STRING)
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class CoachEntity extends AbstractEntity {
......some data....
}
Coach Subclasses:
#Entity
#DiscriminatorValue("EXTERNAL")
public class ExternalCoachEntity extends CoachEntity {
......some data....
}
#Entity
#DiscriminatorValue("INTERNAL")
public class InternalCoachEntity extends CoachEntity {
......some data....
}
One mapper for Abstract Coach class won't have acces to subclasses methods and fields, so I need two different for External and Internal. Than I would have to use them in TrainingMapper class, but (example of internal):
public class CustomTrainingMapper {
public static TrainingDto toTrainingDto(TrainingEntity trainingEntity){
if(trainingEntity == null){
return null;
}
if(trainingEntity.getTrainingType().equals(TrainingType.INTERNAL)){
Set<CoachEntity> coachEntities = trainingEntity.getCoachEntities();
Set<CoachDto> coachDtos = CustomInternalCoachMapper.toDTOSet((Set<InternalCoachEntity>)coachEntities);
}
I get:
cannot cast from Set<CoachEntity> to Set<InternalCoachEntity>
Without cast it simply doesn't see mappers methods with subclass input.
The method toDTOSet(Set<InternalCoachEntity>) in the type CustomInternalCoachMapper is not applicable for the arguments (Set<CoachEntity>)
When in mapper I change method input to abstract Coach Class it doesn't see subclasses methods and fields.
Part of InternalMapper:
public class CustomInternalCoachMapper {
public static CoachDto toCoachDto(InternalCoachEntity coachEntity) {
if (coachEntity == null) {
return null;
}
EmployeeDto employeeDto = CustomEmployeeMapper.toEmployeeDto(coachEntity.getEmployeeEntity());
return new InternalCoachDto(coachEntity.getId(), coachEntity.getVersion(), coachEntity.getCreateDate(),
coachEntity.getUpdateDate(), coachEntity.getName(), coachEntity.getSurname(), employeeDto);
}
Is it possible to mapp this AbstractEntity Set into subclasses DTOs?
I also tried with AbstractDto for Coaches, but then I'm facing the same problem with no access to subclasses getters and setters.

Repository Inheritance

I would like to create a repository which performs the basic CRUD operations.
Since I have different kind of photos (CompanyPhoto, CarPhoto, ..), I would prefer to make the JPA repository generic, but also the EJB service as well.
Here is my classes:
#Entity
#Inheritance
#DiscriminatorColumn(name = "DESCRIMINATOR")
#Table(name = "PHOTOS")
public abstract class Photo {
public Photo() {
}
public Photo(String fileName) {
this.fileName = fileName;
// this.file = file;
}
#Id
#GeneratedValue(strategy = GenerationType.TABLE, generator = "PHOTOS_SEQ")
#TableGenerator(name = "PHOTOS_SEQ", table = "SEQUENCE", pkColumnName = "SEQ_NAME", pkColumnValue = "PHOTOS_SEQ", valueColumnName = "SEQ_COUNT", allocationSize = 50)
#Column(nullable = false)
private long id;
#Column(length = 255)
#Size(min = 0, max = 255, message = "{Photo.description.size}")
protected String description;
#Column(nullable = false, length = 255)
#NotNull(message = "{Photo.fileName.notNull}")
#Size(min = 1, max = 255, message = "{Photo.fileName.size}")
protected String fileName;
// ...
#Entity
#DiscriminatorValue("C")
public class CarPhoto extends Photo {
public CarPhoto() {
}
public CarPhoto(String fileName) {
super.fileName = fileName;
}
#ManyToOne(cascade = { CascadeType.DETACH })
#JoinColumn(name = "CARID", nullable = false)
#NotNull
private Car car;
// ...
#Entity
#DiscriminatorValue("P")
public class PersonPhoto extends Photo {
public PersonPhoto() {
}
public PersonPhoto(String fileName) {
super.fileName = fileName;
}
#ManyToOne(cascade = { CascadeType.DETACH })
#JoinColumn(name = "PERSONID", nullable = false)
#NotNull
private Person person;
// ...
#Stateless
#LocalBean
public class PhotoRepository<E> {
// In this class I would like to do create, remove, update and some basic find //operations..
#PersistenceContext
private EntityManager em;
public PhotoRepository() {
}
PhotoRepository(EntityManager em) {
this.em = em;
}
#Override
public E create(E photo) {
em.persist(photo);
return photo;
}
#Override
public E modify(E photo)
{
Class<E> photoClass;
// QUESTION: How am I going to call the getId() method from the object of type E class?
em.find(photoClass, photo.getId()); // This will not work.. =(
E mergedPhoto = em.merge(photo);
return mergedPhoto;
}
// ...
I hope that you understand what I want to perform. A generic repository for different kind of classes which all inherit from the same baseclass. Can you give me some best practices examples?
=)
Best regards
Change the generics definition to say that E has to be any type that extends from Photo. Then you will be able to access methods of the Photo class on variables of type E
#Stateless
#LocalBean
public class PhotoRepository<E extends Photo> {
You can use the following method to retrieve the actual class.
public Class getEntityClass() {
ParameterizedType parameterizedType =
(ParameterizedType) getClass().getGenericSuperClass();
return (Class) parameterizedtype.getActualTypeArguments()[0];
}
Ir you are using spring you should also take a look at spring-data-jpa - it provides such generic repositories implementation.
You can get the Id from an Entity using,
entityManagerFactory.getPersistenceUnitUtil().getIdentifier(object);
Although you do not need to call find() before merge(), just call merge(), it will do the find if required.

Categories

Resources