I got 2 classes A and B which both got a company. The company of A has got a little more information than the company of B (Company has an id while CNCompany doesn´t). I want to map all fields using orika. If all fields an object are null, I want the object to be null!
I tried to express this with an unit test. What has to be done to get this running?
public class A {
private Company company;
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
public class B {
private CNCompany company;
public CNCompany getCompany() {
return company;
}
public void setCompany(CNCompany company) {
this.company = company;
}
}
public class Company {
private Id id;
private AccountId accountId;
public Id getId() {
return id;
}
public void setId(Id id) {
this.id = id;
}
public AccountId getAccountId() {
return accountId;
}
public void setAccountId(AccountId accountId) {
this.accountId = accountId;
}
}
public class CNCompany {
private AccountId accountId
public AccountId getAccountId() {
return accountId;
}
public void setAccountId(AccountId accountId) {
this.accountId = accountId;
}
}
public class MyMapper extends ConfigurableMapper {
#Override
protected void configure(MapperFactory factory) {
factory.classMap(A.class, B.class) //
.mapNulls(false).mapNullsInReverse(false) //
.byDefault() //
.register();
}
}
#Test
public void testMap() throws Exception {
A a = new A();
Company company = new Company();
Id id = new Id();
id.setValue("1");
company.setId(id);
a.setCompany(company);
MyMapper myMapper = new MyMapper();
B outcome = myMapper.map(a, B.class);
assertThat(outcome.getCompany(), is(nullValue()));
}
If I understand correctly you want getCompany to return null if the Company object contains only null values.
In Orika you can control conversion with a custom converter. For your example that might look something like:
public class CompanyConverter extends CustomConverter<Company, CNCompany> {
public CNCompany convert(Company source, Type<? extends CNCompany> destinationType) {
if (isNothingButNulls(source)) {
return null;
}
final CNCompany newCompany = new CNCompany();
// do your thing
return newCompany;
}
}
I've never written a CustomConverter that can return null so I'm not 100% sure this will work but it should. Note that the converter will still need to be registered. The documentation I linked shows how to register depending on what level you want the converter at.
Related
I want to map a EmployeeDto to a EmployeeValue. Consider the following classes:
public class EmployeeDto {
private String telephoneNumber;
private Integer companyId;
public String getTelephoneNumber() {
return telephoneNumber;
}
public void setTelephoneNumber(String telephoneNumber) {
this.telephoneNumber = telephoneNumber;
}
public Integer getCompanyId() {
return companyId;
}
public void setCompanyId(Integer companyId) {
this.companyId = companyId;
}
}
public class EmployeeValue {
private String telephoneNumber;
private Company company;
public String getTelephoneNumber() {
return telephoneNumber;
}
public void setTelephoneNumber(String telephoneNumber) {
this.telephoneNumber = telephoneNumber;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
public class Company {
private Integer id;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
I am trying to map those classes using a mapstruct-mapper:
#Mapper(componentModel = "cdi")
public interface EmployeeDto2EmployeeValueMapper {
#Mapping(source ="companyId", target = "company.id")
EmployeeValue map(EmployeeDto dto);
}
This is working just perfectly fine, as this test runs green:
class EmployeeDto2EmployeeValueMapperTest {
private static final String TELEPHONE_NUMBER = "telephoneNumber";
private static final int COMPANY_ID = 1;
private EmployeeDto2EmployeeValueMapper classUnderTest = Mappers.getMapper(EmployeeDto2EmployeeValueMapper.class);
#Test
void map() {
EmployeeDto employeeDto = new EmployeeDto();
employeeDto.setTelephoneNumber(TELEPHONE_NUMBER);
employeeDto.setCompanyId(COMPANY_ID);
EmployeeValue outcome = classUnderTest.map(employeeDto);
assertThat(outcome.getTelephoneNumber(), is(TELEPHONE_NUMBER));
assertThat(outcome.getCompany().getId(), is(COMPANY_ID));
}
}
Now, if I add unmappedSourcePolicy = ReportingPolicy.ERROR to the mapper, that is
#Mapper(componentModel = "cdi", unmappedSourcePolicy = ReportingPolicy.ERROR)
public interface EmployeeDto2EmployeeValueMapper {
#Mapping(source ="companyId", target = "company.id")
EmployeeValue map(EmployeeDto dto);
}
the build fails with the following error message:
Unmapped source properties: "telephoneNumber".
To me this seems like a bug, because those field actually got mapped (as my test proved before).
Do you have any ideas on this?
With respect to the mapstruct issue tracker the following entry seems to cover your question.
Issue: unmappedSourcePolicy set to ERROR leads to a nested Bean issue #1881
https://github.com/mapstruct/mapstruct/issues/1881
According to issue comments a fix will be included in the upcoming release 1.4.0. However, I could not discover when this release will happen.
At first, i know this is duplicate question but marked answer didn't solve the error.
I'm trying to create generic Dao interface within my Room Persistance Library following this guide but cannot solve error "Type of the parameter must be a class annotated with #Entity or a collection/array of it" for the generic T in GenericDao.
GenericDao:
#Dao
public interface GenericDao<T> {
#Insert
long insert(T object);
#Update
void update(T... objects);
#Delete
void delete(T... objects);
}
Inherited Dao:
#Dao
public abstract class ExerciseDao implements GenericDao<Exercise> {
#Query("SELECT * FROM exercises")
public abstract LiveData<List<Exercise>> getAllExercises();
}
Entity:
#Entity(tableName = "exercises",
indices = {#Index(value = {"name"}, unique = true)})
public class Exercise {
#PrimaryKey (autoGenerate = true)
int id;
#ColumnInfo(name = "name")
private String name;
#ColumnInfo(name = "muscle_part")
private int musclePart;
#ColumnInfo(name = "is_shown")
private boolean isShown;
public Exercise(String name, int musclePart, boolean isShown) {
this.name = name;
this.musclePart = musclePart;
this.isShown = isShown;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getMusclePart() {
return musclePart;
}
public void setMusclePart(int musclePart) {
this.musclePart = musclePart;
}
public boolean isShown() {
return isShown;
}
public void setShown(boolean shown) {
isShown = shown;
}
}
I've tried:
Change GenericDao to abstract class.
Add/remove #Dao annotation in GenericDao
Add Generic Entity
Override methods in child Daos
...
But always with the error.
Has anyone an idea how to solve this problem?
I have a repository class:
public interface WorkOrderRepository extends JpaRepository<WorkOrderDTO, Integer> {
#Query(value = "SELECT * FROM (SELECT * FROM workorder) Sub1 INNER JOIN (SELECT wo_number, GROUP_CONCAT(service_type SEPARATOR ', ') AS 'service_types' FROM service_type GROUP BY wo_number) Sub2 ON Sub1.wo_number=Sub2.wo_number WHERE fleet_company_id=?1 AND (order_status='On-Bidding' OR order_status='Draft')")
Collection<WorkOrderDTO> findWorkOrdersByFleet(Long fleetCompanyID);
#Query(value = "SELECT * FROM workorder WHERE fleet_company_id=?1")
Collection<WorkOrderDTO> findWorkOrdersByFleet1(Long fleetCompanyID);
}
And an entity class:
#Entity
#Table(name="workorder")
public class WorkOrder implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="wo_number")
private Long woNumber;
#ManyToOne(optional=false, cascade=CascadeType.ALL)
#JoinColumn(name = "vehicle_id")
private Vehicle vehicle;
#ManyToOne(optional=false, cascade=CascadeType.ALL)
#JoinColumn(name = "fleet_company_id")
private FleetCompany fleetCompany;
#Column(name="order_title")
private String orderTitle;
#Column(name="order_date")
private String orderDate;
#Column(name="order_time")
private String orderTime;
#Column(name="order_status")
private String orderStatus;
#Column(name="ref_number")
private String refNumber;
#Column(name="proposals")
private int proposals;
#Transient
private String serviceTypes;
public WorkOrder() {
super();
}
public Long getWoNumber() {
return woNumber;
}
public void setWoNumber(Long woNumber) {
this.woNumber = woNumber;
}
public String getOrderTitle() {
return orderTitle;
}
public void setOrderTitle(String orderTitle) {
this.orderTitle = orderTitle;
}
public String getOrderDate() {
return orderDate;
}
public void setOrderDate(String orderDate) {
this.orderDate = orderDate;
}
public String getOrderTime() {
return orderTime;
}
public void setOrderTime(String orderTime) {
this.orderTime = orderTime;
}
public String getOrderStatus() {
return orderStatus;
}
public void setOrderStatus(String orderStatus) {
this.orderStatus = orderStatus;
}
public String getRefNumber() {
return refNumber;
}
public void setRefNumber(String refNumber) {
this.refNumber = refNumber;
}
public int getProposals() {
return proposals;
}
public void setProposals(int proposals) {
this.proposals = proposals;
}
public Vehicle getVehicle() {
return vehicle;
}
public void setVehicle(Vehicle vehicle) {
this.vehicle = vehicle;
}
public FleetCompany getFleetCompany() {
return fleetCompany;
}
public void setFleetCompany(FleetCompany fleetCompany) {
this.fleetCompany = fleetCompany;
}
public String getServiceTypes() {
return serviceTypes;
}
public void setServiceTypes(String serviceTypes) {
this.serviceTypes = serviceTypes;
}
}
and I have a pojo that extends the entity class:
public class WorkOrderDTO extends WorkOrder {
private String service_types;
public WorkOrderDTO() {
super();
}
public WorkOrderDTO(String service_types) {
this.service_types = service_types;
}
public String getService_types() {
return service_types;
}
public void setService_types(String service_types) {
this.service_types = service_types;
}
}
I want to pass the POJO WorkOrderDTO to the JpaRepository instead of the entity for it to map column service_types which is not part of the entity class. But I have autowiring problems when I set WorkOrderDTO instead ofWorkOrder. Maybe, it is some annotations problem. I didn't put any annotations to the POJO.
You could use the "new" Operator. You must create a constructor in WorkOrderDTO with the values you need, e.g.
public WorkOrderDTO(String serviceTypes) {
this.service_types = serviceTypes;
}
then you can use it like that in a jpql - query:
#Query(value = "SELECT new your.package.WorkorderDTO(w.<select servicetypes somehow>) FROM workorder w WHERE fleet_company_id=?1")
However, I find your first query confusing, I think it is supposed to be a native query... There you can't use the "new" operator.
Maybe it is possible for you to map the ServiceType like Vehicle or FleetCompany as a List? Then you could concatenate just the values in the List for your DTO.
EDIT: You could use #OneToMany to map a List, as it is probably in your Vehicle class for WorkOrder, just to clarify my previous paragraph.
I have 3 classes - Patient, AllergyList and Allergy. Both Patient and Allergy are #Entity and AllergyList simply has a variable with a list of Allergiest.
#Entity
public class Allergy {
private String id;
private String name;
public Allergy(String i, String n) {
id=i;
name=n;
}
#Id
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String n) {
this.name = n;
}
}
The Allergy class MUST have an id, and is referenced by other class in my program.
public class AllergyList {
private List<Allergy> allergies = new ArrayList<Allergy>();
public List<Allergy> getAllergies() {
return allergies;
}
public void setAllergies(List<Allergy> a) {
this.allergies = a;
}
}
I need to have this class, because I have inherited the program, and cannot change the existing codebase.
#Entity
public class Patient {
private String id;
private AllergyList allergyList = new AllergyList();
public Patient() {}
#Id
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public AllergyList getAllergyList() {
return allergyList;
}
public void setAllergyList(AllergyList allergyList) {
this.allergyList = allergyList;
}
}
And the main...
public static void main(String[] args) {
Patient patient = new Patient();
patient.setId("100001");
List<Allergy> allergies = new ArrayList<Allergy>();
allergies.add(new Allergy("1","Dust"));
allergies.add(new Allergy("3","Apple"));
allergies.add(new Allergy("4","Bee"));
patient.allergyList.setAllergies(allergies);
.
.
session.save(patient);
.
.
}
So the above three classes are what I'm given. I need to create allergy_list table that contains the following:
allergy_list
patient_id
allergy_id
I know I can create a List inside the Patient class, but the way the program is written I need to access the Allergy through the AllergyList class. How can I annotate to make this happen? Is it even possible?
This is my Controller method, i am trying to read my database by providing zip, cityname and province name.
#RequestMapping(value = "/retrieve", method = RequestMethod.GET)
public #ResponseBody String retrieveObjectThroughAjax(ModelMap model){
//Calling Service Method to read data according to zip,cityName and province provide
PropertyItems propertyItems=getPropertyTypeandAddressService.readAddressFromZip("H2H-
2N3","Montreal","Quebec");
Gson gson = new Gson();
String json = null;
try{
json = gson.toJson(propertyItems); // serializing object
}catch(Exception e){
logger.error(Constants.METHOD_INSIDE_MESSAGE +"getAuthors",e);
}
logger.debug(json);
return json;
}
}
Service Method
#Service
public class GetPropertyTypeandAddressServiceImpl implements GetPropertyTypeandAddressService{
#Autowired
private GetPropertyTypeandAddressDAO getPropertyTypeandAddressDAO;
#Transactional
public PropertyItems readAddressFromZip(String zipCode,String cityName,String provinceName){
PropertyItems propertyItems=getPropertyTypeandAddressDAO.getAddressFromZip(zipCode, cityName, provinceName);
Hibernate.initialize(propertyItems);
return propertyItems;
}
}
DAO Method
#Repository
public class GetPropertyTypeandAddressDAOimp implements GetPropertyTypeandAddressDAO{
#Autowired
private SessionFactory sessionFactory;
#Override
public PropertyItems getAddressFromZip(String zipCode,String cityName,String provinceName) {
PropertyItems propertyitems = new PropertyItems();
Criteria criteria = sessionFactory.getCurrentSession().createCriteria(PropertyItems.class,"propertyItemsClass");
if(zipCode != null){
criteria.createAlias("propertyItemsClass.address","address");
criteria.add(Restrictions.eq("address.zip",zipCode));
List<PropertyItems> propertyitem = criteria.list();
if(propertyitem.size()>0){
propertyitems = propertyitem.get(0);
}
}
else if(cityName != null){
criteria.createAlias("propertyItemsClass.address","address");
criteria.add(Restrictions.eq("address.city","city"));
criteria.add(Restrictions.eq("city.cityname",cityName));
List<PropertyItems> propertyitem = criteria.list();
if(propertyitem.size()>0){
propertyitems = propertyitem.get(0);
}
}
else if(provinceName != null){
criteria.createAlias("propertyItemsClass.address","address");
criteria.add(Restrictions.eq("address.city","city"));
criteria.add(Restrictions.eq("city.provinces","provinces"));
criteria.add(Restrictions.eq("provinces.provinceName",provinceName));
List<PropertyItems> propertyitem = criteria.list();
if(propertyitem.size()>0){
propertyitems = propertyitem.get(0);
}
}
return propertyitems;
}
}
Console Error
09:53:56,988 ERROR HelloController:567 - Inside Method: getAuthors org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.agilemaple.common.entity.Property.propertyType, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383)
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:375)
As requested my Property Items Look like this
Entity:
Propert Items
#Entity
#Table(name="web_property_item")
public class PropertyItems {
#Id
#GeneratedValue
private Integer id;
private String name;
#ManyToOne
#JoinColumn(name="property_type_id")
private PropertyType propertyType;
#OneToOne(mappedBy="propertyItems",cascade=CascadeType.ALL)
private Address address;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public PropertyType getPropertyType() {
return propertyType;
}
public void setPropertyType(PropertyType propertyType) {
this.propertyType = propertyType;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
Entity : Property Type
#Entity
#Table(name="web_property_type")
public class PropertyType {
#Id
#GeneratedValue
private Integer id;
private String name;
#ManyToOne
#JoinColumn(name="property_id")
private Property property;
#OneToMany(fetch = FetchType.LAZY,mappedBy="propertyType", cascade=CascadeType.ALL)
private Set<PropertyItems> propertyItems;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Property getProperty() {
return property;
}
public void setProperty(Property property) {
this.property = property;
}
public Set<PropertyItems> getPropertyItems() {
return propertyItems;
}
public void setPropertyItems(Set<PropertyItems> propertyItems) {
this.propertyItems = propertyItems;
}
}
The problem in hibernate. Your field Set of properties has Lazy fetch method, it means that it will try to get when you call method get of this set. When u calling tojson methods, gson calls all get methods of object but in this moment hibernate session is close and hibernate can't open it in controller. I've faced with the same problem but directly on JSP. In a three weeks i resolved it by one more property for hibernate ( in your case) and I write code to opening session in view interceptor. I'm underground just right now, so I can't show property, but in a hour I will edit this answer and add property.
Added:
I remembered ! property is: hibernate.enable_lazy_load_no_trans = true
If it won't help, I will add code of opensessioninviewinterceptor.
#Override
public void addInterceptors(InterceptorRegistry registry) {
OpenSessionInViewInterceptor sessionInViewInterceptor = new OpenSessionInViewInterceptor();
sessionInViewInterceptor.setSessionFactory(sessionFactory());
}