Spring boot JPA with 1:M primary key null - java

I have 2 entitied (Post and FileUploads), the Post entity is responsible for a user to create an advert/post and the FileUploads entity is responsible for handling image uploads together with a post. (A post can have multiple FileUploads/Images associated with it).
The issue is that I get an error:
Column 'post_id' cannot be null
I'm unsure as to what's causing it, The PK in the FileUpload entity is a String and the Post entity PK is a Long, I don't know if that could be the cause?
I am auto-generating my Post PK as well.
FileUpload.java (Entity)
#Entity
#Table(name="file_upload")
public class FileUpload {
#Id
#GenericGenerator(name = "uuid", strategy = "uuid2")
#GeneratedValue(generator = "uuid")
#Column(name="id")
private String fileId;
private int imageCount;
private String name;
private String type;
private String fileUploader;
#Lob
private byte[] data;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="post_id", nullable = false)
private Post post;
public FileUpload() {
}
public FileUpload(int imageCount, String name, String type, String fileUploader, byte[] data, Post post) {
this.imageCount = imageCount;
this.name = name;
this.type = type;
this.fileUploader = fileUploader;
this.data = data;
this.post = post;
}
Post.java (Entity)
Left out the other fields since i have a quite a bit
#Entity
public class Post {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotBlank(message = "Required")
#Size(max=45, message = "Maximum of 45 letters")
#Column(unique = true)
private String title;
private String postCreatorEmail;
private String postCreator;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "post")
private List<FileUpload> fileUploads = new ArrayList<>();
public Post() {
}
FileServiceImpl.java
Method to store images
#Service
public class FileUploadServiceImpl implements FileUploadService{
#Autowired
private FileUploadRepository fileUploadRepository;
#Autowired
private PostRepository postRepository;
private int imageCount;
#Override
public FileUpload uploadPostImage(MultipartFile file, String emailAddress) throws IOException {
Post thePost = postRepository.findPostByPostCreatorEmail(emailAddress);
String fileName = StringUtils.cleanPath(file.getOriginalFilename());
FileUpload fileDB = new FileUpload(++imageCount, fileName, file.getContentType(), emailAddress, file.getBytes(), thePost);
return fileUploadRepository.save(fileDB);
}
}
PostServiceImpl.java
#Service
public class PostServiceImpl implements PostService{
#Autowired
private PostRepository postRepository;
#Autowired
private UserRepository userRepository;
#Autowired
private FileUploadServiceImpl fileUploadService;
private int count;
#Override
public Post createOrUpdatePost(String post, String emailAddress){
// Removed update code
Post thePost = new Post();
try {
User user = userRepository.findUserByEmailAddress(emailAddress);
ObjectMapper objectMapper = new ObjectMapper();
thePost = objectMapper.readValue(post, Post.class);
user.setTotalAds(++count);
thePost.setPostCreator(user.getFullName());
thePost.setPostCreatorEmail(emailAddress);
thePost.setFileUploads(thePost.getFileUploads());
thePost.setUser(user);
userRepository.save(user);
return postRepository.save(thePost);
// The ad with the same title already exists - go to catch block
} catch (Exception e) {
throw new PostAlreadyExistsException("Post with title " + thePost.getTitle() + " already exists");
}
}
}
PostController.java (Only adding the handler method)
#PostMapping("/create")
public ResponseEntity<?> createPost(#RequestPart("file") MultipartFile file, #Valid #RequestPart String post, BindingResult result, Principal principal) {
ResponseEntity<?> errorMap = errorValidationService.validationService(result);
if(errorMap != null) return errorMap;
String message = "";
try {
fileUploadService.uploadPostImage(file, principal.getName());
postService.createOrUpdatePost(post, principal.getName());
message = "Uploaded the file successfully: " + file.getOriginalFilename();
return ResponseEntity.status(HttpStatus.OK).body(new ApiResponse(message, true));
} catch (Exception e) {
message = "Could not upload the file: " + file.getOriginalFilename() + "!";
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ApiResponse(message, false));
}
}
Postman results
Console output
Post DDL MySQL
FileUpload DDL MySQL

