Controller
#RequestMapping(value="/create", method=RequestMethod.POST, consumes={"application/json"})
public Alien addDetails(#RequestBody Alien alien){
return repo.save(alien);
}
Alien.java
#Entity
public class Alien{
#Id
private int id;
private String name;
private String planet;
Getter and setter
Now I want to validate the post json data before saving it to the database.
If any of the field is empty then the controller should return an error.
For example
{"id": 1, "name": "Alien1", "planet":"Mars" }
This is acceptable json data
But if there is any field is missing such as
{"name": "Alien1", "planet":"Mars" }
Then the controller should return an error and not creating the instance of Alien
I tried with #Valid #NotNull still the controller creates an empty instance of Alien and save to the database.
First of all, you should add spring boot validation dependency into your pom.xml :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
And then add annotations like #NotBlank or #NotEmpty to verify if a value is assigned to that field or not. Those annotations should be on the top of your entity's attributes which you want to validate them.For example:
public class Alien{
#NotBlank(message="Name is mandatory")
private String name;
}
Finally, add #Valid annotation into your controller method like:
public Alien addDetails(#Valid #RequestBody Alien alien){
return repo.save(alien);
}
You should add #Validated annotation to your controller to make #Valid annotation do the work. You should also annotate fields in your Entity with constraints.
I.e., name could be #NotBlank.
And yes, make sure that you have imported:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Edit:
In Controller, you should not accept Entity as a body. Instead, you should create a DTO object and then map it to the Entity in the service. You should also put validation constraints on the DTO!
Related
I was trying to store some JSON as a string in a column via JPA and Spring and was following a baeldung tutorial. My code like this:
#Column
#Convert(converter = MyEntityExtentionConverter.class)
private Map<String, Object> myEntityExtention;
MyEntityExtentionConverter is an implementation of javax.persistence.AttributeConverter<Map<String, Object>, String> that converts the string back and forth using the Jackson ObjectMapper.
According to mentioned tutorial this should have been it, however now I get an Error that
'Basic' attribute type should not be a map
Theoretically I could disable it by adding #SuppressWarnings("JpaAttributeTypeInspection") to the annotations, but that feels like ignoring rather than solving the error. What am I doing wrong here?
You have to annotate prop "myEntityExtention" with #Type but is not possible to add both #Type and #Convert..
as you can see in this tutorial you have to define the json type on the top of your entity:
#Entity
#Table(name = "some_table_name")
#TypeDef(name = "json", typeClass = JsonStringType.class)
public class CustomEntity {
then add #Type annotation instead #Convert:
#Type( type = "json" )
private Map<String, Object> myEntityExtention;
be sure to add all the right dependencies/versions.
I.E. i'm using hibernate 5.4 so my dependencies are:
<!-- Hibernate ORM core version 5.4.21.Final (inherited from spring-boot 2.3.4)-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
<version>2.8.4</version>
</dependency>
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<!--for hibernate >= 5.2-->
<version>2.10.2</version>
</dependency>
</dependencies>
Look like this is an issue from IntelliJ IDEA:
https://youtrack.jetbrains.com/issue/IDEA-270687
We can use workaround by this way:
Using the #SuppressWarnings("JpaAttributeTypeInspection") annotation removes the warning.
That field is not meant to be persisted. Remove the #Column annotation and use #Transient.
You are supposed to persist it as a JSON which will be done in the customerAttributeJSON, when reading from the database the customerAttributes will be populated and you can use it with DTOs.
#Entity
#Table(name = "Customers")
public class Customer {
#Id
private int id;
private String firstName;
private String lastName;
private String customerAttributeJSON;
#Transient
#Convert(converter = HashMapConverter.class)
private Map<String, Object> customerAttributes;
}
I hava an Spring Boot Rest Api
#RestController
public class BookController {
#Autowired
private BookRepository bookRepo;
#GetMapping(value = "/library/", produces ={MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
public List<Book> index(){
Iterable<Book> bookIterable = bookRepo.findAll();
List<Book> bookList = new ArrayList<>();
bookIterable.forEach(a->bookList.add(a));
return bookList;
}
My Homework is to add an additonal data representation so that when i put in the request i should can choose between which data representation i won't XML or JSON
Problem is
I get even json how can i change between XML and Json when i do a get Request to the Endpoint
To solve your problem you need to use the Accept header. more details
The Content Type header indicates the type of data that you pass in the request. more details
You need to make a request with the header, if you want to send and receive xml:
Accept: application/xml;
Content-Type: application/xml;
usefull link
Ok now i found it my self what you need to know in order to use an XML output is first add
to the pom.xml file following dependencies: Jackson XML Dataformat
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
Then i just have to Add Jackson Annotations to my Entity Class
#Entity
#JacksonXmlRootElement
public class Book {
public Book() {
}
#JacksonXmlProperty(isAttribute = true)
#Id
#GeneratedValue
private Integer id;
#JacksonXmlProperty(isAttribute = true)
private String title;
#JacksonXmlProperty(isAttribute = true)
private Integer numberOfCopies;
Thats it then i can Make a request with the Accept Header value application/xml
Backend Spring MVC and Hibernate. Front end Angularjs. The database I am using is DB2.
Problem: the data field in my JSON object is returning under the form of a random integer ("startDate":1450828800000). The actual date in the DB is "2015-12-23".
The data type for this column in the db is set to DATE.
My code:
#Entity
#Table(name = "PURCHASEORDER",schema = "POTOOL")
public class PurchaseOrder {
#Id
#GeneratedValue
private int id;
private Date startDate;
//rest of the code, including getter and setters
What you see in your JSON response is not a random integer, rather your DB value converted to milliseconds. You can annotate your Date with the following annotation to have the same representation as inside a DB
#JsonFormat(shape= JsonFormat.Shape.STRING, pattern="yyyy-MM-dd")
private Date startDate;
The annotations belong to jackson-annotation.jar if you're using maven you can add the following dependency and it will pull the annotations jar transitively
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.1</version>
</dependency>
if you're not using maven you can still download the jars from maven repo, or find the equivalent conf for your build, hope it helps
It turns out that I can format the date with Angularjs like this: {{po.startDate |date:"yyyy-MM-dd" }}
Still confused why the on the JSON response the date is not converted...
I want to send json data to controller in spring.How to configure jackson in dispatcher servlet and which jackson files to add in build path/lib?
You need to add the Jackson dependency first:
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.7.1</version> //your version//
</dependency>
You must add #ResponseBody statement in your code as well. For example:
public class JSONController {
#RequestMapping(value="{name}", method = RequestMethod.GET)
public #ResponseBody Shop getShopInJSON(#PathVariable String name) {
Shop shop = new Shop();
shop.setName(name);
shop.setStaffName(new String[]{"mkyong1", "mkyong2"});
return shop;
}
}
Also, add < mvc:annotation-driven /> into your Spring XML configuration file.
You can find a full example of Jackson and Spring in this link.
I have such kind of #OneToOne Hibernate relationShip
public class Address implements Serializable {
private String id;
private String city;
private String country;
//setter getters ommitted
}
public class Student implements Serializable {
private String id;
private String firstName;
private String lastName;
private Address address;
}
address Item is mapped as LAZY.
Now I want to fetch user and it's address using
session.load(Student.class,id);
In my daoService.
Then I return it as JSON from my Spring MVC controller:
#RequestMapping(value="/getStudent.do",method=RequestMethod.POST)
#ResponseBody
public Student getStudent(#RequestParam("studentId") String id){
Student student = daoService.getStudent(id);
return student;
}
Unfortunately, it's not working because of Lazy clasees and I fails with:
org.codehaus.jackson.map.JsonMappingException: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: com.vanilla.objects.Student_$$_javassist_1["address"]->com.vanilla.objects.Address_$$_javassist_0["handler"])
at org.codehaus.jackson.map.ser.StdSerializerProvider$1.serialize(StdSerializerProvider.java:62)
I do use OpenSessionInViewInterceptor and it works just fine.
I understand that I can user left join HQL query and retrieve student and address that way and solve the problem. I also understand that changing relation to EAGER will solve it.
But how can I serialize to JSON lazy classes using standard jackson message converter which of cause I added to my XML file.
The easiest solution: Don't serialize entities, use Value Objects.
If that is not an option for you, make sure that the entity Object is detached.
With JPA (2), you would use EntityManager.detach(entity), with plain Hibernate the equivalent is Session.evict(entity).
Once I write a processor to handle this but now it's easy to fix this by using the jackson hibernate module.
Within your DAO method add Hibernate.initialize(<your getter method>); to resolve this.
Student student = findById(<yourId>);
Hibernate.initialize(student.getAddress());
...
return student;
Try like the above.
There is another option that solves your problems. You can add this filter in web.xml
<filter>
<filter-name>springOpenEntityManagerInViewFilter</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
<init-param>
<param-name>entityManagerFactoryBeanName</param-name>
<param-value>entityManagerFactory</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>springOpenEntityManagerInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
The problem is that entities are loaded lazy and serialization happens before they get loaded fully.
But how can I serialize to JSON lazy classes using standard jackson
message converter which of cause I added to my XML file.
First of all, I don't advise to use DTO/Value Object only to solve this issue.
You may find it easy at the beginning but at each new development/change, the duplicate code means making twice modifications at each time... otherwise bugs.
I don't mean that VO or DTO are bad smells but you should use them for reasons they are designed (such as providing a content/structure that differs according to logical layers or solving an unsolvable serialization problem).
If you have a clean and efficient way to solve the serialization issue without VO/DTO and you don't need them, don't use them.
And about it, there is many ways to solve lazy loading issue as you use Jackson with Hibernate entities.
Actually, the simplest way is using FasterXML/jackson-datatype-hibernate
Project to build Jackson module (jar) to support JSON serialization
and deserialization of Hibernate (http://hibernate.org) specific
datatypes and properties; especially lazy-loading aspects.
It provides Hibernate3Module/Hibernate4Module/Hibernate5Module, extension modules that can be registered with ObjectMapper to provide a well-defined set of extensions related to Hibernate specificities.
To do it working, you just need to add the required dependency and to add the
Jackson Module available during processings where it is required.
If you use Hibernate 3 :
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate3</artifactId>
<version>${jackson.version.datatype}</version>
</dependency>
If you use Hibernate 4 :
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate4</artifactId>
<version>${jackson.version.datatype}</version>
</dependency>
And so for...
Where jackson.version.datatype should be the same for the used Jackson version and the ackson-datatype extension.
If you use or may use Spring Boot, you just need to declare the module as a bean in a specific Configuration class or in the SpringBootApplication class and it will be automatically registered for any Jackson ObjectMapper created.
The 74.3 Customize the Jackson ObjectMapper Spring Boot section states that :
Any beans of type com.fasterxml.jackson.databind.Module will be
automatically registered with the auto-configured
Jackson2ObjectMapperBuilder and applied to any ObjectMapper instances
that it creates. This provides a global mechanism for contributing
custom modules when you add new features to your application.
For example :
#Configuration
public class MyJacksonConfig {
#Bean
public Module hibernate5Module() {
return new Hibernate5Module();
}
}
or :
#SpringBootApplication
public class AppConfig {
public static void main(String[] args) throws IOException {
SpringApplication.run(AppConfig.class, args);
}
#Bean
public Module hibernate5Module() {
return new Hibernate5Module();
}
}