Spring Roo: Saving/Retreving BLOB object in Spring Roo

After reading the excellent article titled Saving/Retreving BLOB object in Spring MVC/Hibernate immediately came to my mind:
  1. How would it be the process of recreating the same example but using Spring Roo.
  2. What would it be the similarities/differences in the projects source code.
The article assumes the Reader is a Developer with some familiarity with Spring Roo. For introductory information about Spring Roo vist http://www.ibm.com/developerworks/java/library/os-springroo1/index.html?ca=drs-.

Project setup

Spring Roo brings alternatives for setting up a project: a) Entering project using a script file or b) Creating the database first and reverse engineering it using Roo DBRE commands. For this article I am using option a), via an script file. Option b) is specially convenient for projects with an database already in place. I’ll vist Roo DBRE in a coming article in the near future. Script File
project --topLevelPackage org.pragmatikroo.roodocman persistence setup --provider HIBERNATE --database MYSQL --databaseName roodocman --userName <username> --password <password> entity --class ~.domain.Document field string --fieldName name --notNull --sizeMax 30 field string --fieldName description --sizeMax 500 field string --fieldName filename --notNull //field blob --fieldName content --notNull //Type not supported by Roo field string --fieldName contentType --notNull field numbert --fieldName size - controller all --package ~.web perform clean perform eclipse
Code language: HTML, XML (xml)
Notice content field is of type blob. This field would have to be entered manually into the entity class. You can use the IDE of your choice. I use STS. Either you use STS or the native Roo shell, the project would need to be synced by Roo after the change. More on this later.

Generate Entity Class

package org.pragmatikroo.roodocman; @RooJavaBean @RooToString @RooEntity public class Document { @NotNull @Size(max = 30) private java.lang.String name; @NotNull @Size(max = 500) private java.lang.String description; private java.lang.String filename; @NotNull @Lob @Basic(fetch = FetchType.LAZY) private byte[] content; private java.lang.String contentType; private java.lang.Long size; @Transient @Size(max = 100) private String url ; }
Code language: Java (java)
Please review the seminal article for more information about the example entity fields. All highlighted code is manually added to the original file created by Roo. Why I added field size and url directly into the entity class?. I could have entered referred fields using the shell too. Right?. I did it in purpose to mentioned that Spring Roo is a round-trip tool. There are two more fields not shown in the entity class. They are the id and version fields, Roo automatically handle them using an ITD. Details about round-trip capabilities and how Roo uses ITDs, please visit Spring Roo website at http://www.springsource.org/roo. One more thing about the blob field. DBMS have different type of blobs. Verify that your project database is using the right one for your purposes. As mention before: always make sure that Roo synced your latest changes. That would save you a lot of confusion. It is a good time to deploy the application and verify that everything is working find to this point.

Required Source code changes

Well, to this point Spring Roo has done a lot for us. Properly setup the project. Created a Maven pom.xml with all required dependencies -well, almost; more on this below-. Generate all the code necessary for having a working web app. If this not enough, Roo is there in the background monitoring the project against any possible change. With exception of the blob field -content- all the other fields are ready to go. Roo took care of them. In order to save/retrieve data from the blob field, we need to modify code in the client and in the server side for allowing file uploads.

Server Side Changes