Try to add modify your uploadPostImage to this code:
#Override
public FileUpload uploadPostImage(MultipartFile file, String emailAddress) throws IOException {
Post thePost = postRepository.findPostByPostCreatorEmail(emailAddress);
// new part:
if (thePost == null) {
throw new RuntimeException("No post found yet for eMail-Address '" + emailAddress + "' to store images for!");
}
String fileName = StringUtils.cleanPath(file.getOriginalFilename());
FileUpload fileDB = new FileUpload(++imageCount, fileName, file.getContentType(), emailAddress, file.getBytes(), thePost);
return fileUploadRepository.save(fileDB);
}

Related

How to display an blob image received from database using Spring Boot

Good day dear community. I got a problem when trying to display received image from MySQL via Base64. Image uploaded and stored on DB without a problems.
My model class:
#Entity
public class PostModel {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column (name = "title")
private String title;
#Column (name = "preview")
private String preview;
#Column (name = "content")
private String content;
#Column (name = "views")
private int views;
#Lob
#Column (name = "image")
private byte[] image;
//Getters and setters
Controller:
#GetMapping("/blog/{id}")
public String showContent(#PathVariable(value = "id") long id, Model model) throws
UnsupportedEncodingException {
if (!postRepository.existsById(id)) {
return "redirect:/post_not_exist";
}
Optional<PostModel> post = postRepository.findById(id);
ArrayList<PostModel> content = new ArrayList<>();
post.ifPresent(content::add);
model.addAttribute("post", content);
byte[] encodeBase64 = Base64.getEncoder().encode(post.get().getImage());
String base64Encoded = new String(encodeBase64, "UTF-8");
model.addAttribute("contentImage", base64Encoded );
return "post_content";
}
And HTML tag:
<img src="data:image/jpg;base64,${contentImage}"/>
For result, I have this: The problem element
What I doing wrong?
Good wishes.
You need to add to the view with modelAndView.addObject("contentImage",base64Encoded ); and also import ModelAndView and change your method to ModelAndView and instance the class ModelAndView with ModelAndView modelAndView = new ModelAndView("view"); like this:
import org.springframework.web.servlet.ModelAndView;
#GetMapping("/blog/{id}")
public ModelAndView showContent(#PathVariable(value = "id") long id, Model model) throws
UnsupportedEncodingException {
if (!postRepository.existsById(id)) {
return "redirect:/post_not_exist";
}
Optional<PostModel> post = postRepository.findById(id);
ArrayList<PostModel> content = new ArrayList<>();
post.ifPresent(content::add);
model.addAttribute("post", content);
byte[] encodeBase64 = Base64.getEncoder().encode(post.get().getImage());
String base64Encoded = new String(encodeBase64, "UTF-8");
model.addAttribute("contentImage", base64Encoded );
ModelAndView modelAndView = new ModelAndView("view");
modelAndView.addObject("contentImage",base64Encoded );
return modelAndView;
}
With this, you can call the variables returned from `modelAndView and you can add more values if you want.
Here is a link that can help you with this topic with some examples: ModelAndView

How GET a list of files

I need that every time I list a portfolio it returns all the images that exist in that specific portfolio
I can list 1 by 1 via ID but when I send my endpoint to list all photos belonging to the ID of a specific portfolio it only returns me null
Photo Class
#Entity
public class Foto {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String nomeArquivo;
#ManyToOne(cascade = CascadeType.MERGE)
private Perfil autonomo;
#Column(length = 5_000_000)
private byte[] fotoByte;
private String tipoArquivo;
}//Getters and Setters
AutonomoService
#Autowired
FotosRepository fotosRepository;
#Autowired
PerfisRepository perfisRepository;
public List<byte[]> portfolio(int id){
if (perfisRepository.existsById(id)) {
return fotosRepository.findAllByAutonomoId(id).stream().map(f-> f.getFotoByte()).collect(Collectors.toList());
}
else {
return null;
}
} //Getters and Setters
Controller
#GetMapping("/portfolio/fotos/{id}")
public ResponseEntity<List<byte[]>> getPortfolioAutonomo(#PathVariable int id) throws IOException {
List<byte[]> result = autonomoService.portfolio(id);
return ResponseEntity.status(200).body(result);
}
And this is the way I can get 1 photo by its id
#GetMapping("/portfolio/{id}")
public ResponseEntity getPortfolio(#PathVariable int id){
Optional<Foto> anexoOptional = fotosRepository.findById(id);
if (anexoOptional.isPresent()) {
Foto anexo = anexoOptional.get();
return ResponseEntity.status(200)
.header("content-type", anexo.getTipoArquivo())
.header("content-disposition", "filename=" + anexo.getNomeArquivo())
.body(anexo.getFotoByte());
} else {
return ResponseEntity.status(404).build();
}
}
Instead of
return fotosRepository.findAllByAutonomoId(id).stream().map(f-> f.getFotoByte()).collect(Collectors.toList());
Can you try with
return fotosRepository.findAllById(id).stream().map(f-> f.getFotoByte()).collect(Collectors.toList());
If still this is not working, better to go with #Query implemention.

Cannot deserialize from String value Spring-Boot Restful project

I'm working on a Spring Boot + Maven + Restful + Hibernate project! After creating the RestController for adding new Devices in database i'm getting this error:
2018-03-28 10:15:18.786 WARN 9286 --- [nio-9090-exec-9] .w.s.m.s.DefaultHandlerExceptionResolver : Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.hhm.hsy.hibernate.models.Protocol` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{"id":5,"protocolName":"ProtocolForTesting","port":5202}'); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.hhm.hsy.hibernate.models.Protocol` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{"id":5,"protocolName":"ProtocolForTesting","port":5202}')
at [Source: (PushbackInputStream); line: 1, column: 52] (through reference chain: com.hhm.hsy.hibernate.models.Device["protocol"])
Here is my first entity:
#Entity
#Table(name = "devices", catalog = "hqm")
public class Device implements Serializable {
private static final long serialVersionUID = -8311225474375837513L;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "device_id", unique = true, nullable = false)
private Integer id;
#Column(name = "device_name", unique = true, nullable = false)
private String deviceName;
#ManyToOne
#JoinColumn(name = "protocol_id")
private Protocol protocol;
public Device() {
}
public Device(Integer id, String deviceName, Protocol protocol) {
this.id = id;
this.deviceName = deviceName;
this.protocol = protocol;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDeviceName() {
return deviceName;
}
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public Protocol getProtocol() {
return protocol;
}
public void setProtocol(Protocol protocol) {
this.protocol = protocol;
}
And the second entity:
#Entity
#Table(name = "protocols", catalog = "hqm")
public class Protocol implements Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "protocol_id", unique = true, nullable = false)
private Integer id;
#Column(name = "protocol_name", unique = true, nullable = false, length = 45)
private String protocolName;
#Column(name = "port", nullable = false)
private Integer port;
#OneToMany(mappedBy = "protocol", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Device> devices = new HashSet<>();
public Protocol() {
}
public Protocol(Integer id, String protocolName, Integer port) {
this.id = id;
this.protocolName = protocolName;
this.port = port;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getProtocolName() {
return protocolName;
}
public void setProtocolName(String protocolName) {
this.protocolName = protocolName;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
#JsonIgnore
public Set<Device> getDevices() {
return devices;
}
#JsonIgnore
public void setDevices(Set<Device> devices) {
this.devices = devices;
}
}
Controller:
#RestController
#RequestMapping(value = "/api/devices")
#ComponentScan({"com.hhm.hsy.pmcs.*"})
public class DevicesController {
#Autowired
#Qualifier(value = "deviceService")
GenericServiceIntf deviceService;
// get ALL DEVICE
#RequestMapping(value = "", method = RequestMethod.GET)
public Map<String, Object> getDevices() {
Map<String, Object> devicesMap = new HashMap<>();
devicesMap.put("devices", deviceService.getAll());
return devicesMap;
}
//save a new DEVICE
#RequestMapping(value = "", method = RequestMethod.POST, consumes = {"application/json"}, produces = {"application/json"})
#ResponseStatus(HttpStatus.CREATED)
public ResponseEntity<Device> addDevice(#RequestBody Device device) {
deviceService.save(device);
return ResponseEntity.accepted().body(device);
}
}
Service:
#Service("deviceService")
public class DeviceServiceImpl extends GenericServiceAbstractImpl<Device, Integer> implements Serializable{
private static final long serialVersionUID = 697655212967127150L;
#Autowired
public DeviceServiceImpl(#Qualifier("deviceDao") GenericDaoIntf genericDao) {
super(genericDao);
}
}
So when i'm trying to add a new device, i get the error i mentioned upper.I don't know what is causing this exception. When I try to add with post a new Protocol it's working, table is being created in the database correctly and I am getting the data correctly in GET request as well..Please help me, I'm new to springboot and restful... if some more information is required, please just inform me and i will post it! Thank you!
I tried to reproduce your problem: here, but everything works as expected.
I think it can be related with this bug.
You should try to reproduce bug with different jackson version.
EDIT:
One more thing: It looks like you try to construct Protocol instead of Device. Show us your deviceService, if you can.
Failed to read HTTP message:
org.springframework.http.converter.HttpMessageNotReadableException:
JSON parse error: Cannot construct instance of
`com.hhm.hsy.hibernate.models.Protocol

Error reading entity from input stream Dropwizard example

I'm attempting to use this dropwizard example and build off of it. I tried to add a column userName to the people table in Person.java like below
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "fullName", nullable = false)
private String fullName;
#Column(name = "jobTitle", nullable = false)
private String jobTitle;
#Column(name = "userName", nullable = false)
private String userName;
public Person() {
}
public Person(String fullName, String jobTitle, String userName) {
this.fullName = fullName;
this.jobTitle = jobTitle;
this.userName = userName;
}
I added the appropriate getters and setters, and equals method.
However I'm getting an error reading entity from input stream in this block.
#Test
public void testPostPerson() throws Exception {
final Person person = new Person("Dr. IntegrationTest", "Chief Wizard", "Dr. Wizard");
final Person newPerson = RULE.client().target("http://localhost:" + RULE.getLocalPort() + "/people")
.request()
.post(Entity.entity(person, MediaType.APPLICATION_JSON_TYPE))
--> .readEntity(Person.class);
assertThat(newPerson.getId()).isNotNull();
assertThat(newPerson.getFullName()).isEqualTo(person.getFullName());
assertThat(newPerson.getJobTitle()).isEqualTo(person.getJobTitle());
assertThat(newPerson.getUserName()).isEqualTo(person.getUserName());
}
the input stream error is caused by the following
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:
Unrecognized field "code" (class com.example.helloworld.core.Person), not marked as ignorable (4 known properties: "fullName", "id", "userName", "jobTitle"])
will #JsonIgnoreProperties annotation at the class level solve this problem? Is this safe practice?
EDIT: PersonResource.java
#Path("/people/{personId}")
#Produces(MediaType.APPLICATION_JSON)
public class PersonResource {
private final PersonDAO peopleDAO;
public PersonResource(PersonDAO peopleDAO) {
this.peopleDAO = peopleDAO;
}
#GET
#UnitOfWork
public Person getPerson(#PathParam("personId") LongParam personId) {
return findSafely(personId.get());
}
#GET
#Path("/view_freemarker")
#UnitOfWork
#Produces(MediaType.TEXT_HTML)
public PersonView getPersonViewFreemarker(#PathParam("personId") LongParam personId) {
return new PersonView(PersonView.Template.FREEMARKER, findSafely(personId.get()));
}
#GET
#Path("/view_mustache")
#UnitOfWork
#Produces(MediaType.TEXT_HTML)
public PersonView getPersonViewMustache(#PathParam("personId") LongParam personId) {
return new PersonView(PersonView.Template.MUSTACHE, findSafely(personId.get()));
}
private Person findSafely(long personId) {
return peopleDAO.findById(personId).orElseThrow(() -> new NotFoundException("No such user."));
}
I think it's because the resource fails and throws a web application exception and code is actually the http status code.
Try it like this:
Response response = RULE.client().target("http://localhost:" + RULE.getLocalPort() + "/people")
.request()
.post(Entity.entity(person, MediaType.APPLICATION_JSON_TYPE));
assertEquals(200, response.getStatus());
Person newPerson = response.readEntity(Person.class);
....
You may also debug like this:
String responseString = response.readEntity(String.class);
Which will dump you the body of the response.

How to send a JSON object with an array of objects in it to the spring controller?

I have two domain models mapped using Hibernate #OneToMany. I am trying to create a JSON object in the frontend and send it to the spring mvc controller to set the model data on its own.
Following are my model classes:
ConceptModelDetails.java
#Entity
#Table(name="conceptModelDetails")
#SequenceGenerator(name="CONCEPT_SEQ",sequenceName="concept_sequence", initialValue=1, allocationSize=1)
public class ConceptModelDetails implements java.io.Serializable{
private static final long serialVersionUID = 1L;
#Id #GeneratedValue(strategy = GenerationType.SEQUENCE, generator="CONCEPT_SEQ")
private int instructionsId;
private String operationType;
private String conceptModelID;
private String requestor;
private String status;
private Timestamp requestDateTime;
private Timestamp lastExecutedDateTime;
private Timestamp completedDateTime;
#OneToMany(fetch = FetchType.EAGER, cascade=CascadeType.ALL, mappedBy="conceptModelDetails")
#JsonManagedReference // nested exception is org.springframework.http.converter.HttpMessageNotWritableException:
//Could not write JSON: Infinite recursion
//The fix is to get Jackson to be able to handle bi-directional references
private List<Instructions> instructions = new ArrayList<Instructions>();
public ConceptModelDetails() {
// TODO Auto-generated constructor stub
}
//setter & getter methods
}
and Instructions.java:
#Entity
#Table(name="instructions")
#SequenceGenerator(name="INSTRUCTIONS_SEQ", sequenceName="instructions_sequence",initialValue=1, allocationSize=1)
public class Instructions implements java.io.Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator="INSTRUCTIONS_SEQ")
private int Sno;
private String instruction;
#ManyToOne
#JoinColumn(name="instructionsId")
#JsonBackReference
private ConceptModelDetails conceptModelDetails;
//setter & getter methods
}
This is my send method at frontend to create and send the JSON object:
$scope.send = function() {
console.log("test");
var dataObj = {
"operationType" : $scope.operationType,
"conceptModelID" : $scope.conceptID,
"requestor" : $scope.requestor,
"status" : "new",
"requestDateTime" : null,
"lastExecutedDateTime" : null,
"completedDateTime" : null,
"instructions" : null
};
console.log(dataObj);
dataObj.instructions = [];
console.log($scope.operations_publish);
var ins = getSelected();
for ( var i in ins) {
var temp = {
instruction : null,
conceptModelDetails : null
}
temp.instruction = ins[i];
dataObj.instructions.push(temp);
}
var response = $http.post(
'PostService', dataObj);
response.success(function(data, status, headers, config) {
$scope.responseData = data;
});
response.error(function(data, status, headers, config) {
alert("Exception details: " + JSON.stringify({
data : data
}));
});
}
Following is my controller:
#RequestMapping(value = "/PostService", method = RequestMethod.POST)
public #ResponseBody String Test(#RequestBody ConceptModelDetails conceptModelDetails){
ApplicationContext context = new ClassPathXmlApplicationContext(
"applicationContext.xml");
ConceptModelDAO obj = (ConceptModelDAO) context.getBean("objDAO");
System.out.println("concept id: "+conceptModelDetails.getConceptModelID()+" "+ conceptModelDetails.getInstructionsId());
System.out.println("instructions id: "+conceptModelDetails.getInstructions());
// ConceptModelDAOImpl objDAO = new ConceptModelDAOImpl();
obj.add(conceptModelDetails);
Instructions instructions = new Instructions();
System.out.println("dimba: " + instructions.getInstruction());
ArrayList<Instructions> operations = (ArrayList<Instructions>) conceptModelDetails.getInstructions();
for (int i = 0; i< operations.size(); i++ ) {
instructions.setInstruction(operations.get(i).getInstruction());
instructions.setConceptModelDetails(conceptModelDetails);
obj.addInstructions(instructions);
}
return null;
}
I am getting the eror: 400 (Bad Request) because of List<Instructions> instructions. Please suggest how do I deal with this.
I have found the problem in this code. As explained by Bozho here,
ArrayList<Instructions> operations = (ArrayList<Instructions>) conceptModelDetails.getInstructions();
should be
List<Instructions> operations = conceptModelDetails.getInstructions();
in the spring controller.

Categories

Resources