I am developing a Spring Boot application using MapStruct for mapping between DTOs and entities. The application has a Many-to-Many relationship between Post and Tag entities. When I send a request to create a Post without tags, it works perfectly. However, when I include tagIds in the request, I encounter a and foreign key constraint violations. Despite ensuring that the tags exist in the database, the error persists. I have tried various solutions, including using and in MapStruct, but the issue continues. I need help resolving this problem.StackOverflowError@Named@BeanMapping
I have tried the following steps to resolve the issue:
Ensured that the ‘tagIds‘ exist in the database by querying the tag table and verifying the records.
Updated the PostMapper and TagMapper to handle circular dependencies using @Named and @BeanMapping annotations in MapStruct.
Simplified the TagMapper to map Set directly to Set.
I expected these changes to allow the creation of a Post with associated Tags without errors. However, when I include tagIds in the request, I still encounter a StackOverflowError and foreign key constraint violations.
What actually happened?
When I send a request to create a Post without tagIds, the Post is created successfully, and I receive the expected response.
Request without Tags:
{
"title": "Oracle OCI",
"content": "Oracle OCI",
"subCategoryId": 9,
"tagIds": [],
"author": "Fsengul",
"featuredImageUrl": "http://example.com/imageora.jpg",
"publishedDate": "2024-05-19T12:00:00",
"isActive": true,
"isFeatured": false,
"metaDescription": "A detailed post about oracle variables.",
"metaKeywords": "oracle"
}
Response:
{
"id": 21,
"title": "Oracle OCI",
"content": "Oracle OCI",
"subCategoryId": 9,
"tags": [],
"author": "Fsengul",
"featuredImageUrl": "http://example.com/imageora.jpg",
"publishedDate": "2024-05-19T12:00:00",
"viewCount": 0,
"likeCount": 0,
"isActive": true,
"isFeatured": false,
"metaDescription": "A detailed post about oracle variables.",
"metaKeywords": "oracle",
"createdDate": "2024-05-20T11:47:44.544941",
"lastModifiedDate": null
}
When I include tagIds in the request, I receive a long list of “No static resource api” messages and the following error:
Request with Tags:
{
"title": "Oracle OCI",
"content": "Oracle OCI",
"subCategoryId": 9,
"tagIds": [1, 2],
"author": "Fsengul",
"featuredImageUrl": "http://example.com/imageora.jpg",
"publishedDate": "2024-05-19T12:00:00",
"isActive": true,
"isFeatured": false,
"metaDescription": "A detailed post about oracle variables.",
"metaKeywords": "oracle"
}
Console Output:
No static resource api/No static resource api/... could not execute statement [ERROR: insert or update on table "post_tags" violates foreign key constraint "fkp7cfgjsujc2vl3p88qfqkpws6" Ayrıntı: Key (tag_id)=(2) is not present in table "tag".] [insert into post_tags (post_id, tag_id) values (....................]
Despite ensuring the tagIds exist in the database, the error persists. I need help to resolve this issue so that I can create a Post with associated Tags without encountering errors.
Entities
Post Entity
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(name = "posts")
public class Post extends BaseEntity {
@Column(nullable = false)
private String title;
@Column(nullable = false, columnDefinition = "TEXT")
private String content;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "sub_category_id", nullable = false)
private SubCategory subCategory;
@ManyToMany
@JoinTable(
name = "post_tags",
joinColumns = @JoinColumn(name = "post_id"),
inverseJoinColumns = @JoinColumn(name = "tag_id")
)
private Set<Tag> tags = new HashSet<>();
@Column(nullable = true)
private String author;
@Column(nullable = true)
private String featuredImageUrl;
@Column(nullable = true)
private LocalDateTime publishedDate;
@Column(nullable = false)
private int viewCount = 0;
@Column(nullable = false)
private int likeCount = 0;
@Column(nullable = true)
private Boolean isActive;
@Column(nullable = true)
private Boolean isFeatured;
@Column(nullable = true)
private String metaDescription;
@Column(nullable = true)
private String metaKeywords;
// Getters and Setters
}
Tag Entity
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
@Entity
@Table(name = "tags")
public class Tag extends BaseEntity {
@Column(nullable = false, unique = true)
private String name;
@Column(nullable = true)
private String description;
@ManyToMany(mappedBy = "tags")
private Set<Post> posts = new HashSet<>();
@Column(nullable = true)
private Boolean isActive;
// Getters and Setters
}
DTOs
PostRequest
import java.time.LocalDateTime;
import java.util.Set;
public class PostRequest {
private String title;
private String content;
private Long subCategoryId;
private Set<Long> tagIds;
private String author;
private String featuredImageUrl;
private LocalDateTime publishedDate;
private Boolean isActive;
private Boolean isFeatured;
private String metaDescription;
private String metaKeywords;
// Getters and Setters
}
PostResponse
import java.time.LocalDateTime;
import java.util.Set;
public class PostResponse {
private Long id;
private String title;
private String content;
private Long subCategoryId;
private Set<TagResponse> tags;
private String author;
private String featuredImageUrl;
private LocalDateTime publishedDate;
private int viewCount;
private int likeCount;
private Boolean isActive;
private Boolean isFeatured;
private String metaDescription;
private String metaKeywords;
private LocalDateTime createdDate;
private LocalDateTime lastModifiedDate;
// Getters and Setters
}
TagRequest
public class TagRequest {
private String name;
private String description;
private Boolean isActive;
// Getters and Setters
}
TagResponse
import java.time.LocalDateTime;
import java.util.Set;
public class TagResponse {
private Long id;
private String name;
private String description;
private Set<PostResponse> posts;
private Boolean isActive;
private LocalDateTime createdDate;
private LocalDateTime lastModifiedDate;
// Getters and Setters
}
Mapper Interfaces
TagMapper
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.Set;
@Mapper(componentModel = "spring")
public interface TagMapper {
TagMapper INSTANCE = Mappers.getMapper(TagMapper.class);
TagResponse toTagResponse(Tag tag);
Set<TagResponse> toTagResponses(Set<Tag> tags);
Tag toTagEntity(TagRequest tagRequest);
}
PostMapper
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper(componentModel = "spring", uses = {TagMapper.class})
public interface PostMapper {
PostMapper INSTANCE = Mappers.getMapper(PostMapper.class);
@Mapping(source = "subCategory.id", target = "subCategoryId")
@Mapping(source = "tags", target = "tags")
PostResponse toPostResponse(Post post);
@Mapping(source = "subCategoryId", target = "subCategory.id")
@Mapping(target = "tags", ignore = true)
Post toPostEntity(PostRequest postRequest);
}
Service
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Set;
import java.util.stream.Collectors;
@Service
public class PostService {
@Autowired
private PostRepository postRepository;
@Autowired
private SubCategoryRepository subCategoryRepository;
@Autowired
private TagRepository tagRepository;
@Autowired
private PostMapper postMapper;
@Transactional
public PostResponse createPost(PostRequest postRequest) {
Post post = postMapper.toPostEntity(postRequest);
post.setSubCategory(subCategoryRepository.findById(postRequest.getSubCategoryId())
.orElseThrow(() -> new ResourceNotFoundException("SubCategory not found")));
Set<Tag> tags = tagRepository.findAllById(postRequest.getTagIds()).stream().collect(Collectors.toSet());
if (tags.size() != postRequest.getTagIds().size()) {
throw new ResourceNotFoundException("One or more tags not found. Make sure all tags exist in the database.");
}
post.setTags(tags);
post = postRepository.save(post);
return postMapper.toPostResponse(post);
}
}
Controller
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@RestController
@RequestMapping("/api/posts")
@Validated
public class PostController {
@Autowired
private PostService postService;
@PostMapping
public PostResponse createPost(@Valid @RequestBody PostRequest postRequest) {
return postService.createPost(postRequest);
}
}
Database State
I have verified that the Tag entities with IDs 1 and 2 exist in the database. Here is the state of the Tag table:
[
{
"id": 1,
"name": "Java-Spring-Boot",
"description": "Java Tag",
"posts": [],
"isActive": true,
"createdDate": "2024-05-19T17:56:21.93962",
"lastModifiedDate": null
},
{
"id": 2,
"name": "Java-EE",
"description": "Java EE Tag",
"posts": [],
"isActive": true,
"createdDate": "2024-05-19T18:34:05.697195",
"lastModifiedDate": null
},
{
"id": 3,
"name": "Devops",
"description": "Devops",
"posts": [],
"isActive": true,
"createdDate": "2024-05-19T19:20:25.294445",
"lastModifiedDate": null
},
{
"id": 4,
"name": "Spring MVC",
"description": "Spring MVC",
"posts": [],
"isActive": true,
"createdDate": "2024-05-19T21:11:45.590621",
"lastModifiedDate": null
},
{
"id": 5,
"name": "Python",
"description": "Python",
"posts": [],
"isActive": true,
"createdDate": "2024-05-19T21:26:10.882249",
"lastModifiedDate": null
},
{
"id": 6,
"name": "Academic",
"description": "Academic",
"posts": [],
"isActive": true,
"createdDate": "2024-05-19T21:26:28.042141",
"lastModifiedDate": null
}
]
Despite these records being present, the error persists when attempting to create a Post with associated tags.
Question
How can I resolve the StackOverflowError and foreign key constraint violation when creating a Post with associated Tags using Spring Boot and MapStruct? What am I missing or doing wrong in my current setup?
Thank you for your help!