I am creating a REST API using Spring Boot and for my update method I try to map a DTO Object to a domain one:
public ItemDTO updateItem(Long departmentId, Long itemId, ItemDTO itemDTO) throws ApiException {
Department department = departmentRepository.findById(departmentId).orElseThrow(
() -> new ApiException("Department with this id does not exist ",
HttpStatus.NOT_FOUND));
Item foundItem = validateItem(itemId);
Item convertedItem = mapper.convertToType(itemDTO, Item.class);
if (convertedItem.getStock() < 0) {
throw new ApiException("Stock must be a positive value", HttpStatus.BAD_REQUEST);
}
if (convertedItem.getStockThreshold() < 0) {
throw new ApiException("Stock threshold must be a positive value", HttpStatus.BAD_REQUEST);
}
itemDTO.setId(itemId);
itemDTO.setDepartment(mapper.convertToType(department, DepartmentDTO.class));
foundItem = mapper.convertToType(itemDTO, Item.class);
itemRepository.save(foundItem);
// ItemDTO returnItem = mapper.convertToType(convertedItem, ItemDTO.class);
logger.info("Updated item");
return itemDTO;
}
And this is my model mapper
#Component
public class Mapper {
private final ModelMapper modelMapper;
private final Logger logger = LoggerFactory.getLogger(ModelMapper.class);
public Mapper(ModelMapper modelMapper) {
this.modelMapper = modelMapper;
}
public <T> T convertToType(Object source, Class<T> resultClass) {
logger.debug("converted object from " + source.getClass().getSimpleName() + " to " + resultClass.getSimpleName());
return modelMapper.map(source, resultClass);
}
}
At this line foundItem = mapper.convertToType(itemDTO, Item.class); it's the problem, before this conversion my foundItem would have a department, after conversion, the department field is null.
Here are my domain and DTO classes
#NoArgsConstructor
#Getter
#Setter
public class DepartmentDTO {
private Long id;
private String description;
private List<Item> items;
public DepartmentDTO(long id, String description) {
this.id = id;
this.description = description;
}
#Override
public String toString() {
return "DepartmentDTO{" +
"id=" + id +
", description='" + description + '\'' +
", items=" + items +
'}';
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DepartmentDTO that = (DepartmentDTO) o;
return Objects.equals(id, that.id) && Objects.equals(description, that.description) && Objects.equals(items, that.items);
}
// #Override
// public int hashCode() {
// return Objects.hash(id, description, items);
// }
}
#NoArgsConstructor
#Entity
#Getter
#Setter
#Table(name = "DEPARTMENTS")
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String description;
#OneToMany(mappedBy = "department", fetch = FetchType.EAGER,
cascade = CascadeType.ALL)
private List<Item> items;
public Department(Long id, String description) {
this.id = id;
this.description = description;
}
#Override
public String toString() {
return "Department{" +
"id=" + id +
", description='" + description + '\'' +
", items=" + items +
'}';
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Department)) return false;
Department that = (Department) o;
return id.equals(that.id) && Objects.equals(description, that.description) && Objects.equals(items, that.items);
}
#Override
public int hashCode() {
return Objects.hash(id, description, items);
}
}
#AllArgsConstructor
#NoArgsConstructor
#Setter
public class ItemDTO {
private Long id;
private String description;
private String price;
private int stock;
private int stockThreshold;
private DepartmentDTO department;
public Long getId() {
return id;
}
public String getDescription() {
return description;
}
public String getPrice() {
return price;
}
public int getStock() {
return stock;
}
public int getStockThreshold() {
return stockThreshold;
}
public Long getDepartment() {
return department.getId();
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ItemDTO itemDTO = (ItemDTO) o;
return stock == itemDTO.stock && stockThreshold == itemDTO.stockThreshold &&
id.equals(itemDTO.id) && description.equals(itemDTO.description) &&
price.equals(itemDTO.price) && department.equals(itemDTO.department);
}
#Override
public int hashCode() {
return Objects.hash(id, description, price, stock);
}
}
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#Entity
#Table(name = "ITEMS")
public class Item {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Long id;
private String description;
private String price;
private int stock;
private int stockThreshold = 5;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "department_id", nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
#JsonIgnore
private Department department;
#Override
public String toString() {
return "Item{" +
"id=" + id +
", description='" + description + '\'' +
", price='" + price + '\'' +
", stock=" + stock +
", department=" + department +
'}';
}
}
Related
when I use #OrderColumn annotation, Hibernate returns collection with 42075 [Null] records, but without #OrderColumn everything works perfectly why? I want to use field "OrderNumber" to have always ordered entity by this field. The type of this "OrderNumber" on PostgreSQL side is "serial" with auto increasing count.
DocTestEntity:
#Entity
#Table(name = "`Document`")
public class DocTestEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "`Document_ID`")
private Integer id;
#ManyToMany
#JoinTable(name = "`DocumentEmployee`",
joinColumns = #JoinColumn(name = "`Document_ID`"),
inverseJoinColumns = #JoinColumn(name = "`Employee_ID`"))
#OrderColumn(name ="`OrderNumber`", updatable = false, insertable = false)
private List<EmployeeTestEntity> employeeEntityList;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public List<EmployeeTestEntity> getEmployeeEntityList() {
return employeeEntityList;
}
public void setEmployeeEntityList(List<EmployeeTestEntity> employeeEntityList) {
this.employeeEntityList = employeeEntityList;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DocTestEntity docEntity = (DocTestEntity) o;
return Objects.equals(id, docEntity.id);
}
#Override
public int hashCode() {
return Objects.hash(id);
}
#Override
public String toString() {
return "DocTestEntity{" +
"id=" + id +
'}';
}
}
EmployeeTestEntity:
#Entity
#Table(name = "`Employee`")
public class EmployeeTestEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "`Employee_ID`")
private Integer id;
#Column(name = "`Employee_name`")
private String name;
#Column(name = "`Employee_surname`")
private String surname;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String employeeName) {
this.name = employeeName;
}
public String getSurname() {
return surname;
}
public void setSurname(String employeeSurname) {
this.surname = employeeSurname;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EmployeeTestEntity that = (EmployeeTestEntity) o;
return Objects.equals(id, that.id);
}
#Override
public int hashCode() {
return Objects.hash(id);
}
#Override
public String toString() {
return "EmployeeTestEntity{" +
"id=" + id +
'}';
}
}
And Test DAO:
#Stateless
public class DocTestDAO {
#PersistenceContext
private EntityManager em;
public DocTestEntity selectDocumentByID(Integer id) {
var result = em.createQuery("SELECT DISTINCT a from DocTestEntity a " +
" WHERE a.id = :id ", DocTestEntity.class)
.setParameter("id", id)
.getResultStream()
.map(Optional::ofNullable)
.findFirst()
.flatMap(Function.identity())
.orElse(null);
System.out.println("List records count is: " + result.getEmployeeEntityList().size());
return result;
}
}
You don't need the OrderNumber column to be autoincremented, the hibernate can manage the order column itself depending on the order of the items in the collection employeeEntityList.
You should make the OrderNumber to be insertable and updatable (true by default):
#OrderColumn(name = "OrderNumber")
I would recommend cleaning up the code, removing these apostrophes from the column names
"`DocumentEmployee`" should be "DocumentEmployee". You mentioned that everything could work without #OrderColumn, so I suppose apostrophes don't affect the functionality but look weird.
Please let me know if this still doesn't work after mentioned updates.
I work on a Java Spring boot app where I get the error of Hot-swap failed and schema change is not implemented and the operation is not supported by the VM. Afterward, the table is truncated and have no data at all.
I have 2 models provided below,
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
#NotNull
#NotEmpty
private String name;
#Column(name = "countryName")
#NotNull
#NotEmpty
private String countryName;
#Column(name = "currencyName")
#NotNull
#NotEmpty
private String currencyName;
/*
* total steps is for the keepign the history of the user movement
* */
#Column(name = "totalSteps")
#Min(value = 0L, message = "The value must be positive")
private int totalSteps;
/*
* current steps is for providing the user reward. We will need to set
* it to zero after processing the user payment
* */
#Column(name = "currentSteps")
#Min(value = 0L, message = "The value must be positive")
private int currentSteps;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<RewardList> rewardLists = new ArrayList<>();
public User() {
}
public User(#NotNull #NotEmpty String name, #NotNull #NotEmpty String countryName) {
this.name = name;
this.countryName = countryName;
}
public User(#NotNull #NotEmpty String name, #NotNull #NotEmpty String countryName, #Min(value = 0L, message = "The value must be positive") int totalSteps) {
this.name = name;
this.countryName = countryName;
this.totalSteps = totalSteps;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCountryName() {
return countryName;
}
public String getCurrencyName() {
return currencyName;
}
public void setCurrencyName(String currencyName) {
this.currencyName = currencyName;
}
public void setCountryName(String countryName) {
this.countryName = countryName;
}
public int getTotalSteps() {
return totalSteps;
}
public void setTotalSteps(int totalSteps) {
this.totalSteps = totalSteps;
}
public int getCurrentSteps() {
return currentSteps;
}
public void setCurrentSteps(int currentSteps) {
this.currentSteps = currentSteps;
}
public List<RewardList> getRewardLists() {
return rewardLists;
}
public void setRewardLists(RewardList rl) {
this.rewardLists.add(rl);
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
return getTotalSteps() == user.getTotalSteps() &&
getCurrentSteps() == user.getCurrentSteps() &&
getId().equals(user.getId()) &&
getName().equals(user.getName()) &&
getCountryName().equals(user.getCountryName()) &&
getRewardLists().equals(user.getRewardLists());
}
#Override
public int hashCode() {
return Objects.hash(getId(), getName(), getCountryName(), getTotalSteps(), getCurrentSteps(), getRewardLists());
}
#Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", countryName='" + countryName + '\'' +
", totalSteps=" + totalSteps +
", currentSteps=" + currentSteps +
'}';
}
}
#Entity
public class RewardList {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "reward")
#Min(value = 0L, message = "The value must be positive")
private double reward;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", nullable = false)
private User user;
public RewardList() {
}
public RewardList(User user) {
this.user = user;
}
public RewardList(#Min(value = 0L, message = "The value must be positive") double reward) {
this.reward = reward;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public double getReward() {
return reward;
}
public void setReward(double reward) {
this.reward = reward;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof RewardList)) return false;
RewardList list = (RewardList) o;
return Double.compare(list.getReward(), getReward()) == 0 &&
getId().equals(list.getId()) &&
getUser().equals(list.getUser());
}
#Override
public int hashCode() {
return Objects.hash(getId(), getReward(), getUser());
}
#Override
public String toString() {
return "RewardList{" +
"id=" + id +
", reward=" + reward +
", user=" + user +
'}';
}
}
The end-point where I have this issue provided below,
// $ curl -X PUT http://localhost:8080/api/v1/users/calculateReward?userId=1 | jq
#PutMapping("/calculateReward")
public ResponseEntity<Object> calculateReward(#RequestParam("userId") Long userId) {
Optional<User> optional = userService.findById(userId);
if (!optional.isPresent()) {
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}
User user = optional.get();
double reward = user.getCurrentSteps() * Parameters.REWARD_PER_STEPS_EUR;
RewardList list = new RewardList();
list.setUser(user);
list.setReward(reward);
rewardListService.save(list);
user.setCurrentSteps(0);
user.setRewardLists(list);
userService.save(user);
JSONObject json = new JSONObject();
double convertionRateToEuro = currencyMap.get(user.getCurrencyName());
double rewardConverted = reward * convertionRateToEuro;
json.put("name", user.getName());
json.put("currency", user.getCurrencyName());
json.put("reward", rewardConverted);
return ResponseEntity.status(HttpStatus.CREATED).body(json);
}
Does anyone know what is going on and can provide a solution?
Thank you.
I find the reason and below I provide a solution. We will need to save the models based on the hierarchy. HotSwap doesn't support adding methods or hierarchy changes as indicated by the error messages. It's the limitation of Java HotSwap, not IntelliJ IDEA. The proper way of code sequence will be,
User user = optional.get();
RewardList list = new RewardList();
list.setUser(user);
list.setReward(reward);
user.setCurrentSteps(0);
user.setRewardLists(list);
// first save the User
userService.save(user);
// Then, save the RewardList as it has one to many relations
rewardListService.save(list);
I created some model, for equipment, but I am not sure if I did correct mapping, also when I want to get rid of eager loading I got error:
"Type definition error: [simple type, class
org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor]; nested
exception is
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No
serializer found for class
org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no
properties discovered to create BeanSerializer (to avoid exception,
disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference
chain:
java.util.ArrayList[0]-com.winterrent.winterrent.entity.ItemProperty[\"item\"]-com.winterrent.winterrent.entity.Item$HibernateProxy$RO0mkQSh[\"hibernateLazyInitializer\"])",
but if I change the fetch type to eager everything works fine.
My reverse engineering schema:
Then my entities:
import javax.persistence.*;
import java.util.Objects;
#Entity
#Table(name = "item")
public class Item {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Enumerated(EnumType.STRING)
#Column(name = "type")
private ItemType itemType;
public Item() {
}
public Item(ItemType itemType) {
this.itemType = itemType;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public ItemType getItemType() {
return itemType;
}
public void setItemType(ItemType itemType) {
this.itemType = itemType;
}
#Override
public String toString() {
return "Item{" +
"id=" + id +
", itemType=" + itemType +
'}';
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Item item = (Item) o;
return id == item.id &&
Objects.equals(itemType, item.itemType);
}
#Override
public int hashCode() {
return Objects.hash(id, itemType);
}
}
2)
public enum ItemType {
SKI, BOARD
}
3)
import javax.persistence.*;
import java.util.Objects;
#Entity
#Table(name = "item_property_definition")
public class ItemPropertyDefinition {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "property_name")
private String propertyName;
#Column(name = "type")
#Enumerated(EnumType.STRING)
private ItemType itemType;
public ItemPropertyDefinition() {
}
public ItemPropertyDefinition(String propertyName, ItemType itemType) {
this.propertyName = propertyName;
this.itemType = itemType;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getPropertyName() {
return propertyName;
}
public void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
public ItemType getItemType() {
return itemType;
}
public void setItemType(ItemType itemType) {
this.itemType = itemType;
}
#Override
public String toString() {
return "ItemPropertyDefinition{" +
"id=" + id +
", propertyName='" + propertyName + '\'' +
", itemType=" + itemType +
'}';
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ItemPropertyDefinition that = (ItemPropertyDefinition) o;
return id == that.id &&
Objects.equals(propertyName, that.propertyName) &&
Objects.equals(itemType, that.itemType);
}
#Override
public int hashCode() {
return Objects.hash(id, propertyName, itemType);
}
}
And finally mapping:
import javax.persistence.*;
import java.util.Objects;
#Entity
#Table(name = "item_properties")
public class ItemProperty {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "item_id")
private Item item;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "item_property_definition_id")
private ItemPropertyDefinition itemPropertyDefinition;
#Column(name = "value")
private String value;
public ItemProperty(){}
public ItemProperty(Item item, ItemPropertyDefinition itemPropertyDefinition, String value) {
this.item = item;
this.itemPropertyDefinition = itemPropertyDefinition;
this.value = value;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Item getItem() {
return item;
}
public void setItem(Item item) {
this.item = item;
}
public ItemPropertyDefinition getItemPropertyDefinition() {
return itemPropertyDefinition;
}
public void setItemPropertyDefinition(ItemPropertyDefinition itemPropertyDefinition) {
this.itemPropertyDefinition = itemPropertyDefinition;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
#Override
public String toString() {
return "ItemProperty{" +
"id=" + id +
", item=" + item +
", itemPropertyDefinition=" + itemPropertyDefinition +
", value='" + value + '\'' +
'}';
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ItemProperty that = (ItemProperty) o;
return id == that.id &&
Objects.equals(item, that.item) &&
Objects.equals(itemPropertyDefinition, that.itemPropertyDefinition) &&
Objects.equals(value, that.value);
}
#Override
public int hashCode() {
return Objects.hash(id, item, itemPropertyDefinition, value);
}
}
Thank you for the hints. It's my first playing around with backend.
The problem is that you are overriding toString(), equals() and hashCode() methods using entities. Everything, that is used inside those functions need to be basic entities, that are loaded together with parent entity. That is why there was no exception was thrown, when loaded eagerly.
In general I wouldn't recommend using child entities to determine equality and so on as, for example, this requires them to be loaded eagerly, which is not good for performance. I would leave them lazily loaded and rewrite overridden methods for performance sake, but if you need them used in those methods, you need to load them eagerly.
Vlad Mihalcea wrote a good read about implementing toString(), equals and hashCode().
Hello I am trying to pull a record from MySql db using spring jpa.
Entity 1
#Entity #Table(name = "sku_warehouse") #Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) #Document(indexName = "skuwarehouse") public class SkuWarehouse extends AbstractAuditingEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "avail_qty")
private Integer availQty;
#Column(name = "effective_date")
private ZonedDateTime effectiveDate;
#Column(name = "expiration_date")
private ZonedDateTime expirationDate;
#ManyToOne
private SkuMaster skuMaster;
#ManyToOne
private Country country;
#ManyToOne
private Warehouse warehouse;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getAvailQty() {
return availQty;
}
public SkuWarehouse availQty(Integer availQty) {
this.availQty = availQty;
return this;
}
public void setAvailQty(Integer availQty) {
this.availQty = availQty;
}
public ZonedDateTime getEffectiveDate() {
return effectiveDate;
}
public SkuWarehouse effectiveDate(ZonedDateTime effectiveDate) {
this.effectiveDate = effectiveDate;
return this;
}
public void setEffectiveDate(ZonedDateTime effectiveDate) {
this.effectiveDate = effectiveDate;
}
public ZonedDateTime getExpirationDate() {
return expirationDate;
}
public SkuWarehouse expirationDate(ZonedDateTime expirationDate) {
this.expirationDate = expirationDate;
return this;
}
public void setExpirationDate(ZonedDateTime expirationDate) {
this.expirationDate = expirationDate;
}
public SkuMaster getSkuMaster() {
return skuMaster;
}
public SkuWarehouse skuMaster(SkuMaster skuMaster) {
this.skuMaster = skuMaster;
return this;
}
public void setSkuMaster(SkuMaster skuMaster) {
this.skuMaster = skuMaster;
}
public Country getCountry() {
return country;
}
public SkuWarehouse country(Country country) {
this.country = country;
return this;
}
public void setCountry(Country country) {
this.country = country;
}
public Warehouse getWarehouse() {
return warehouse;
}
public SkuWarehouse warehouse(Warehouse warehouse) {
this.warehouse = warehouse;
return this;
}
public void setWarehouse(Warehouse warehouse) {
this.warehouse = warehouse;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SkuWarehouse skuWarehouse = (SkuWarehouse) o;
if (skuWarehouse.id == null || id == null) {
return false;
}
return Objects.equals(id, skuWarehouse.id);
}
#Override
public int hashCode() {
return Objects.hashCode(id);
}
#Override
public String toString() {
return "SkuWarehouse{" +
"id=" + id +
", availQty='" + availQty + "'" +
", effectiveDate='" + effectiveDate + "'" +
", expirationDate='" + expirationDate + "'" +
'}';
} }
Entity 2 :
#Entity
#Table(name = "sku_master")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Document(indexName = "skumaster")
public class SkuMaster extends AbstractAuditingEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "ordering_item")
private String orderingItem;
#NotNull
#Column(name = "sku_number", nullable = false)
private String skuNumber;
#Column(name = "sku_description")
private String skuDescription;
#Column(name = "brand_name")
private String brandName;
#Column(name = "item_status")
private String itemStatus;
#Column(name = "item_weight")
private Float itemWeight;
#Column(name = "item_tax_cd")
private String itemTaxCd;
#Enumerated(EnumType.STRING)
#Column(name = "vendor_ship_cd")
private Flag vendorShipCd;
#Column(name = "ltu_quantity")
private String ltuQuantity;
#ManyToMany
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#JoinTable(name = "sku_master_country",
joinColumns = #JoinColumn(name="sku_masters_id", referencedColumnName="id"),
inverseJoinColumns = #JoinColumn(name="countries_id", referencedColumnName="id"))
private Set<Country> countries = new HashSet<>();
#ManyToMany
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#JoinTable(name = "sku_master_warehouse",
joinColumns = #JoinColumn(name="sku_masters_id", referencedColumnName="id"),
inverseJoinColumns = #JoinColumn(name="warehouses_id", referencedColumnName="id"))
private Set<Warehouse> warehouses = new HashSet<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getOrderingItem() {
return orderingItem;
}
public SkuMaster orderingItem(String orderingItem) {
this.orderingItem = orderingItem;
return this;
}
public void setOrderingItem(String orderingItem) {
this.orderingItem = orderingItem;
}
public String getSkuNumber() {
return skuNumber;
}
public SkuMaster skuNumber(String skuNumber) {
this.skuNumber = skuNumber;
return this;
}
public void setSkuNumber(String skuNumber) {
this.skuNumber = skuNumber;
}
public String getSkuDescription() {
return skuDescription;
}
public SkuMaster skuDescription(String skuDescription) {
this.skuDescription = skuDescription;
return this;
}
public void setSkuDescription(String skuDescription) {
this.skuDescription = skuDescription;
}
public String getBrandName() {
return brandName;
}
public SkuMaster brandName(String brandName) {
this.brandName = brandName;
return this;
}
public void setBrandName(String brandName) {
this.brandName = brandName;
}
public String getItemStatus() {
return itemStatus;
}
public SkuMaster itemStatus(String itemStatus) {
this.itemStatus = itemStatus;
return this;
}
public void setItemStatus(String itemStatus) {
this.itemStatus = itemStatus;
}
public Float getItemWeight() {
return itemWeight;
}
public SkuMaster itemWeight(Float itemWeight) {
this.itemWeight = itemWeight;
return this;
}
public void setItemWeight(Float itemWeight) {
this.itemWeight = itemWeight;
}
public String getItemTaxCd() {
return itemTaxCd;
}
public SkuMaster itemTaxCd(String itemTaxCd) {
this.itemTaxCd = itemTaxCd;
return this;
}
public void setItemTaxCd(String itemTaxCd) {
this.itemTaxCd = itemTaxCd;
}
public Flag getVendorShipCd() {
return vendorShipCd;
}
public SkuMaster vendorShipCd(Flag vendorShipCd) {
this.vendorShipCd = vendorShipCd;
return this;
}
public void setVendorShipCd(Flag vendorShipCd) {
this.vendorShipCd = vendorShipCd;
}
public String getLtuQuantity() {
return ltuQuantity;
}
public SkuMaster ltuQuantity(String ltuQuantity) {
this.ltuQuantity = ltuQuantity;
return this;
}
public void setLtuQuantity(String ltuQuantity) {
this.ltuQuantity = ltuQuantity;
}
public Set<Country> getCountries() {
return countries;
}
public SkuMaster countries(Set<Country> countries) {
this.countries = countries;
return this;
}
public SkuMaster addCountry(Country country) {
this.countries.add(country);
return this;
}
public SkuMaster removeCountry(Country country) {
this.countries.remove(country);
return this;
}
public void setCountries(Set<Country> countries) {
this.countries = countries;
}
public Set<Warehouse> getWarehouses() {
return warehouses;
}
public SkuMaster warehouses(Set<Warehouse> warehouses) {
this.warehouses = warehouses;
return this;
}
public SkuMaster addWarehouse(Warehouse warehouse) {
this.warehouses.add(warehouse);
return this;
}
public SkuMaster removeWarehouse(Warehouse warehouse) {
this.warehouses.remove(warehouse);
return this;
}
public void setWarehouses(Set<Warehouse> warehouses) {
this.warehouses = warehouses;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SkuMaster skuMaster = (SkuMaster) o;
if (skuMaster.id == null || id == null) {
return false;
}
return Objects.equals(id, skuMaster.id);
}
#Override
public int hashCode() {
return Objects.hashCode(id);
}
#Override
public String toString() {
return "SkuMaster{" +
"id=" + id +
", orderingItem='" + orderingItem + "'" +
", skuNumber='" + skuNumber + "'" +
", skuDescription='" + skuDescription + "'" +
", brandName='" + brandName + "'" +
", itemStatus='" + itemStatus + "'" +
", itemWeight='" + itemWeight + "'" +
", itemTaxCd='" + itemTaxCd + "'" +
", vendorShipCd='" + vendorShipCd + "'" +
", ltuQuantity='" + ltuQuantity + "'" +
'}';
}
}
Method that pull the record
#Query("select sw from SkuWarehouse sw left join fetch Warehouse w where sw.warehouse = w.warehouseId AND w.warehouseId=:warehouseId AND sw.skuMaster=:skuMasterId")
SkuWarehouse findOneWithEagerRelationships(#Param("warehouseId") String warehouseId, #Param("skuMasterId") Long skuMasterId);
Actually SQL query that works
select * from sku_warehouse sw
join warehouse w on (sw.warehouse_id = w.id)
where w.warehouse_id='024'
and sw.sku_master_id='1072'
Error Log
Caused by: org.hibernate.property.access.spi.PropertyAccessException: Error accessing field [private java.lang.Long com.company.wmapis.domain.SkuMaster.id] by reflection for persistent property [com.company.wmapis.domain.SkuMaster#id] : 1454
at org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:71)
at org.hibernate.tuple.entity.AbstractEntityTuplizer.getIdentifier(AbstractEntityTuplizer.java:224)
at org.hibernate.persister.entity.AbstractEntityPersister.getIdentifier(AbstractEntityPersister.java:4647)
at org.hibernate.persister.entity.AbstractEntityPersister.isTransient(AbstractEntityPersister.java:4358)
at org.hibernate.engine.internal.ForeignKeys.isTransient(ForeignKeys.java:226)
at org.hibernate.engine.internal.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:276)
at org.hibernate.type.EntityType.getIdentifier(EntityType.java:462)
at org.hibernate.type.ManyToOneType.nullSafeSet(ManyToOneType.java:161)
at org.hibernate.param.NamedParameterSpecification.bind(NamedParameterSpecification.java:53)
at org.hibernate.loader.hql.QueryLoader.bindParameterValues(QueryLoader.java:628)
at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1956)
at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1909)
at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1887)
at org.hibernate.loader.Loader.doQuery(Loader.java:932)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:349)
at org.hibernate.loader.Loader.doList(Loader.java:2615)
at org.hibernate.loader.Loader.doList(Loader.java:2598)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2430)
at org.hibernate.loader.Loader.list(Loader.java:2425)
at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:502)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:371)
at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:216)
at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1459)
at org.hibernate.query.internal.AbstractProducedQuery.doList(AbstractProducedQuery.java:1426)
at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1398)
at org.hibernate.query.internal.AbstractProducedQuery.getSingleResult(AbstractProducedQuery.java:1444)
at org.springframework.data.jpa.repository.query.JpaQueryExecution$SingleEntityExecution.doExecute(JpaQueryExecution.java:210)
at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:82)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:116)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:106)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:482)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:460)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
... 167 common frames omitted
Caused by: java.lang.IllegalArgumentException: Can not set java.lang.Long field com.company.wmapis.domain.SkuMaster.id to java.lang.Long
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:58)
at sun.reflect.UnsafeObjectFieldAccessorImpl.get(UnsafeObjectFieldAccessorImpl.java:36)
at java.lang.reflect.Field.get(Field.java:393)
at org.hibernate.property.access.spi.GetterFieldImpl.get(GetterFieldImpl.java:67)
... 206 common frames omitted
What is wrong in the Jpa query? I even tried passing the SkuMaster Object instead of Long but then I get MySql query exception.
It works after I changed my Jpa query and method as below
#Query(value="SELECT * FROM sku_warehouse sw JOIN warehouse w ON (sw.warehouse_id = w.Id) WHERE w.warehouse_id=?1 AND sw.sku_master_id=?2", nativeQuery = true)
SkuWarehouse findOneWithEagerRelationships(String warehouseId, Long skuMasterId);
I have the following query in my repository:
SELECT p FROM Project p JOIN p.users u WHERE u.login =:login
There is a Many To Many relationshio between user and project.
Everything works fine and it returns the user's projects, but I want it for each project to return the corresponding set of users. So updated it with a fetch join:
SELECT p FROM Project p JOIN FETCH p.users JOIN p.users u WHERE u.login =:login
But now i got the following exception:
nested exception is java.lang.IllegalArgumentException: Count query validation failed for method public abstract org.springframework.data.domain.Page com.example.app.repository.ProjectRepository.findAllByUserLogin(java.lang.String,org.springframework.data.domain.Pageable)! org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list
Cannot find a workaround for it to execute the where clause and fetch the collection at the same time.
Project Entity:
#Entity
#Table(name = "project")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Document(indexName = "project")
public class Project implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
#Size(min = 10, max = 50)
#Column(name = "name", length = 50, nullable = false)
private String name;
#Size(max = 150)
#Column(name = "description", length = 150)
private String description;
#Column(name = "project_type")
private Integer projectType;
#Column(name = "is_active")
private Boolean isActive;
#Column(name = "date_created")
private ZonedDateTime dateCreated;
#Column(name = "date_updated")
private ZonedDateTime dateUpdated;
#ManyToMany
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#JoinTable(name = "project_user",
joinColumns = #JoinColumn(name="projects_id", referencedColumnName="ID"),
inverseJoinColumns = #JoinColumn(name="users_id", referencedColumnName="ID"))
private Set<User> users = new HashSet<>();
#OneToMany(mappedBy = "project")
#JsonIgnore
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<Milestone> milestones = new HashSet<>();
#OneToMany(mappedBy = "project")
#JsonIgnore
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<TaskList> taskLists = new HashSet<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Integer getProjectType() {
return projectType;
}
public void setProjectType(Integer projectType) {
this.projectType = projectType;
}
public Boolean isIsActive() {
return isActive;
}
public void setIsActive(Boolean isActive) {
this.isActive = isActive;
}
public ZonedDateTime getDateCreated() {
return dateCreated;
}
public void setDateCreated(ZonedDateTime dateCreated) {
this.dateCreated = dateCreated;
}
public ZonedDateTime getDateUpdated() {
return dateUpdated;
}
public void setDateUpdated(ZonedDateTime dateUpdated) {
this.dateUpdated = dateUpdated;
}
public Set<User> getOwners() {
return users;
}
public void setOwners(Set<User> users) {
this.users = users;
}
public Set<Milestone> getMilestones() {
return milestones;
}
public void setMilestones(Set<Milestone> milestones) {
this.milestones = milestones;
}
public Set<TaskList> getTaskLists() {
return taskLists;
}
public void setTaskLists(Set<TaskList> taskLists) {
this.taskLists = taskLists;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Project project = (Project) o;
if(project.id == null || id == null) {
return false;
}
return Objects.equals(id, project.id);
}
#Override
public int hashCode() {
return Objects.hashCode(id);
}
#Override
public String toString() {
return "Project{" +
"id=" + id +
", name='" + name + "'" +
", description='" + description + "'" +
", projectType='" + projectType + "'" +
", isActive='" + isActive + "'" +
", dateCreated='" + dateCreated + "'" +
", dateUpdated='" + dateUpdated + "'" +
'}';
}
}
User Entity:
#Entity
#Table(name = "user")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Document(indexName = "user")
public class User extends AbstractAuditingEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
#Pattern(regexp = Constants.LOGIN_REGEX)
#Size(min = 1, max = 100)
#Column(length = 100, unique = true, nullable = false)
private String login;
#JsonIgnore
#NotNull
#Size(min = 60, max = 60)
#Column(name = "password_hash",length = 60)
private String password;
#Size(max = 50)
#Column(name = "first_name", length = 50)
private String firstName;
#Size(max = 50)
#Column(name = "last_name", length = 50)
private String lastName;
#Email
#Size(max = 100)
#Column(length = 100, unique = true)
private String email;
#NotNull
#Column(nullable = false)
private boolean activated = false;
#Size(min = 2, max = 5)
#Column(name = "lang_key", length = 5)
private String langKey;
#Size(max = 20)
#Column(name = "activation_key", length = 20)
#JsonIgnore
private String activationKey;
#Size(max = 20)
#Column(name = "reset_key", length = 20)
private String resetKey;
#Column(name = "reset_date", nullable = true)
private ZonedDateTime resetDate = null;
#Column(name = "avatar", nullable = true)
private String avatar;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login.toLowerCase(Locale.ENGLISH);
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public boolean getActivated() {
return activated;
}
public void setActivated(boolean activated) {
this.activated = activated;
}
public String getActivationKey() {
return activationKey;
}
public void setActivationKey(String activationKey) {
this.activationKey = activationKey;
}
public String getResetKey() {
return resetKey;
}
public void setResetKey(String resetKey) {
this.resetKey = resetKey;
}
public ZonedDateTime getResetDate() {
return resetDate;
}
public void setResetDate(ZonedDateTime resetDate) {
this.resetDate = resetDate;
}
public String getLangKey() {
return langKey;
}
public void setLangKey(String langKey) {
this.langKey = langKey;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
User user = (User) o;
if (!login.equals(user.login)) {
return false;
}
return true;
}
#Override
public int hashCode() {
return login.hashCode();
}
#Override
public String toString() {
return "User{" +
"login='" + login + '\'' +
", avatar='" + avatar + '\'' +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", activated='" + activated + '\'' +
", langKey='" + langKey + '\'' +
", activationKey='" + activationKey + '\'' +
"}";
}
}
Try to remove second join:
SELECT p FROM Project p JOIN FECTH p.users u WHERE u.login =:login
And if you want to get Projects which contains specified user by login then you can try this:
SELECT p FROM Project p JOIN FECTH p.users u WHERE :login in elements(u.login)