I'm looking for the correct way to implements this operations (delete, post, get) in my controller class using JPA.
Here's my code i'm not sure about post if it is correct but really delete is causing problem because HttpDelete from Apache Component doesn't have setEntity method for request body.
The DAO :
#Repository
public interface PiecesReparationDAO extends JpaRepository<PiecesReparation, Integer>{
PiecesReparation findById(PieceRId id);
PiecesReparation findById_Id_produit(int id);
PiecesReparation findById_Id_Reparation(int id);
void deleteById(PieceRId id);
}
the model :
#Entity
public class PiecesReparation {
#EmbeddedId
PieceRId id;
#MapsId("id_produit")
#ManyToOne(optional = false)
#JoinColumn(name = "id_produit", referencedColumnName = "id")
private Produit product;
#MapsId("id_reparation")
#ManyToOne(optional = false)
#JoinColumn(name = "", referencedColumnName = "id")
private Reparation reparation;
private Timestamp date_utilisation;
public PieceRId getPieceRId(){
return this.id;
}
public void setPieceRId(PieceRId id){
this.id=id;
}
public Timestamp getDateUtilisation(){
return this.date_utilisation;
}
public void setDateUtilisation(Timestamp date){
this.date_utilisation=date;
}
}
classe PieceRId :
#Embeddable
public class PieceRId implements Serializable {
int id_produit;
int id_reparation;
//implements equals and hashCode
public int getIdProduit(){
return this.id_produit;
}
public int getIdReparation(){
return this.id_reparation;
}
public PieceRId(){
}
public PieceRId(int id_produit, int id_reparation){
this.id_produit=id_produit;
this.id_reparation=id_reparation;
}
#Override
public boolean equals(Object o){
if(o ==null) return false;
if(((PieceRId)o).getIdProduit() == this.getIdProduit() &&
((PieceRId)o).getIdReparation() == this.getIdReparation())
return true;
else return false;
}
#Override
public int hashCode() {
return Integer.valueOf(String.valueOf(id_produit)+String.valueOf(id_reparation));
}
}
The controller :
#RestController
public class PiecesReparationController {
#Autowired
private PiecesReparationDAO piecesReparationDao;
#GetMapping(value = "/Pieces")
public ArrayList<PiecesReparation> listePiecesReparations() {
ArrayList<PiecesReparation> listePiecesReparations= new ArrayList() ;
Iterable<PiecesReparation> piecesReparations = piecesReparationDao.findAll();
MappingJacksonValue produitsFiltres = new MappingJacksonValue(piecesReparations);
for (Iterator<PiecesReparation> i = piecesReparations.iterator(); i.hasNext();) {
PiecesReparation item = i.next();
listePiecesReparations.add(item);
}
return listePiecesReparations;
}
//Récupérer un produit par son Id
#GetMapping(value = "/Pieces/{id}")
public PiecesReparation selectionPiecesReparation(#PathVariable PieceRId id) {
return piecesReparationDao.findById(id);
}
//ajouter un produit
#PostMapping(value = "/Pieces")
public ResponseEntity<?> ajouterPiecesReparation(#RequestBody PieceReparationDTO pieceReparation) {
PiecesReparation pieceR = new PiecesReparation();
pieceR.setPieceRId(new PieceRId(pieceReparation.getIdProduit(), pieceReparation.getIdReparation()));
pieceR.setDateUtilisation(pieceReparation.getDateUtilisation());
PiecesReparation piecesReparationAdded = piecesReparationDao.save(pieceR);
if (piecesReparationAdded == null)
return new ResponseEntity<>(HttpStatus.UNPROCESSABLE_ENTITY);
else return new ResponseEntity<>(HttpStatus.CREATED);
}
#DeleteMapping(value = "/Pieces/{id}")
public void supprimerPiecesReparation(#PathVariable PieceRId id) {
piecesReparationDao.delete(id);
}
#DeleteMapping(value = "/Pieces")
public void supprimerPiecesReparation2(#RequestBody PieceRId id) {
PiecesReparation pieceR= new PiecesReparation();
pieceR.setPieceRId(new PieceRId(id.getIdProduit(),id.getIdReparation()));
piecesReparationDao.delete(pieceR);
}
#PutMapping (value = "/Pieces")
public void updateProduit(#RequestBody PieceReparationDTO pieceReparation) {
PiecesReparation pieceR = new PiecesReparation();
pieceR.setPieceRId(new PieceRId(pieceReparation.getIdProduit(), pieceReparation.getIdReparation()));
pieceR.setDateUtilisation(pieceReparation.getDateUtilisation());
PiecesReparation piecesReparationAdded = piecesReparationDao.save(pieceR);
}
}
method to consume web service :
public static void deleteRest(int id, String type){
try{
CloseableHttpClient client = HttpClientBuilder.create().build();
HttpDelete deleteRequest = new HttpDelete(getUrl(type)+"/"+id);
HttpResponse response=client.execute(deleteRequest);
System.out.print("response code :
"+response.getStatusLine().getStatusCode());
client.close();
}catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
I know how to delete with url and path variable but don't how to do with request body because no setEntity method. I'm not sure it it's ok for the others operations if it's implemented correctly. Thanks.
Related
I'm trying to do an Unit test, but I receive the error 'cannot resolve the method thenReturn(suspectTest) ; wrap using java.Optional', but when I do (.thenReturn(Optional.of(suspectTest)), the test fails - 'Optional cannot be cast to class Suspect'. Here it is the test, the SuspectService and the SuspectController (edited post*). Thank you in advance!
#DataJpaTest
public class ControllerTest {
#Mock
Model model;
#Mock
SuspectService suspectService;
SuspectController suspectController;
#Rule
public MockitoRule rule = MockitoJUnit.rule();
#Before
public void setUp() throws Exception {
suspectController = new SuspectController(suspectService);
}
#Captor
ArgumentCaptor<Suspect> argumentCaptor;
#Test
public void showById() {
Integer id = 1;
Suspect suspectTest = new Suspect();
suspectTest.setId(Long.valueOf(id));
when(suspectService.getById(Long.valueOf(id))).thenReturn(Optional.of(suspectTest));
String viewName = suspectController.showById(id.toString(), model);
Assert.assertEquals("info", viewName);
verify(suspectService, times(1)).getById(Long.valueOf(id));
verify(model, times(1))
.addAttribute(eq("suspect"), argumentCaptor.capture() );
Suspect suspectArg = argumentCaptor.getValue();
Assert.assertEquals(suspectArg.getId(), suspectTest.getId() );
}
}
#Service
public class SuspectService implements BaseService<Suspect> {
private final com.unibuc.AWBD_Project_v1.repositories.SuspectRepository suspectRepository;
#Autowired
public SuspectService(SuspectRepository SuspectRepository) {
this.suspectRepository = SuspectRepository;
}
#Override
public Suspect insert(Suspect object) {
return suspectRepository.save(object);
}
#Override
public Suspect update(Long id, Suspect updatedObject) {
var foundId = suspectRepository.findById(id);
return foundId.map(suspectRepository::save).orElse(null);
}
#Override
public List<Suspect> getAll() {
return suspectRepository.findAll();
}
#Override
public Optional<Suspect> getById(Long id) {
return suspectRepository.findById(id);
}
#Override
public void deleteById(Long id)
{
try {
suspectRepository.deleteById(id);
} catch (SuspectException e) {
throw new SuspectException("Suspect not found");
}
}
#Override
public Page<Suspect> findAll(int page, int size, String sortBy, String sortType){
Sort sort = sortType.equalsIgnoreCase(Sort.Direction.ASC.name()) ? Sort.by(sortBy).ascending() :
Sort.by(sortBy).descending();
Pageable pageable = PageRequest.of(page - 1, size, sort);
return suspectRepository.findAll(pageable);
}
}
#RestController
#RequestMapping("/api/suspect")
public class SuspectController implements BaseController<Suspect> {
private final com.unibuc.AWBD_Project_v1.services.SuspectService SuspectService;
#Autowired
public SuspectController(SuspectService SuspectService) {
this.SuspectService = SuspectService;
}
#PostMapping("/suspects/add")
#Override
public String NewObject(Model model) {
model.addAttribute("suspect", new Suspect());
return "addsuspect";
}
#PostMapping("/suspects/update/{id}")
#Override
public String update(#PathVariable Long id, #Valid #ModelAttribute("suspect") Suspect suspect, BindingResult bindingResult, Model model) {
if(bindingResult.hasErrors())
{
boolean update = true;
model.addAttribute("update", update);
return "addsuspect";
}
SuspectService.insert(suspect);
return "redirect:/suspects";
}
#DeleteMapping("/suspects/delete/{id}")
#Override
public String deleteById(#PathVariable Long id) {
try {
SuspectService.deleteById(id);
} catch (SuspectException e)
{
throw new SuspectException("Suspect with " + id + " does not exist!");
}
return "redirect:/suspects";
}
#GetMapping("/suspect/{id}")
public String showById(#PathVariable String id, Model model){
model.addAttribute("suspect",
SuspectService.getById(Long.valueOf(id)));
return "info";
}
#RequestMapping("/suspects")
public String objectsList(#RequestParam(value = "page", required = false) Integer page,
#RequestParam(value = "sortBy", required = false) String sortBy,
#RequestParam(value = "sortType", required = false) String sortType, Model model) {
int size = 5;
if (page == null)
page = 1;
if(sortBy==null)
sortBy ="name";
if(sortType==null)
sortType ="asc";
Page<Suspect> pageSuspect = SuspectService.findAll(page, size, sortBy, sortType);
List<Suspect> suspects = pageSuspect.getContent();
model.addAttribute("currentPage", page);
model.addAttribute("totalPages", pageSuspect.getTotalPages());
model.addAttribute("totalItems", pageSuspect.getTotalElements());
model.addAttribute("sortBy", sortBy);
model.addAttribute("sortType", sortType);
model.addAttribute("reverseSortDir", sortType.equals("asc") ? "desc" : "asc");
model.addAttribute("suspects", suspects);
var suspect = new Suspect();
model.addAttribute("suspect", suspect);
return "suspects";
}
}
i write java code for sending multiple json same objects in java as below..
public class Book {
private String name;
private int prize;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrize() {
return prize;
}
public void setPrize(int prize) {
this.prize = prize;
}
}
public class Books {
List<Book> books;
public List<Book> getBooks() {
return books;
}
public void setBooks(List<Book> books) {
this.books = books;
}
}
#Path("books")
public interface BookIntf {
#Path("/book")
#POST
#Produces({ "application/xml", "application/json" })
#Consumes({ "application/xml", "application/json" })
public Response addBooks(Books books);
}
public class BookServiceImpl implements BookIntf {
public Response addBooks(Books books) {
String status = null;
JSONObject soapDatainJsonObject = null;
String jsontext = null;
try {
BookDao iotdao = new BookDao();
status = iotdao.addBooks(books);
} catch (Exception localException) {
status = "failed in Service layer";
}
return Response.ok().type(MediaType.APPLICATION_JSON).entity(jsontext).build();
}
}
public class BookDao {
public String addBooks(Books books) {
Session aoSession = MDHibernateUtil.getSessionFactory().openSession();
Transaction addObjectTx = null;
String XMLStatusString = null;
StringBuffer sb = new StringBuffer();
try {
addObjectTx = aoSession.beginTransaction();
List inPutAnalytics = books.getBooks();
Iterator itrIPAnalytics = inPutAnalytics.iterator();
while (itrIPAnalytics.hasNext()) {
Book iotAnalyticPOJO = (Book) itrIPAnalytics.next();
String bookName = iotAnalyticPOJO.getName();
int price = iotAnalyticPOJO.getPrize();
System.out.println("bookName:"+bookName);
System.out.println("price: "+price);
}
} catch (Exception localexception) {
if (addObjectTx != null)
addObjectTx.rollback();
} finally {
aoSession.close();
}
return XMLStatusString;
}
}
can you please tell me is their any mistakes in above code. and url for that is:-
http://localhost:8081/Smarter/services/books/book
Request Body:-
{
"books":
[
{
"name":"mani",
"prize":120
},
{
"name":"nani",
"prize":1240
}
]
}
The Error is 415 Unsupported Media Type...
can you please tell me how to pass multiple same objects in java using json.
Thanks..
I am trying hands on Spring Rest service via #Controller. But i came across situation where one method call is responding with valid data and other filtered method call returning incorrect response. Here is my controller
#Controller
#RequestMapping("/company")
public class CompanyDetailsController {
#Autowired
private CompanyServices companyServices;
#RequestMapping(method = RequestMethod.GET)
public #ResponseBody List<CompanyDetails> getCompanyDetails() {
return companyServices.getCompanyDetails();
}
#RequestMapping(value = "/{companyShortCode}", method = RequestMethod.GET)
public #ResponseBody CompanyDetails getCompanyDetails(#PathVariable("companyShortCode") String companyShortCode) {
System.out.println("companyShortCode = "+companyShortCode);
CompanyDetails companyDetails = companyServices.getCompanyDetails(companyShortCode);
/*CompanyDetails companyDetails = new CompanyDetails();
companyDetails.setCompanyCode("abc");
companyDetails.setCompanyEmail("abc#123.com");
companyDetails.setCompanyPhone(92349234);
companyDetails.setWebSite("abc.abc.com");
System.out.println("companyDetails = "+companyDetails);*/
return companyDetails;
}
}
CompanyDetails
#Entity
#Table(name = "company_details")
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class CompanyDetails implements Serializable {
private static final long serialVersionUID = 8713608417980657723L;
#Id
#Column(name="COMPANY_CODE", nullable = false, updatable = false)
private String companyCode = null;
#Column(name = "COMPANY_NAME", nullable = false, updatable = true)
private String companyName = null;
#Column(name = "COMPANY_PHONE", nullable = true, updatable = true)
private long companyPhone = 0;
#Column(name = "COMPANY_EMAIL", nullable = true, updatable = true)
private String companyEmail = null;
#Column(name = "COMPANY_WEBSITE", nullable = true, updatable = true)
private String webSite = null;
public String getCompanyCode() {
return companyCode;
}
public void setCompanyCode(String companyCode) {
this.companyCode = companyCode;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public long getCompanyPhone() {
return companyPhone;
}
public void setCompanyPhone(long companyPhone) {
this.companyPhone = companyPhone;
}
public String getCompanyEmail() {
return companyEmail;
}
public void setCompanyEmail(String companyEmail) {
this.companyEmail = companyEmail;
}
public String getWebSite() {
return webSite;
}
public void setWebSite(String webSite) {
this.webSite = webSite;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((companyCode == null) ? 0 : companyCode.hashCode());
result = prime * result
+ ((companyName == null) ? 0 : companyName.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CompanyDetails other = (CompanyDetails) obj;
if (companyCode == null) {
if (other.companyCode != null)
return false;
} else if (!companyCode.equals(other.companyCode))
return false;
if (companyName == null) {
if (other.companyName != null)
return false;
} else if (!companyName.equals(other.companyName))
return false;
return true;
}
#Override
public String toString() {
return "CompanyDetails [companyCode=" + companyCode + ", companyName="
+ companyName + ", companyPhone=" + companyPhone
+ ", companyEmail=" + companyEmail + ", webSite=" + webSite
+ "]";
}
}
Repository
#Repository
public class DetailsDaoImpl implements DetailsDao {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
#Override
#Transactional(readOnly = true)
public List<CompanyDetails> getCompanyDetails() {
List<CompanyDetails> details = (List<CompanyDetails>) sessionFactory.getCurrentSession().createCriteria(CompanyDetails.class).list();
return details;
}
#Override
#Transactional(readOnly = true)
public CompanyDetails getCompanyDetails(String companyShortCode) {
System.out.println("companyShortCode = "+companyShortCode);
CompanyDetails details = (CompanyDetails) sessionFactory.getCurrentSession().load(CompanyDetails.class, companyShortCode);
System.out.println("details = "+details);
return details;
}
}
Service
public class CompanyServicesImpl implements CompanyServices {
#Autowired
DetailsDao detailsDao;
#Override
public List<CompanyDetails> getCompanyDetails() {
return detailsDao.getCompanyDetails();
}
#Override
public CompanyDetails getCompanyDetails(String companyShortCode) {
System.out.println("companyShortCode = "+companyShortCode);
return detailsDao.getCompanyDetails(companyShortCode);
}
}
If I call List<CompanyDetails> getCompanyDetails(), It return -
[
{
"companyCode": "ABCD",
"companyName": "Test Company",
"companyPhone": 11226123456,
"companyEmail": "abc#abcd.com",
"webSite": "www.abc.com"
},
{
"companyCode": "XYZA",
"companyName": "XYZA Company",
"companyPhone": 1122000000,
"companyEmail": "abc#XYZA.com",
"webSite": "www.XYZA.com"
}
]
But when i call - Public #ResponseBody CompanyDetails getCompanyDetails(#PathVariable("companyShortCode") String companyShortCode) {,
It return -
{"companyPhone": 0}
I tried with dummy Object and it makes it to Json. Tried Creating normal bean and not #Entity and used BeanUtils to copy properties and responding the correct Json. Anyone Know why this is happening?
Updated
before sending response, it is printing as CompanyDetails [companyCode=ABCD, companyName=Test Company, companyPhone=9823611147, companyEmail=abc#abcd.com, webSite=www.abc.com]
In your public CompanyDetails getCompanyDetails(String companyShortCode) method when you are retrieving CompanyDetails object using its Primary Key companyShortCode you are using load method.
load method return a proxy object in which only Primary Key attribute is set and all other attributes are not set. Your session object will send query for retrieving the actual object only when you call any one of getter method for a attribute like details.getCompanyEmail().
So for avoiding that you can call get method. See below code snippet:
#Override
#Transactional(readOnly = true)
public CompanyDetails getCompanyDetails(String companyShortCode) {
System.out.println("companyShortCode = "+companyShortCode);
CompanyDetails details = (CompanyDetails) sessionFactory.getCurrentSession().get(CompanyDetails.class, companyShortCode);
System.out.println("details = "+details);
return details;
}
}
If you want to go for load method then use below code snippet:
#Override
#Transactional(readOnly = true)
public CompanyDetails getCompanyDetails(String companyShortCode) {
System.out.println("companyShortCode = "+companyShortCode);
CompanyDetails details = (CompanyDetails) sessionFactory.getCurrentSession().load(CompanyDetails.class, companyShortCode);
details.getCompanyName(); //after this all attribute of CompanyDetails object will be set
System.out.println("details = "+details);
return details;
}
}
For more reference see this
Hope it helps.
First of all the code is not completely posted, And I am not clear wi ththe response of the second call. The problem might be in the companyservices where you might be creating new object rather than fetching from JPA or from its underlying platform. Another cause might be the JPA configuration.
This is going to be somewhat long, but I am simply trying to learn fancy things using spring 4.1.7
The problem that I am facing is that spring doesn't like that there is no bean declared for R and W in the Reader and Writer Controllers. Everything compiles but during runtime nothing works and I get error
Error creating bean with name 'userController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: protected carefree.coding.dao.CommerceReaderDAO carefree.coding.controllers.rest.CommerceReaderController.reader; nested exception is java.lang.IllegalArgumentException: Can not set carefree.coding.dao.CommerceReaderDAO field carefree.coding.controllers.rest.CommerceReaderController.reader to com.sun.proxy.$Proxy57
I have a simple DAO class
#MappedSuperclass
public abstract class CommerceObject
{
public enum Type
{
USER
}
#Transient
protected Type type;
#Id
#GeneratedValue
#Column(name = "id")
protected long id;
public CommerceObject(Type type)
{
this.type = type;
setId(-1l);
}
public Type getType()
{
return type;
}
public long getId()
{
return id;
}
public void setId(long id)
{
this.id = id;
}
public boolean equals(CommerceObject object)
{
return getId() == object.getId() && getType().equals(object.getType());
}
#Override
public String toString()
{
return new Gson().toJson(this);
}
public CommerceObject fromString(String json)
{
return new Gson().fromJson(json, getClass());
}
public abstract boolean update(CommerceObject object);
public abstract CommerceObject copy();
#Override
public int hashCode()
{
throw new UnsupportedOperationException("No hashing for game objects");
}
}
Which is extended by
#Entity(name = "user")
public class User extends CommerceObject
{
#Column(name = "email")
private String email;
public User()
{
super(Type.USER);
}
public String getEmail()
{
return email;
}
public void setEmail(String email)
{
this.email = email;
}
#Override
public boolean update(CommerceObject object)
{
if (object instanceof User)
{
User user = (User) object;
if (equals(user))
{
user.setEmail(email);
}
}
return false;
}
#Override
public CommerceObject copy()
{
User user = new User();
user.setEmail(getEmail());
return user;
}
}
For reading and writing things to database I have two interfaces
Reader
public interface CommerceReaderInterface<V extends CommerceObject>
{
#Transactional(readOnly = true)
List<V> get();
#Transactional(readOnly = true)
V get(long id);
}
Writer
public interface CommerceWriterInterface<V extends CommerceObject>
{
#Transactional
V add(V v);
#Transactional
V update(V v);
#Transactional
V delete(V v);
}
Basic database access class
public abstract class CommerceDAO
{
protected SessionFactory sessionFactory;
protected Class aClass;
public CommerceDAO(SessionFactory sessionFactory, Class aClass)
{
this.sessionFactory = sessionFactory;
this.aClass = aClass;
}
}
Which allows reader and writer to exist
Reader
public class CommerceReaderDAO<V extends CommerceObject> extends CommerceDAO implements CommerceReaderInterface<V>
{
public CommerceReaderDAO(SessionFactory sessionFactory, Class aClass)
{
super(sessionFactory, aClass);
}
#Override
public List<V> get()
{
ClassMetadata hibernateMetadata = sessionFactory.getClassMetadata(aClass);
if (hibernateMetadata == null)
{
return null;
}
if (hibernateMetadata instanceof AbstractEntityPersister)
{
AbstractEntityPersister persister = (AbstractEntityPersister) hibernateMetadata;
String tableName = persister.getTableName();
if (tableName != null)
{
return sessionFactory.getCurrentSession().
createQuery(tableName).list();
}
}
return null;
}
#Override
public V get(long id)
{
V object = (V) sessionFactory.getCurrentSession().
get(aClass, id);
Hibernate.initialize(object);
return object;
}
}
Writer
public class CommerceWriterDAO<V extends CommerceObject> extends CommerceDAO implements CommerceWriterInterface<V>
{
public CommerceWriterDAO(SessionFactory sessionFactory, Class aClass)
{
super(sessionFactory, aClass);
}
#Override
public V add(V v)
{
if (v != null)
{
sessionFactory.getCurrentSession().save(v);
return v;
}
return null;
}
#Override
public V update(V v)
{
if (v != null)
{
sessionFactory.getCurrentSession().update(v);
return v;
}
return null;
}
#Override
public V delete(V v)
{
if (v != null)
{
sessionFactory.getCurrentSession().delete(v);
sessionFactory.getCurrentSession().flush();
return v;
}
return null;
}
}
And for controllers I decided to do same thing and have a reader and writer interface
Reader
public interface CommerceReaderInterface
{
#RequestMapping(value = "/get",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<String> get();
#RequestMapping(value = "/get/{id}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<String> get(#PathVariable long id);
}
Writer
public interface CommerceWriterInterface<V extends CommerceObject>
{
#RequestMapping(value = "/add",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<String> add(#RequestBody V v);
#RequestMapping(value = "/update",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<String> update(#RequestBody V v);
#RequestMapping(value = "/delete",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<String> delete(#RequestBody V v);
}
And they are implemented normally
Reader
public abstract class CommerceReaderController<R extends CommerceReaderDAO<V>, V extends CommerceObject> implements CommerceReaderInterface
{
#Autowired
protected R reader;
#Override
public ResponseEntity<String> get()
{
List<V> list = reader.get();
if (list == null || list.isEmpty())
{
return ResponseUtil.notFound();
}
return ResponseUtil.ok(list);
}
#Override
public ResponseEntity<String> get(#PathVariable long id)
{
V object = reader.get(id);
if (object == null)
{
return ResponseUtil.notFound();
}
return ResponseUtil.ok(object);
}
}
Writer
public abstract class CommerceWriterController<W extends CommerceWriterDAO<V>, R extends CommerceReaderDAO<V>, V extends CommerceObject> extends CommerceReaderController<R, V> implements CommerceWriterInterface<V>
{
#Autowired
protected W writer;
#Override
public ResponseEntity<String> add(#RequestBody V v)
{
v = writer.add(v);
if (v == null)
{
return ResponseUtil.notFound();
}
return ResponseUtil.ok(v);
}
#Override
public ResponseEntity<String> update(#RequestBody V v)
{
v = writer.update(v);
if (v == null)
{
return ResponseUtil.notFound();
}
return ResponseUtil.ok(v);
}
#Override
public ResponseEntity<String> delete(#RequestBody V v)
{
v = writer.delete(v);
if (v == null)
{
return ResponseUtil.notFound();
}
return ResponseUtil.ok(v);
}
}
After all that work I thought that having a simple controller would be allowed
#Controller
#RequestMapping("/commerce/api/user")
public class UserController extends CommerceWriterController<UserWriterDAO, UserReaderDAO, User>
{
}
However as I understand in my abstract controllers I am not allowed to have such vague DAO objects. Is there any way I can circumvent this?
I'm currently writing a REST api using Jackson (2.4.0-rc3) and spring mvc (4.0.3), and I'm trying to make it secure.
In this way, I try to use JsonView to select the parts of the objects that can be serialized.
I've found the solution (which is not for me) to annotate my Controller method with the view I want. But I'd like to select on the fly the view inside the controller.
Is it possible to extend the ResponseEntity class in order to specify which JsonView I want ?
A little piece of code :
Here is the account class
public class Account {
#JsonProperty(value = "account_id")
private Long accountId;
#JsonProperty(value = "mail_address")
private String mailAddress;
#JsonProperty(value = "password")
private String password;
#JsonProperty(value = "insert_event")
private Date insertEvent;
#JsonProperty(value = "update_event")
private Date updateEvent;
#JsonProperty(value = "delete_event")
private Date deleteEvent;
#JsonView(value = PublicView.class)
public Long getAccountId() {
return accountId;
}
#JsonView(value = PublicView.class)
public void setAccountId(Long accountId) {
this.accountId = accountId;
}
#JsonView(value = OwnerView.class)
public String getMailAddress() {
return mailAddress;
}
#JsonView(value = OwnerView.class)
public void setMailAddress(String mailAddress) {
this.mailAddress = mailAddress;
}
#JsonIgnore
public String getPassword() {
return password;
}
#JsonView(value = OwnerView.class)
public void setPassword(String password) {
this.password = password;
}
#JsonView(value = AdminView.class)
public Date getInsertEvent() {
return insertEvent;
}
#JsonView(value = AdminView.class)
public void setInsertEvent(Date insertEvent) {
this.insertEvent = insertEvent;
}
#JsonView(value = AdminView.class)
public Date getUpdateEvent() {
return updateEvent;
}
#JsonView(value = AdminView.class)
public void setUpdateEvent(Date updateEvent) {
this.updateEvent = updateEvent;
}
#JsonView(value = AdminView.class)
public Date getDeleteEvent() {
return deleteEvent;
}
#JsonView(value = OwnerView.class)
public void setDeleteEvent(Date deleteEvent) {
this.deleteEvent = deleteEvent;
}
#JsonProperty(value = "name")
public abstract String getName();
}
Here is the account controller
#RestController
#RequestMapping("/account")
public class AccountCtrlImpl implements AccountCtrl {
#Autowired
private AccountSrv accountSrv;
public AccountSrv getAccountSrv() {
return accountSrv;
}
public void setAccountSrv(AccountSrv accountSrv) {
this.accountSrv = accountSrv;
}
#Override
#RequestMapping(value = "/get_by_id/{accountId}", method = RequestMethod.GET, headers = "Accept=application/json")
public ResponseEntity<Account> getById(#PathVariable(value = "accountId") Long accountId) {
try {
return new ResponseEntity<Account>(this.getAccountSrv().getById(accountId), HttpStatus.OK);
} catch (ServiceException e) {
return new ResponseEntity<Account>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
#Override
#RequestMapping(value = "/get_by_mail_address/{mail_address}", method = RequestMethod.GET, headers = "Accept=application/json")
public ResponseEntity<Account> getByMailAddress(#PathVariable(value = "mail_address") String mailAddress) {
try {
return new ResponseEntity<Account>(this.getAccountSrv().getByMailAddress(mailAddress), HttpStatus.OK);
} catch (ServiceException e) {
return new ResponseEntity<Account>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
#Override
#RequestMapping(value = "/authenticate/{mail_address}/{password}", method = RequestMethod.GET, headers = "Accept=application/json")
public ResponseEntity<Account> authenticate(#PathVariable(value = "mail_address") String mailAddress, #PathVariable(value = "password") String password) {
return new ResponseEntity<Account>(HttpStatus.NOT_IMPLEMENTED);
}
}
I really like the solution presented here to dynamically select a json view inside your controller method.
Basically, you return a MappingJacksonValue which you construct with the value you want to return. After that you call setSerializationView(viewClass) with the proper view class. In my use case, I returned a different view depending on the current user, something like this:
#RequestMapping("/foos")
public MappingJacksonValue getFoo(#AuthenticationPrincipal UserDetails userDetails ) {
MappingJacksonValue value = new MappingJacksonValue( fooService.getAll() );
if( userDetails.isAdminUser() ) {
value.setSerializationView( Views.AdminView.class );
} else {
value.setSerializationView( Views.UserView.class );
}
return value;
}
BTW: If you are using Spring Boot, you can control if properties that have no view associated are serialized or not by setting this in your application.properties:
spring.jackson.mapper.default_view_inclusion=true
I've solved my problem extending ResponseEntity like this :
public class ResponseViewEntity<T> extends ResponseEntity<ContainerViewEntity<T>> {
private Class<? extends BaseView> view;
public ResponseViewEntity(HttpStatus statusCode) {
super(statusCode);
}
public ResponseViewEntity(T body, HttpStatus statusCode) {
super(new ContainerViewEntity<T>(body, BaseView.class), statusCode);
}
public ResponseViewEntity(T body, Class<? extends BaseView> view, HttpStatus statusCode) {
super(new ContainerViewEntity<T>(body, view), statusCode);
}
}
and ContainerViewEntity encapsulate the object and the selected view
public class ContainerViewEntity<T> {
private final T object;
private final Class<? extends BaseView> view;
public ContainerViewEntity(T object, Class<? extends BaseView> view) {
this.object = object;
this.view = view;
}
public T getObject() {
return object;
}
public Class<? extends BaseView> getView() {
return view;
}
public boolean hasView() {
return this.getView() != null;
}
}
After that, we have convert only the object with the good view.
public class JsonViewMessageConverter extends MappingJackson2HttpMessageConverter {
#Override
protected void writeInternal(Object object, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
if (object instanceof ContainerViewEntity && ((ContainerViewEntity) object).hasView()) {
writeView((ContainerViewEntity) object, outputMessage);
} else {
super.writeInternal(object, outputMessage);
}
}
protected void writeView(ContainerViewEntity view, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
JsonEncoding encoding = this.getJsonEncoding(outputMessage.getHeaders().getContentType());
ObjectWriter writer = this.getWriterForView(view.getView());
JsonGenerator jsonGenerator = writer.getFactory().createGenerator(outputMessage.getBody(), encoding);
try {
writer.writeValue(jsonGenerator, view.getObject());
} catch (IOException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
}
}
private ObjectWriter getWriterForView(Class<?> view) {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
return mapper.writer().withView(view);
}
}
And to finish, I enable the converter
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="wc.handler.view.JsonViewMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
And that's it, I can select the View in the controller
#Override
#RequestMapping(value = "/get_by_id/{accountId}", method = RequestMethod.GET, headers = "Accept=application/json")
public ResponseViewEntity<Account> getById(#PathVariable(value = "accountId") Long accountId) throws ServiceException {
return new ResponseViewEntity<Account>(this.getAccountSrv().getById(accountId), PublicView.class, HttpStatus.OK);
}
FYI, Spring 4.1 already supported using #JsonView directly on #ResponseBody and ResponseEntity:
Jackson’s #JsonView is supported directly on #ResponseBody and ResponseEntity controller methods for serializing different amounts of detail for the same POJO (e.g. summary vs. detail page). This is also supported with View-based rendering by adding the serialization view type as a model attribute under a special key.
And in http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-jsonview you can find the much simpler solution:
#RestController
public class UserController {
#RequestMapping(value = "/user", method = RequestMethod.GET)
#JsonView(User.WithoutPasswordView.class)
public User getUser() {
return new User("eric", "7!jd#h23");
}
}
public class User {
public interface WithoutPasswordView {};
public interface WithPasswordView extends WithoutPasswordView {};
private String username;
private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
#JsonView(WithoutPasswordView.class)
public String getUsername() {
return this.username;
}
#JsonView(WithPasswordView.class)
public String getPassword() {
return this.password;
}
}
This works great :
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public void getZone(#PathVariable long id, #RequestParam(name = "tree", required = false) boolean withChildren, HttpServletResponse response) throws IOException {
LOGGER.debug("Get a specific zone with id {}", id);
Zone zone = zoneService.findById(id);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
if (withChildren) {
response.getWriter().append(mapper.writeValueAsString(zone));
} else {
response.getWriter().append(mapper.writerWithView(View.ZoneWithoutChildren.class).writeValueAsString(zone));
}
}