Basically we need code that allows us to save/retrieve documents that include a blob field. The documents are entered using a web form. Typically Roo by default, generates client and server side code for having CRUD operations for every entity. I am not allowing updates in this version of the application. So, I disabled it, by setting the update=false in the @RooWebScaffold annotation. Please make sure next code is included in the DocumentController project file.
@RooWebScaffold(path = "documents", formBackingObject = Document.class, update=false) @InitBinder protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws ServletException { binder.registerCustomEditor(byte[].class, newByteArrayMultipartFileEditor()); } @RequestMapping(value="savedoc", method = RequestMethod.POST) public String createdoc(@Valid Document document, BindingResult result, Model model, @RequestParam("content") MultipartFile content, HttpServletRequest request) { document.setContentType(content.getContentType()); document.setFilename(content.getOriginalFilename()); document.setSize(content.getSize()); log.debug("Document: "); log.debug("Name: "+content.getOriginalFilename()); log.debug("Description: "+document.getDescription()); log.debug("File: " +content.getName()); log.debug("Type: "+content.getContentType()); log.debug("Size: "+content.getSize()); if (result.hasErrors()) { model.addAttribute("document", document); return "documents/create"; } document.persist(); return "redirect:/documents?page=1&size=10" + encodeUrlPathSegment(document.getId().toString(), request); } @RequestMapping(value = "/{id}", method = RequestMethod.GET) public String show(@PathVariable("id") Long id, Model model) { Document doc = Document.findDocument(id); doc.setU("/documents/showdoc/"+id); model.addAttribute("document", Document.findDocument(id)); model.addAttribute("itemId", id); return "documents/show"; } @RequestMapping(value = "/showdoc/{id}", method = RequestMethod.GET) public String showdoc( @PathVariable("id") Long id, HttpServletResponse response, Model model) { Document doc = Document.findDocument(id); try { response.setHeader("Content-Disposition", "inline;filename=\"" +doc.getFilename()+ "\""); OutputStream out = response.getOutputStream(); response.setContentType(doc.getContentType()); IOUtils.copy( new ByteArrayInputStream(doc.getContent()),out); out.flush(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return null; }
Code language: Java (java)
The code above allows CRUD operations with all the document fields including the blob. Very-very important: all this source code must be placed in the DocumentControler.java file to avoid been removed by Roo when the project is synced for changes.

Client Side Changes

On client side, I created a create custom form. Please replace the content of the create.jspx, with: Create.jpsx Form
<form:multi id="fc_org_pragmatikroo_roodocman_domain_Document" modelAttribute="document" path="/documents/savedoc" render="${empty dependencies}" z="O7jiRGyhKQOUnBT8yJ9W4XU6VrQ="> <field:input field="name" id="c_org_pragmatikroo_roodocman_domain_Document_name" max="30" required="true" z="K3YncHn7F+HtBX/zgCZMYM6VO7k="/> <field:textarea field="description" id="c_org_pragmatikroo_roodocman_domain_Document_description" required="true" z="/RLpJWAf9zVjHtRapWIFve8JB8Y="/> <field:input field="filename" id="c_org_pragmatikroo_roodocman_domain_Document_filename" render="false" z="u4EclOEjQnID4g34VPv9zu9+qPo="/> <field:file field="content" id="c_org_pragmatikroo_roodocman_domain_Document_content" required="true" z="GZHlfc+o4h7EBA/SZ8/yXMVenOw="/> <field:input field="contentType" id="c_org_pragmatikroo_roodocman_domain_Document_contentType" render="false" z="jtq5/DHhBTgNImjac/d4AIfpCzA="/> <field:input field="size" id="c_org_pragmatikroo_roodocman_domain_Document_size" render="false" validationMessageCode="field_invalid_integer" z="ONDmhU5Eg5pw1j8LgTz9sXjOm6E="/> <field:textarea field="url" id="c_org_pragmatikroo_roodocman_domain_Document_url" render="false" z="bQ3FUVGL01nfWselK7WDPUD65Rw="/> </form:multi>
Code language: HTML, XML (xml)
As you can see, the form depends on two custom tagx files -multi.tagx and file.tagx- not included in the Spring Roo tag library. These two tags handle the blob field input.

Create Form

Show.jpx Form
<page:show id="ps_org_pragmatikroo_roodocman_domain_Document" object="${document}" path="/documents" update="false" z="J8X2O2T06x6jYb3WAyaNiTPGVZ0="> <field:display field="name" id="s_org_pragmatikroo_roodocman_domain_Document_name" object="${document}" z="VZEPJgXqYkqaHpDDPTcYr6DAjsM="/> <field:display field="description" id="s_org_pragmatikroo_roodocman_domain_Document_description" object="${document}" z="CeXQhSyEDeLn1Iu2hblanNbVc+A="/> <field:display field="filename" id="s_org_pragmatikroo_roodocman_domain_Document_filename" object="${document}" z="2HT3+zT1+1ft5kN4KhvXFWT9QnM="/> <field:display field="contentType" id="s_org_pragmatikroo_roodocman_domain_Document_contentType" object="${document}" z="FgCNUQ2KCygzKpUbP06Nxyjyfd8="/> <field:display field="size" id="s_org_pragmatikroo_roodocman_domain_Document_size" object="${document}" z="WdQ4wob2LThzMpucODZRa275TBc="/> <field:frame field="url" id="s_org_pragmatikroo_roodocman_domain_Document_url" object="${document}" render="true" z="user-managed"/> </page:show>
Code language: HTML, XML (xml)
This form depends on a custom tag frame.tagx. Basically is a iframe html for rendering images.

Show Form

List Form

Little issue with a missed dependency: When you deploy the app -after the file upload changes- you will find a missing dependence error.
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.0.1</version> </dependency>
Code language: HTML, XML (xml)
This is it!.

Download Source

Click here to download source code (ZIP, 94kb).

Conclusion

Well, the Reader has been exposed to two pretty close but at the same time different ways of implementing a particular web application project. Both are using the same high quality open source code Java-based development stack: Spring 3.X. I believe both ways are good and valid. There is a significant difference in Spring Roo approach though. Roo advocates for removing the “unnecessary abstractions” from web development. So, you won’t find Daos or Services classes in a typical Roo project. This is totally fine with me. However, it is a subject still in revision by the community at large. Not to mention that nothing stops you for implementing them if you need/want them. If you allow me one final point, I like better Roo approach because it gives me more time for thinking and figuring our how to solve the problem.

View Comments

  • Thanks for sharing!

    Might merge this into my project(s) and if I might find useful stuff to be added, I'll be back. :)

    grts

  • Jochen, publish the link when possible... It will be great to see it working on other projects.
    If you want to see it working now. Visit the WorldAlmanac on the Cities Menu.

    Cheers jD

  • Thank you! I was looking for it for a while and I think your post is the only place to get this information! Thanks for sharing!

  • Great tutorial. Thanks! One question though. Will the browser cache the image given that it doesn't change?

    • Hi JD ,

      I saw u r message but i want retrive image from database and display in jsp using form,DAO,DO i am tring to display but some thing is wrong .i got the imgdata but how can i display in jsp ,i saw google they r using but they r also cant display img so please send me some source code to me plz help me?

      ThX&regards
      ----------------
      faroo

  • Hi Jd,
    the above application display the imgage from database how can i display in my jsp like above page i am also using same fields and same data but retriving data success but how can i display

  • Good tutorial. When i try it , I have error with multi-tagx et file-tagx file , I don't know the content of this file .

    Thanks for your help

    • @ jac have you been able to s0lve this issue i am having internal error because of the multi tag for form too

Recent Posts

  • Java

Java URL Encoder/Decoder Example

Java URL Encoder/Decoder Example - In this tutorial we will see how to URL encode/decode…

4 years ago
  • General

How to Show Multiple Examples in OpenAPI Spec

Show Multiple Examples in OpenAPI - OpenAPI (aka Swagger) Specifications has become a defecto standard…

4 years ago
  • General

How to Run Local WordPress using Docker

Local WordPress using Docker - Running a local WordPress development environment is crucial for testing…

4 years ago
  • Java

Create and Validate JWT Token in Java using JJWT

1. JWT Token Overview JSON Web Token (JWT) is an open standard defines a compact…

4 years ago
  • Spring Boot

Spring Boot GraphQL Subscription Realtime API

GraphQL Subscription provides a great way of building real-time API. In this tutorial we will…

4 years ago
  • Spring Boot

Spring Boot DynamoDB Integration Test using Testcontainers

1. Overview Spring Boot Webflux DynamoDB Integration tests - In this tutorial we will see…

4 years ago