I use DTO and modelMapper in order not to make visible some fields.
I have a CategoryEntity that can have subcategories
public class CategoryEntity {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
#Column(length = 30, nullable = false)
private String categoryKeyId;
#Column(nullable = false)
private String name;
#ManyToOne(optional = true, fetch = FetchType.LAZY)
#JoinColumn(name="parent_id", nullable=true)
private CategoryEntity parentCategory;
// allow to delete also subcategories
#OneToMany(mappedBy="parentCategory", cascade = CascadeType.ALL)
private List<CategoryEntity> subCategories;
}
When i create a category I use a model:
#Getter #Setter
public class CategoryRequestModel {
private String name;
private String parentCategoryKeyId;
}
In this model i want parentCategoryKeyId to match with the categoryKeyId of the parent.
For example if i create a "top" category :
{
"name": "topCategory"
}
It returns me :
{
"categoryKeyId": "jUcpO27Ch2YrT2zkLr488Q435F8AKS",
"name": "topCategory",
"subCategories": null
}
When i do this :
{
"name": "sub",
"parentCategoryKeyId": "jUcpO27Ch2YrT2zkLr488Q435F8AKS"
}
In my Controller, i pass the rest object to a DTO Layer which calls a service :
public CategoryRestResponseModel createCategory(#RequestBody CategoryRequestModel categoryRequestModel) {
CategoryRestResponseModel returnValue = new CategoryRestResponseModel();
if( categoryRequestModel.getName().isEmpty())
throw new NullPointerException(ErrorMessages.MISSING_REQUIRED_FIELDS.getErrorMessage());
ModelMapper modelMapper = new ModelMapper();
CategoryDto categoryDto = modelMapper.map(categoryRequestModel, CategoryDto.class);
CategoryDto createdCategory = categoryService.createCategory(categoryDto);
returnValue = modelMapper.map(createdCategory, CategoryRestResponseModel.class);
return returnValue;
}
My CategoryDto is a basic POJO :
#Getter #Setter
public class CategoryDto implements Serializable {
#Getter(AccessLevel.NONE)
private static final long serialVersionUID = 1L;
private String categoryKeyId;
private String parentCategoryKeyId;
private String name;
private CategoryDto parentCategory;
private List<CategoryDto> subCategories;
}
In my Service :
public CategoryDto createCategory(CategoryDto categoryDto) {
//1. Create an empty object to return
System.out.println("Hello World");
CategoryDto returnValue = new CategoryDto();
System.out.println("CategoryDto: " + categoryDto);
// check if category exists
if (categoryRepository.findByName(categoryDto.getName()) != null)
throw new ApplicationServiceException("Record already in Database");
ModelMapper modelMapper = new ModelMapper();
CategoryEntity categoryEntity = modelMapper.map(categoryDto, CategoryEntity.class);
// Generate categoryKeyId
String categoryKeyId = utils.generateCategoryKeyId(30);
categoryEntity.setCategoryKeyId(categoryKeyId);
System.out.println("categoryDto parentCategory: " + categoryDto.getParentCategory());
System.out.println("CategoryDto: " + categoryDto);
if(categoryDto.getParentCategoryKeyId() != null) {
CategoryEntity parentCategory = categoryRepository.findByCategoryKeyId(categoryDto.getParentCategoryKeyId());
categoryEntity.setParentCategory(parentCategory);
System.out.println("CategoryEntity: " + categoryEntity);
System.out.println("parentCategory: " + parentCategory);
}
CategoryEntity storedCategory = categoryRepository.save(categoryEntity);
returnValue = modelMapper.map(storedCategory, CategoryDto.class);
return returnValue;
}
My issue is that I would like to save the subcategory and retrieve the ID that match the categoryKeyId ...
In the database my entry should be like this
My First entry should have:
id = 1 - parent_id = null, category_key_id = jUcpO27Ch2YrT2zkLr488Q435F8AKS, name = topCategory ...
AND :
id = 2 - parent_id = 1 , category_key_id = "another generated key", name= sub
Unfortunatelly I just persist the id, the categorykeyid and the name.
I removed id from CategoryDto and i obtain : 1) Converter org.modelmapper.internal.converter.NumberConverter#348fc3d8 failed to convert java.lang.String to java.lang.Long.
I solved it in a "dirty" way.
I just changed my object in entry and added a long id.
It gives me :
#Getter #Setter
public class CategoryRequestModel {
private Long id;
private String name;
private String parentCategoryKeyId;
}
Related
public class ChallengeDto {
private Long id;
private Category category;
private String title;
private String subTitle;
private boolean like;
private int totalScore;
private int requiredScore;
public ChallengeDto(Long id, Category category, String title, String subTitle, boolean like, int totalScore, int requiredScore) {
this.id = id;
this.category = category;
this.title = title;
this.subTitle = subTitle;
this.like = like;
this.totalScore = totalScore;
this.requiredScore = requiredScore;
}
}
I created challengeDto that include challenge's properties(id, category, title, subtitle, totalScore, requiredScore) and like property(can know that if i like challenge or not).
If I put like button, that information stored challengeLike table.
public class ChallengeLike {
#Id
#GeneratedValue
#Column(name = "challenge_like_id")
private Long id;
#ManyToOne(fetch = LAZY)
#JoinColumn(name = "user_id")
private User user;
#ManyToOne(fetch = LAZY)
#JoinColumn(name = "challenge_id")
private Challenge challenge;
private LocalDateTime createDate;
}
Now I'm trying to write a code to retrieve challengeDto that checks if I clicked like or not, but I'm having a problem... I can't think of what kind of code to make.
#Repository
#RequiredArgsConstructor
public class ChallengeDtoRepository {
private final EntityManager em;
#Transactional
public List<ChallengeDto> findChallenges(Long userId) {
return em.createQuery(
"select new " +
"com.example.candy.controller.challenge.ChallengeDto(c.id,c.category,c.title,c.subTitle,????,c.totalScore,c.requiredScore)" +
" from Challenge c" +
" left join ChallengeLike cl on c.id = cl.challenge.id" +
" and cl.user.id = : userId", ChallengeDto.class)
.setParameter("userId", userId)
.getResultList();
}
}
try to rename the field to likeDone or something different than like, it makes the code ambiguous.
However, just simply do:
cl.likeDone
which means:
return em.createQuery(
"select new " +
"com.example.random.demo.dto.ChallengeDto(c.id,c.category,c.title,c.subTitle,cl.likeDone,c.totalScore,c.requiredScore)" +
" from Challenge c" +
" left join ChallengeLike cl on c.id = cl.challenge.id" +
" where cl.user.id = : userId", ChallengeDto.class)
.setParameter("userId", userId)
.getResultList();
However, try to use JPA if you don't have any mandatory condition to use native query or jpql.
JPA implementation:
#Repository
public interface ChallengeLikeRepository extends JpaRepository<ChallengeLike, Long> {
List<ChallengeLike> findAllByUser_Id(long userId);
}
Just call the repository method from service layer and map to your required dto:
public List<ChallengeDto> findChallenges(Long userId) {
List<ChallengeLike> entities = this.repository.findAllByUser_Id(userId);
return entities.stream().map(this::mapToDto).collect(Collectors.toList());
}
The mapToDto() method converts the entity to corresponding ChallengeDto
private ChallengeDto mapToDto(ChallengeLike x) {
return ChallengeDto.builder()
.category(x.getChallenge().getCategory())
.id(x.getChallenge().getId())
.like(x.isLikeDone())
.requiredScore(x.getChallenge().getRequiredScore())
.subTitle(x.getChallenge().getSubTitle())
.title(x.getChallenge().getTitle())
.totalScore(x.getChallenge().getTotalScore())
.userId(x.getUser().getId())
.build();
}
For your convenience, some properties has been added or changed in some classes. The #Builder annotation has been added to the ChallengeDto class. The rest of the corresponding entity and other classes:
a) ChallengeLike.java
#Entity
#Data
public class ChallengeLike {
#Id
#GeneratedValue
#Column(name = "challenge_like_id")
private Long id;
#ManyToOne
#JoinColumn(name = "user_id")
#JsonIgnoreProperties("challengeLikes")
private User user;
#ManyToOne
#JoinColumn(name = "challenge_id")
#JsonIgnoreProperties("challengeLikes")
private Challenge challenge;
private boolean likeDone;
private LocalDateTime createDate;
}
b) Challenge.java
#Entity
#Data
public class Challenge {
#Id
private Long id;
private Category category;
private String title;
private String subTitle;
private int totalScore;
private int requiredScore;
#OneToMany(mappedBy = "challenge", cascade = CascadeType.ALL)
#JsonIgnoreProperties("challenge")
private List<ChallengeLike> challengeLikes = new ArrayList<>();
}
c) Category.java
public enum Category {
CAT_A,
CAT_B
}
Update
If you want to fetch Challenge entity instead of ChallengeLike and map that to ChallengeDto, first implement ChallangeRepository:
#Repository
public interface ChallengeRepository extends JpaRepository<Challenge, Long> {
}
Add the fetchType to EAGER in Challange Entity class:
#OneToMany(mappedBy = "challenge", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JsonIgnoreProperties("challenge")
private List<ChallengeLike> challengeLikes = new ArrayList<>();
And to map the Challenge to ChallengeDto, you can add another mothod as follows:
private ChallengeDto mapToDto(Challenge x) {
return ChallengeDto.builder()
.category(x.getCategory())
.id(x.getId())
.like(!x.getChallengeLikes().isEmpty() && x.getChallengeLikes().get(0).isLikeDone())
.requiredScore(x.getRequiredScore())
.subTitle(x.getSubTitle())
.title(x.getTitle())
.totalScore(x.getTotalScore())
.userId(x.getUserId()) // if you have user reference in Challenge, remove this otherwise.
.build();
}
finally, to incorporate everything properly, change the caller:
public List<ChallengeDto> findChallenges(Long userId) {
List<Challenge> entities = this.repository.findAll();
List<ChallengeDto> entitiesWithoutChallengeLikes = entities.stream()
.filter(x -> x.getChallengeLikes() == null
|| x.getChallengeLikes().isEmpty())
.map(this::mapToDto).collect(Collectors.toList());
List<ChallengeDto> entitiesInferredFromChallengeLikes = entities.stream()
.filter(x -> x.getChallengeLikes() != null && !x.getChallengeLikes().isEmpty())
.flatMap(x -> x.getChallengeLikes().stream())
.map(this::mapToDto)
.collect(Collectors.toList());
entitiesInferredFromChallengeLikes.addAll(entitiesWithoutChallengeLikes);
return entitiesInferredFromChallengeLikes;
}
Final Update
Well, I finally understood properly what you expected. Adopt the following changes to the previous solution and you will get exactly what you want.
Change the 2 occurrence of the following in the findChallanges method:
.map(this::mapToDto)
To:
.map(x -> mapToDto(x, userId))
And the two mapToDto functions will be changed to follows:
private ChallengeDto mapToDto(ChallengeLike x, long userId) {
return ChallengeDto.builder()
.category(x.getChallenge().getCategory())
.id(x.getChallenge().getId())
.like(x.getUser().getId() == userId && x.isLikeDone())
.requiredScore(x.getChallenge().getRequiredScore())
.subTitle(x.getChallenge().getSubTitle())
.title(x.getChallenge().getTitle())
.totalScore(x.getChallenge().getTotalScore())
.userId(x.getUser().getId())
.build();
}
private ChallengeDto mapToDto(Challenge x, long userId) {
return ChallengeDto.builder()
.category(x.getCategory())
.id(x.getId())
.like(false)
.requiredScore(x.getRequiredScore())
.subTitle(x.getSubTitle())
.title(x.getTitle())
.totalScore(x.getTotalScore())
.userId(userId)
.build();
}
I am studying spring boot data using this API SWAPI, I did almost things but now I dont know how to map the relationship about two lists, above you can see my code and entities.
Entity Film
#Data
#Entity
public class Film extends Persistent<Long> {
private String title;
#JsonProperty(value = "episode_id")
private int episodeId;
#JsonProperty(value = "opening_crawl")
#Column(columnDefinition = "CLOB")
private String openingCrawl;
private String director;
private String producer;
#JsonDeserialize(converter = StringToLocalDateConverter.class)
#JsonProperty(value = "release_date")
private LocalDate releaseDate;
#JsonDeserialize(converter = ApiURLToEntitiesConverter.class)
#ManyToMany(mappedBy = "films")
private List<Person> characters;
#JsonDeserialize(converter = StringToLocalDateTimeConverter.class)
private LocalDateTime created;
#JsonDeserialize(converter = StringToLocalDateTimeConverter.class)
private LocalDateTime edited;
private String url;
}
Entity Person
#Data
#Entity
public class Person extends Persistent<Long> {
private String name;
private String height;
private String mass;
#JsonProperty(value = "hair_color")
private String hairColor;
#JsonProperty(value = "skin_color")
private String skinColor;
#JsonProperty(value = "eye_color")
private String eyeColor;
#JsonProperty(value = "birth_year")
private String birthYear;
private String gender;
#JsonDeserialize(converter = ApiURLToEntityConverter.class)
#JoinColumn(name = "planet_id", foreignKey = #javax.persistence.ForeignKey(name = "none"))
#OneToOne(optional = true)
private Planet homeworld;
#JsonDeserialize(converter = ApiURLToEntitiesConverter.class)
#ManyToMany
#JoinTable(
name = "film_person",
joinColumns = #JoinColumn(name = "film_fk", referencedColumnName = "id", nullable = true),
inverseJoinColumns = #JoinColumn(name = "person_fk", referencedColumnName = "id", nullable = true))
private List<Film> films;
#JsonDeserialize(converter = StringToLocalDateTimeConverter.class)
private LocalDateTime created;
#JsonDeserialize(converter = StringToLocalDateTimeConverter.class)
private LocalDateTime edited;
private String url;
}
I am trying to use the spring jpa method to saveAll
#Override
public List<T> insertAll(List<T> entities) {
for (Persistent entity : entities) {
Set<ConstraintViolation<Persistent>> violations = validator.validate(entity);
if (violations != null && !violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
}
return repository.saveAll(entities);
}
Converter Method
#Override
public List convert(List<String> s) {
if (s == null || s.isEmpty()) {
return null;
}
List objetos = new LinkedList();
for (String url : s) {
if (url.contains("people")) {
objetos.add(Util.getPerson(url));
}
if (url.contains("planets")) {
objetos.add(Util.getPlanet(url));
}
if (url.contains("starships")) {
objetos.add(Util.getStarship(url));
}
if (url.contains("vehicles")) {
objetos.add(Util.getVehicle(url));
}
if (url.contains("species")) {
objetos.add(Util.getSpecie(url));
}
}
return objetos;
}
}
Util method
public static Person getPerson(String characterApiUrl) {
if (characterApiUrl == null || characterApiUrl.isEmpty()) {
return null;
}
Person person = new Person();
person.setId(StringUtil.getIdEntity(characterApiUrl, "people/"));
return person;
}
The relationship table is being created but no populated
I created 2 entities :
#Entity
#Table(name="products")
public class ProductEntity {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private String productKeyId;
// many to one relationship with category
#ManyToOne
#JoinColumn(name = "category_id")
private CategoryEntity category;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private double price;
#Column(nullable = false)
private int qty;
private String imgPath;
// getters & setters
}
And :
#Entity
#Table(name="categories")
public class CategoryEntity {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
#Column(length = 30, nullable = false)
private String categoryKeyId;
#Column(nullable = false)
private String name;
#ManyToOne(optional = true, fetch = FetchType.LAZY)
#JoinColumn(name="parent_id", nullable=true)
private CategoryEntity parentCategory;
// allow to delete also subcategories
#OneToMany(mappedBy="parentCategory", cascade = CascadeType.ALL)
private List<CategoryEntity> subCategories;
//Here mappedBy indicates that the owner is in the other side
#OneToMany(fetch = FetchType.EAGER, mappedBy = "category", cascade = CascadeType.REMOVE)
private List<ProductEntity> products;
}
I have datas in the database generated :
Here is my Product table
And the category table
My issue is the following. I use a REST API to update the product and the category (if needed).
{
"name": "Pizza12",
"price": 25.0,
"qty": 15,
"imgPath": "anotherpathImage",
"category": {
"categoryKeyId": "VMz7EM6tNfoOAQtO1SHPYcH14jj0Cy",
"name": "Fish"
}
}
In my service I try to update both part separatelly :
#Override
public ProductDto updateProduct(String productKeyId, ProductDto productDto) {
// create a return object of type Product
ProductDto returnValue = new ProductDto();
// create Entity objects to request on the database
ProductEntity productEntity = productRepository.findByProductKeyId(productKeyId);
CategoryEntity categoryEntity = categoryRepository.findCategoryEntityByProductKeyId(productKeyId);
ModelMapper modelMapper = new ModelMapper();
if (productEntity == null)
throw new ApplicationServiceException(ErrorMessages.NO_RECORD_FOUND.getErrorMessage());
productEntity.setProductKeyId(productKeyId);
productEntity.setName(productDto.getName());
productEntity.setPrice(productDto.getPrice());
productEntity.setQty(productDto.getQty());
productEntity.setImgPath(productDto.getImgPath());
// update the category
CategoryEntity updatedCategory = categoryRepository.save(categoryEntity);
productEntity.setCategory(updatedCategory);
// productEntity.setCategory(categoryEntity);
System.out.println("product entity : " + productEntity.toString());
ProductEntity updatedProduct = productRepository.save(productEntity);
updatedProduct.setCategory(updatedCategory);
returnValue = modelMapper.map(updatedProduct, ProductDto.class);
return returnValue;
}
Unfortunatelly, it doesn't seem to work as expected. The product is updated, the category remains the same.
I finally solved my Issue thanks to Janar and Repoker.
#Override
public ProductDto updateProduct(String productKeyId, ProductDto productDto) {
// create a return object of type Product
ProductDto returnValue = new ProductDto();
// create Entity objects to request on the database
ProductEntity productEntity = productRepository.findByProductKeyId(productKeyId);
CategoryEntity categoryEntity = categoryRepository.findByCategoryKeyId(productDto.getCategory().getCategoryKeyId());
//CategoryEntity categoryEntity = categoryRepository.findCategoryEntityByProductKeyId(productKeyId);
ModelMapper modelMapper = new ModelMapper();
if (productEntity == null)
throw new ApplicationServiceException(ErrorMessages.NO_RECORD_FOUND.getErrorMessage());
productEntity.setProductKeyId(productKeyId);
productEntity.setName(productDto.getName());
productEntity.setPrice(productDto.getPrice());
productEntity.setQty(productDto.getQty());
productEntity.setImgPath(productDto.getImgPath());
// update the category
CategoryEntity updatedCategory = categoryRepository.save(categoryEntity);
productEntity.setCategory(productEntity.getCategory());
// productEntity.setCategory(categoryEntity);
System.out.println("product entity : " + productEntity.toString());
ProductEntity updatedProduct = productRepository.save(productEntity);
updatedProduct.setCategory(updatedCategory);
returnValue = modelMapper.map(updatedProduct, ProductDto.class);
return returnValue;
}
I was not persisting the new values entered but the values that were initially set...
Hi guys I am new to Spring and I am getting this error in my project:
org.hibernate.PropertyAccessException: Could not set field value [1] value by
reflection : [class com.**.domain.identities.NurseAgencyIdentity.agencyId]
setter of com.**.domain.identities.NurseAgencyIdentity.agencyId
There are some classes involved in this process: Nurse , Agency, Named(abstract), NurseAgency and NurseAgencyIdentity. There is a many-to-many relationship between Nurse--Agency with an extra column nurse record. The Named class is an abstract class that contains the fields id and name and is being used by many tables in my design being id the identifier of the descendant table. To implement the many-to-many I had to use the #Embeddable annotation in the last class NurseAgencyIdentity which is the id of my NurseAgency join table. Here is the code:
NurseAgencyIdentity
#Embeddable
#Data
public class NurseAgencyIdentity implements Serializable {
#Column(name="nurse_id")
private Long nurseId;
#Column(name="agency_id")
private Long agencyId;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NurseAgencyIdentity that = (NurseAgencyIdentity) o;
return Objects.equals(nurseId, that.nurseId) &&
Objects.equals(agencyId, that.agencyId);
}
#Override
public int hashCode() {
return Objects.hash(nurseId, agencyId);
}
}
NurseAgency
#Entity
#Data
public class NurseAgency {
#EmbeddedId
private NurseAgencyIdentity id;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("nurseId")
private Nurse nurse;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("agencyId")
private Agency agency;
private String nurseRecord;
}
Nurse
#Entity
#Data
public class Nurse {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
#Enumerated(EnumType.STRING)
private License license;
#OneToMany(mappedBy = "nurse", cascade = CascadeType.ALL, orphanRemoval = true)
private List<NurseAgency> agencies = new ArrayList<>();
// need the extra column
public void addAgency(Agency agency) {//, String nurseRecord) {
NurseAgency nurseAgency = new NurseAgency();
nurseAgency.setAgency(agency);
nurseAgency.setNurse(this);
//nurseAgency.setNurseRecord(nurseRecord);
agency.getNurses().add(nurseAgency);
}
public void removeAgency(Agency agency) {
for (Iterator<NurseAgency> iterator = agencies.iterator(); iterator.hasNext(); ) {
NurseAgency nurseAgency = iterator.next();
if (nurseAgency.getNurse().equals(this) && nurseAgency.getAgency().equals(agency)){
iterator.remove();
nurseAgency.getAgency().getNurses().remove(nurseAgency);
nurseAgency.setNurse(null);
nurseAgency.setAgency(null);
}
}
}
#Override
public String toString() {
return id + " " + firstName + " " + middleName + " " + lastName;
}
}
Named
#MappedSuperclass
#EntityListeners(AuditingEntityListener.class)
#Data
public abstract class Named implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
Agency
#Entity
#Data
public class Agency extends Named {
private String description;
#OneToMany(mappedBy = "agency", cascade = CascadeType.ALL, orphanRemoval = true)
private List<NurseAgency> nurses = new ArrayList<>();
}
And I am having this error when trying to seed the join table:
BootStrapData
#Component
public class BootStrapData implements CommandLineRunner {
#Autowired
private final NurseRepository nurseRepository;
#Autowired
private final AgencyRepository agencyRepository;
private final NurseAgencyRepository nurseAgencyRepository;
public BootStrapData(NurseRepository nurseRepository, AgencyRepository agencyRepository, NurseAgencyRepository nurseAgencyRepository) {
this.nurseRepository = nurseRepository;
this.agencyRepository = agencyRepository;
this.nurseAgencyRepository = nurseAgencyRepository;
}
#Override
public void run(String... args) throws Exception {
System.out.println("Loading agencies ");
ArrayList<Agency> agencies = GetAgencies();
System.out.println("Loading Nurses ");
ArrayList<Nurse> nurses = GetNurses(agencies);
nurses.stream().forEach( n -> nurseRepository.save(n));
agencies.stream().forEach( a -> agencyRepository.save(a));
//Nurses Agencies
ArrayList<NurseAgency> nurseAgencies = new ArrayList<>(1);
nurseAgencies.addAll(SetNurseAndAgencies(nurses.get(0), new Agency[]{agencies.get(0), agencies.get(1), agencies.get(2)}));
nurseAgencies.addAll(SetNurseAndAgencies(nurses.get(1), new Agency[]{agencies.get(0), agencies.get(1)}));
nurseAgencies.addAll(SetNurseAndAgencies(nurses.get(2), new Agency[]{agencies.get(1), agencies.get(2)}));
for (int i=0; i<nurseAgencies.size();i++){
nurseAgencyRepository.save(nurseAgencies.get(i)); // I've got the error in first iteration in this line
}
}
private ArrayList<Agency> GetAgencies() {
ArrayList<Agency> agencies = new ArrayList<>(3);
Agency a1 = new Agency();
a1.setName("Agency 1");
agencies.add(a1);
Agency a2 = new Agency();
a2.setName("Agency 2");
agencies.add(a2);
Agency a3 = new Agency();
a3.setName("Agency 3");
agencies.add(a3);
return agencies;
}
private ArrayList<Nurse> GetNurses(ArrayList<Agency> agencies) {
ArrayList<Nurse> nurses = new ArrayList<>(3);
Nurse n1 = new Nurse();
n1.setFirstName("Mario");
n1.setLastName("Perez");
nurses.add(n1);
Nurse n2 = new Nurse();
n2.setFirstName("Luis");
n2.setLastName("Ruiz");
nurses.add(n2);
Nurse n3 = new Nurse();
n3.setFirstName("Maria");
n3.setLastName("Crez");
nurses.add(n3);
return nurses;
}
private ArrayList<NurseAgency> SetNurseAndAgencies(Nurse nurse, Agency[] agencies) {
ArrayList<NurseAgency> nurseagencies = new ArrayList<>(agencies.length);
for (int i=0; i<agencies.length; i++){
NurseAgency na = new NurseAgency();
na.setNurse(nurse);
na.setAgency(agencies[i]);
na.setNurseRecord(nurse.getFirstName() + agencies[i].getName());
nurseagencies.add(na);
}
return nurseagencies;
}
}
Where is the problem?
Try changing the NurseAgencyIdentity declaration on NurseAgency from:
#EmbeddedId
private NurseAgencyIdentity id;
to:
#EmbeddedId
private NurseAgencyIdentity id = new NurseAgencyIdentity();
I didn't see the full stack trace but the root cause can be a NullPointerException when hibernate tries to set fields (generated agencyId [ 1 ] in your case) via reflection on NurseAgencyIdentity and it's null.
See org.hibernate.tuple.entity.AbstractEntityTuplizer#getIdentifier
In our spring boot Restful WebService, we have two master tables with many-to-many relationship between them. But in the transaction table, we want one extra field (current_time) as part of the embedded key other than the primary keys of the two tables. Now, we’ve created a separate class for defining embedded primary key using #Embeddable. Now, while inserting one transaction row to transaction table using Spring JPA, I am manually setting the primary keys in the corresponding entity and calling the save method on corresponding repository. But It is giving me ConstraintViolationException as the current_time is going with null value even if I have manually set it. Any help would be highly appreciated.
First Entity is as follows :
#Entity
#Table(name = "project")
public class Project {
#Id
#GenericGenerator(name = "projectid", strategy = "com.sample.upload.entity.ProjectIDGenerator")
#GeneratedValue(generator = "projectid")
#Column(name = "projectid")
private String projectID;
#Column(name = "project_name")
private String projectName;
#Column(name = "project_descr")
private String projectDesc;
#Column(name = "project_input_path")
private String projectPath;
#Column(name = "project_creation_time")
private Calendar projectCreationTime;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "project_migration", joinColumns = #JoinColumn(name = "projectid", referencedColumnName = "projectid"), inverseJoinColumns = #JoinColumn(name = "migratorid", referencedColumnName = "migratorid"))
private List<Migrator> migrators;
#Column(name = "account_name")
private String accountName;
#Column(name = "account_group")
private String accountGroup;
public String getProjectID() {
return projectID;
}
public void setProjectID(String projectID) {
this.projectID = projectID;
}
public String getAccountName() {
return accountName;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
public String getAccountGroup() {
return accountGroup;
}
public void setAccountGroup(String accountGroup) {
this.accountGroup = accountGroup;
}
public String getProjectName() {
return projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
public String getProjectDesc() {
return projectDesc;
}
public void setProjectDesc(String projectDesc) {
this.projectDesc = projectDesc;
}
public String getProjectPath() {
return projectPath;
}
public void setProjectPath(String projectPath) {
this.projectPath = projectPath;
}
public Calendar getProjectCreationTime() {
return projectCreationTime;
}
public void setProjectCreationTime(Calendar projectCreationTime) {
this.projectCreationTime = projectCreationTime;
}
public List<Migrator> getMigrators() {
return migrators;
}
public void setMigrators(List<Migrator> migrators) {
this.migrators = migrators;
}
}
Second Entity :
#Entity
#GenericGenerator(name = "generatorName", strategy = "increment")
#Table(name = "migrator")
public class Migrator {
#Id
#GeneratedValue(generator = "generatorName")
#Column(name = "migratorid")
private String migratorId;
#Column(name = "src_tech_name")
private String srcTechName;
#Column(name = "dest_tech_name")
private String destTechName;
#Column(name = "migrator_name")
private String migratorName;
#Column(name = "migrator_type")
private String migratorType;
public String getMigratorId() {
return migratorId;
}
public void setMigratorId(String migratorId) {
this.migratorId = migratorId;
}
public String getSrcTechName() {
return srcTechName;
}
public void setSrcTechName(String srcTechName) {
this.srcTechName = srcTechName;
}
public String getDestTechName() {
return destTechName;
}
public void setDestTechName(String destTechName) {
this.destTechName = destTechName;
}
public String getMigratorName() {
return migratorName;
}
public void setMigratorName(String migratorName) {
this.migratorName = migratorName;
}
public String getMigratorType() {
return migratorType;
}
public void setMigratorType(String migratorType) {
this.migratorType = migratorType;
}
#Override
public String toString() {
return "Technology [migratorId=" + migratorId + ", srcTechName=" + srcTechName + ", destTechName="
+ destTechName + ", migratorName=" + migratorName + ", migratorType=" + migratorType + "]";
}
}
The join (transaction) table's entity :
#Entity
#Table(name = "project_migration")
public class ProjectMigration {
#EmbeddedId
private ProjectMigrationID migrationId;
#Column(name ="migration_finish_time")
private Calendar migrationFinishTime;
#Column(name ="time_in_millis_for_migration")
private long timeInMillisForMigration;
#Column(name ="migration_status")
private String migrationStatus;
#Column(name ="migrated_codebase_path")
private String migratedCodeBasePath;
The embedded Primary Key class is as follows:
#Embeddable
public class ProjectMigrationID implements Serializable {
private static final long serialVersionUID = -3623993529011381924L;
#Column(name = "projectid")
private String projectId;
#Column(name = "migratorid")
private String migratorId;
#Column(name = "migration_start_time")
private Calendar migrationStartTime;
public ProjectMigrationID() {
}
public ProjectMigrationID(String projectId, String migratorId, Calendar migrationStartTime) {
this.projectId = projectId;
this.migratorId = migratorId;
this.migrationStartTime = migrationStartTime;
}
The snippet from service Class :
for (String migratorId : data.getMigratorIds()) {
Migrator migrator = migratorRepository.findByMigratorId(migratorId);
migrators.add(migrator);
}
if (projectId != null) {
project = projectRepository.findByProjectID(projectId);
System.out.println(project==null);
project.setMigrators(migrators);
System.out.println("I am here");
if (project != null) {
//project.setMigrationStatus("In Progress");
ProjectMigrationID pmId = new ProjectMigrationID();
pmId.setProjectId(project.getProjectID());
pmId.setMigratorId(project.getMigrators().get(0).getMigratorId());
pmId.setMigrationStartTime(new GregorianCalendar());
ProjectMigration pm = new ProjectMigration();
pm.setMigrationId(pmId);
pm.setMigrationStatus("Pending");
projectMigrationRepository.save(pm);
That's because of the #JoinTable where the date is not included and it skips the insertion. If you include a column with all the primary keys needed, it will work as expected.
Only the columns mapped via #JoinTable will be included during insertion or update (defaults to true when mapped)
Either include the date time column in the Project class or use association without #JoinTable.
I'm editing via mobile. So please ignore typos if any.