ExtJS, Spring MVC 3 and Hibernate 3.5: CRUD DataGrid Example

September 2, 2010 | By

extjs spring crud grid ExtJS, Spring MVC 3 and Hibernate 3.5: CRUD DataGrid Example

This tutorial will walk through how to implement a CRUD (Create, Read, Update, Delete) DataGrid using ExtJS, Spring MVC 3 and Hibernate 3.5.

What do we usually want to do with data?

  • Create (Insert)
  • Read / Retrieve (Select)
  • Update (Update)
  • Delete / Destroy (Delete)

Until ExtJS 3.0 we only could READ data using a dataGrid. If you wanted to update, insert or delete, you had to do some code to make these actions work. Now ExtJS 3.0 (and newest versions) introduces the ext.data.writer, and you do not need all that work to have a CRUD Grid.

So… What do I need to add in my code to make all these things working together?

In this example, I’m going to use JSON as data format exchange between the browser and the server.

ExtJS Code

First, you need an Ext.data.JsonWriter:

 // The new DataWriter component.
    var writer = new Ext.data.JsonWriter({
        encode: true,
        writeAllFields: true
    });

Where writeAllFields identifies that we want to write all the fields from the record to the database. If you have a fancy ORM then maybe you can set this to false. In this example, I’m using Hibernate, and we have saveOrUpate method – in this case, we need all fields to updated the object in database, so we have to ser writeAllFields to true. This is my record type declaration:

    var Contact = Ext.data.Record.create([
	{name: 'id'},
    {
        name: 'name',
        type: 'string'
    }, {
        name: 'phone',
        type: 'string'
    }, {
        name: 'email',
        type: 'string'
    }]);

Now you need to setup a proxy like this one:

    var proxy = new Ext.data.HttpProxy({
        api: {
            read : 'contact/view.action',
            create : 'contact/create.action',
            update: 'contact/update.action',
            destroy: 'contact/delete.action'
        }
    });

FYI, this is how my reader looks like:

    var reader = new Ext.data.JsonReader({
        totalProperty: 'total',
        successProperty: 'success',
        idProperty: 'id',
        root: 'data',
        messageProperty: 'message'  // <-- New "messageProperty" meta-data
    },
    Contact);

The Writer and the proxy (and the reader) can be hooked to the store like this:

 // Typical Store collecting the Proxy, Reader and Writer together.
    var store = new Ext.data.Store({
        id: 'user',
        proxy: proxy,
        reader: reader,
        writer: writer,  // <-- plug a DataWriter into the store just as you would a Reader
        autoSave: false // <-- false would delay executing create, update, destroy requests until specifically told to do so with some [save] buton.
    });

Where autosave identifies if you want the data in automatically saving mode (you do not need a save button, the app will send the actions automatically to the server). In this case, I implemented a save button, so every record with new or updated value will have a red mark on the cell left up corner). When the user alters a value in the grid, then a “save” event occurs (if autosave is true). Upon the “save” event the grid determines which cells has been altered. When we have an altered cell, then the corresponding record is sent to the server with the ‘root’ from the reader around it. E.g if we read with root “data”, then we send back with root “data”. We can have several records being sent at once. When updating to the server (e.g multiple edits). And to make you life even easier, let’s use the RowEditor plugin, so you can easily edit or add new records. All you have to do is to add the css and js files in your page:

<!-- Row Editor plugin css -->
	<link rel="stylesheet" type="text/css" href="/extjs-crud-grid/ext-3.1.1/examples/ux/css/rowEditorCustom.css" />
	<link rel="stylesheet" type="text/css" href="/extjs-crud-grid/ext-3.1.1/examples/shared/examples.css" />
	<link rel="stylesheet" type="text/css" href="/extjs-crud-grid/ext-3.1.1/examples/ux/css/RowEditor.css" />

<!-- Row Editor plugin js -->
	<script src="/extjs-crud-grid/ext-3.1.1/examples/ux/RowEditor.js"></script>

Add the plugin on you grid declaration:

    var editor = new Ext.ux.grid.RowEditor({
        saveText: 'Update'
    });

    // create grid
    var grid = new Ext.grid.GridPanel({
        store: store,
        columns: [
            {header: "NAME",
             width: 170,
             sortable: true,
             dataIndex: 'name',
             editor: {
                xtype: 'textfield',
                allowBlank: false
            }},
            {header: "PHONE #",
             width: 150,
             sortable: true,
             dataIndex: 'phone',
             editor: {
                 xtype: 'textfield',
                 allowBlank: false
            }},
            {header: "EMAIL",
             width: 150,
             sortable: true,
             dataIndex: 'email',
             editor: {
                xtype: 'textfield',
                allowBlank: false
            }})}
        ],
        plugins: [editor],
        title: 'My Contacts',
        height: 300,
        width:610,
		frame:true,
		tbar: [{
            iconCls: 'icon-user-add',
            text: 'Add Contact',
            handler: function(){
                var e = new Contact({
                    name: 'New Guy',
                    phone: '(000) 000-0000',
                    email: 'new@loianetest.com'
                });
                editor.stopEditing();
                store.insert(0, e);
                grid.getView().refresh();
                grid.getSelectionModel().selectRow(0);
                editor.startEditing(0);
            }
        },{
            iconCls: 'icon-user-delete',
            text: 'Remove Contact',
            handler: function(){
                editor.stopEditing();
                var s = grid.getSelectionModel().getSelections();
                for(var i = 0, r; r = s[i]; i++){
                    store.remove(r);
                }
            }
        },{
            iconCls: 'icon-user-save',
            text: 'Save All Modifications',
            handler: function(){
                store.save();
            }
        }]
    });

Java code

Finally, you need some server side code.

Controller:

package com.loiane.web;

@Controller
public class ContactController  {

	private ContactService contactService;

	@RequestMapping(value="/contact/view.action")
	public @ResponseBody Map<String,? extends Object> view() throws Exception {

		try{

			List<Contact> contacts = contactService.getContactList();

			return getMap(contacts);

		} catch (Exception e) {

			return getModelMapError("Error retrieving Contacts from database.");
		}
	}

	@RequestMapping(value="/contact/create.action")
	public @ResponseBody Map<String,? extends Object> create(@RequestParam Object data) throws Exception {

		try{

			List<Contact> contacts = contactService.create(data);

			return getMap(contacts);

		} catch (Exception e) {

			return getModelMapError("Error trying to create contact.");
		}
	}

	@RequestMapping(value="/contact/update.action")
	public @ResponseBody Map<String,? extends Object> update(@RequestParam Object data) throws Exception {
		try{

			List<Contact> contacts = contactService.update(data);

			return getMap(contacts);

		} catch (Exception e) {

			return getModelMapError("Error trying to update contact.");
		}
	}

	@RequestMapping(value="/contact/delete.action")
	public @ResponseBody Map<String,? extends Object> delete(@RequestParam Object data) throws Exception {

		try{

			contactService.delete(data);

			Map<String,Object> modelMap = new HashMap<String,Object>(3);
			modelMap.put("success", true);

			return modelMap;

		} catch (Exception e) {

			return getModelMapError("Error trying to delete contact.");
		}
	}

	private Map<String,Object> getMap(List<Contact> contacts){

		Map<String,Object> modelMap = new HashMap<String,Object>(3);
		modelMap.put("total", contacts.size());
		modelMap.put("data", contacts);
		modelMap.put("success", true);

		return modelMap;
	}

	private Map<String,Object> getModelMapError(String msg){

		Map<String,Object> modelMap = new HashMap<String,Object>(2);
		modelMap.put("message", msg);
		modelMap.put("success", false);

		return modelMap;
	}

	@Autowired
	public void setContactService(ContactService contactService) {
		this.contactService = contactService;
	}

}

Some observations:

In Spring 3, we can get the objects from requests directly in the method parameters using @RequestParam. I don’t know why, but it did not work with ExtJS. I had to leave as an Object and to the JSON-Object parser myself. That is why I’m using a Util class – to parser the object from request into my POJO class. If you know how I can replace Object parameter from controller methods, please, leave a comment, because I’d really like to know that! icon smile ExtJS, Spring MVC 3 and Hibernate 3.5: CRUD DataGrid Example

Service Class:

package com.loiane.service;

@Service
public class ContactService {

	private ContactDAO contactDAO;
	private Util util;

	@Transactional(readOnly=true)
	public List<Contact> getContactList(){

		return contactDAO.getContacts();
	}

	@Transactional
	public List<Contact> create(Object data){

        List<Contact> newContacts = new ArrayList<Contact>();

		List<Contact> list = util.getContactsFromRequest(data);

		for (Contact contact : list){
			newContacts.add(contactDAO.saveContact(contact));
		}

		return newContacts;
	}

	@Transactional
	public List<Contact> update(Object data){

		List<Contact> returnContacts = new ArrayList<Contact>();

		List<Contact> updatedContacts = util.getContactsFromRequest(data);

		for (Contact contact : updatedContacts){
			returnContacts.add(contactDAO.saveContact(contact));
		}

		return returnContacts;
	}

	@Transactional
	public void delete(Object data){

		//it is an array - have to cast to array object
		if (data.toString().indexOf('[') > -1){

			List<Integer> deleteContacts = util.getListIdFromJSON(data);

			for (Integer id : deleteContacts){
				contactDAO.deleteContact(id);
			}

		} else { //it is only one object - cast to object/bean

			Integer id = Integer.parseInt(data.toString());

			contactDAO.deleteContact(id);
		}
	}

	@Autowired
	public void setContactDAO(ContactDAO contactDAO) {
		this.contactDAO = contactDAO;
	}

	@Autowired
	public void setUtil(Util util) {
		this.util = util;
	}
}

Contact Calss – POJO:

package com.loiane.model;

@JsonAutoDetect
@Entity
@Table(name="CONTACT")
public class Contact {

	private int id;
	private String name;
	private String phone;
	private String email;

	@Id
	@GeneratedValue
	@Column(name="CONTACT_ID")
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	@Column(name="CONTACT_NAME", nullable=false)
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Column(name="CONTACT_PHONE", nullable=false)
	public String getPhone() {
		return phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}

	@Column(name="CONTACT_EMAIL", nullable=false)
	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}
}

DAO Class:

package com.loiane.dao;

@Repository
public class ContactDAO implements IContactDAO{

	private HibernateTemplate hibernateTemplate;

	@Autowired
	public void setSessionFactory(SessionFactory sessionFactory) {
		hibernateTemplate = new HibernateTemplate(sessionFactory);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<Contact> getContacts() {
		return hibernateTemplate.find("from Contact");
	}

	@Override
	public void deleteContact(int id){
		Object record = hibernateTemplate.load(Contact.class, id);
		hibernateTemplate.delete(record);
	}

	@Override
	public Contact saveContact(Contact contact){
		hibernateTemplate.saveOrUpdate(contact);
		return contact;
	}
}

Util Class:

package com.loiane.util;

@Component
public class Util {

	public List<Contact> getContactsFromRequest(Object data){

		List<Contact> list;

		//it is an array - have to cast to array object
		if (data.toString().indexOf('[') > -1){

			list = getListContactsFromJSON(data);

		} else { //it is only one object - cast to object/bean

			Contact contact = getContactFromJSON(data);

			list = new ArrayList<Contact>();
			list.add(contact);
		}

		return list;
	}

	private Contact getContactFromJSON(Object data){
		JSONObject jsonObject = JSONObject.fromObject(data);
		Contact newContact = (Contact) JSONObject.toBean(jsonObject, Contact.class);
		return newContact;
	}
)
	private List<Contact> getListContactsFromJSON(Object data){
		JSONArray jsonArray = JSONArray.fromObject(data);
		List<Contact> newContacts = (List<Contact>) JSONArray.toCollection(jsonArray,Contact.class);
		return newContacts;
	}

	public List<Integer> getListIdFromJSON(Object data){
		JSONArray jsonArray = JSONArray.fromObject(data);
		List<Integer> idContacts = (List<Integer>) JSONArray.toCollection(jsonArray,Integer.class);
		return idContacts;
	}
}

If you want to see all the code (complete project will all the necessary files to run this app), download it from my GitHub repository: http://github.com/loiane/extjs-crud-grid-spring-hibernate

This was a requested post. I’ve got a lot of comments from my previous CRUD Grid example and some emails. I made some adjustments to current code, but the idea is still the same. I hope I was able answer all the questions. icon smile ExtJS, Spring MVC 3 and Hibernate 3.5: CRUD DataGrid Example

Happy coding!

Filed in: ExtJS, Hibernate, Spring | Tags: , , , , , , , , ,

Comments (54)

Links to this Post

  1. ExtJS and Spring MVC Framework: CRUD DataGrid Example | Loiane Groner | September 2, 2010
  2. extjstutorial.org | May 4, 2011
  3. JavaPins | January 8, 2012
  1. Aggelos Karalias (mehiel)

    Hi Loiane,

    this is a very good and clean post.

    I think it could be even better if you tried it in a more RESTful manner. And it is very close (code-wise) to what you have already. Just a few annotations to change and setup the store properly.

    To get the object into the controller without using your Util class you need the @RequestBody annotation. Then if you have Jackson configured as a message converter and client set header Content-Type as ‘application/json’ to the request, Spring does the job automatically.

    Check this post http://blog.springsource.com/2010/01/25/ajax-simplifications-in-spring-3-0/

    Regards,
    Aggelos

    • Loiane

      @ Aggelos
      I tried to implement RESTful grid once, but because of that issue, I did not work. I’m going to try again.
      Thanks for the answer!

  2. AndyC

    To map the JSON that you submit to a method parameter on your @Controller, you need to be using @RequestBody, not @RequestParam, and you need the Jackson library in your app. That will cause the body of the request to be marshalled via a MappingJacksonHttpMessageConverter.

    See this blog entry from SpringSource which contains an example.

    http://blog.springsource.com/2010/01/25/ajax-simplifications-in-spring-3-0/

  3. Abhaya

    I cannot download the Example.
    Please help!!!

  4. Artem

    @Abhaya

    If you already installed Git, just run this command:

    git clone git://github.com/loiane/extjs-crud-grid-spring-hibernate.git

  5. Artem

    @Loiane

    Hi, very nice blog with many useful information. Thanx for tutorials!

  6. Hi Loiane, gr8 tutorial, although there are some things which did not understand like where from which part of the code in crud-grid.js does the code create.action gets called? i.e after store.save() how does it reach create.action?
    Secondly the modelMap returned from the controller where does it go to in the crud-grid.js
    Thanks in advance & Regards
    Mudassar Hakim

  7. Juanzeva

    Hola Loiane, yo recien comienzo con Extjs y me parece estupendo, gracias por el aporte de este tutorial, espero poder hacerte algunas consultas mas adelante, gracias.

  8. jjzd2w

    Uno de los mejores ejemplos que he visto te agradezco es lo que buscaba .
    Gracias !

  9. mrs

    I’d like to pass some data from extjs to controllers view() method. When I change view method definition to :

    public @ResponseBody Map view(@RequestParam Object data)

    extjs shows error. Is it possible to pass some parameters (and read them) to the view method ?

    • Loiane

      @mrs
      yes, but you have to use @requestbody instead of @requestparam.
      I still did not find out why we get error with requestparam.

  10. Aggelos Karalias (Mehiel)

    @Loiane
    I think I had the same problem with @RequestBody when I tried to use the restful api of both spring and extjs’ store.

    Ext’s JsonWriter by default posts data to the request’s body like this:
    data={property: value, property2: etc}

    Where ‘data’ is what you set as root in the extjs’ configuration. But Jackson (and Spring) try to unmarshal the object from the body in plain json like this:
    {property: value, property2: etc}

    So (I didn’t find any other way and) I created a PlainJsonWriter extending JsonWriter’s render method to send only the json… and everyone is happy now!

    Regards,
    Aggelos

  11. great post. i also wanna share a spoon-fed tutorial on Hibernate integration to spring mvc 3.0 for newbies like me

    http://www.adobocode.com/spring/adding-crud-capability-to-spring-mvc

    hope this helps.

  12. khanti

    Thank for tutorials……

  13. elmasse

    @Aggelos, @Loiane

    use Writer and just set root to ” that will plain the requestBody to {..} instead of data={…}.

    Excellent article! Really clear! I agreed with a RESTfull example would help even more! Keep the good work!

  14. Graham

    This is awesome!

    I am having trouble with hibernate for implementing a crud grid where the table has foreign keys using the same frameworks as the example. Any advice?

  15. janaka wanigatunga

    nice article.
    @elmasse: Can you pls explain me more how to do this with @Requestbody annotation?

    Thanks
    Janaka

  16. Mehmet

    İ ‘ve some problems that are firstly i dont connect my extjs project with mysql.And i dont know how to do ıt.
    second,i wanna use java codes( by hibernate) sql and extjs.i connect java-mysql but i dont link extjs how to do ıt???

    thx alot..

  17. Manisekharan Chellamuthu

    Great work Loiane. I have a question.
    I modified writeAllFields to false. So that it should update modified field. I changed contact name. But it throws following error, “Caused by: org.hibernate.PropertyValueException: not-null property references a null or transient value: com.loiane.model.Contact.email”. Why does it check email?
    Please let me know how to fix it.

  18. Deiveehan

    how are u writing the Ext JS code in eclipse. I found it difficult as I have to remember all the attributes, there is not auto help (like when you type the Ctrl+space normally it used to give the list of attributes or methods in eclipse). I dont find this for ext js (plugin for eclipse). 
    I tried with Sptket plugin for eclipse but it doesnt solve the problem, i still have to remember all the attributes and methods, 
    is anyone successful in this ??

    • Loiane

      I use Sptket plugin for eclipse. And I keep the documentation opened in a browser as well.

  19. Annie

    Very helpful! Thanks for taking the time to write and publish this.

  20. Hades

    Hi Loiane,
    this example is very userful to me, i want this code to study,but i can’t find it on internet. Can you send it to my email? taoo_hades@hotmail.com.
    thank you !

    • Loiane

      Hi Hades, you can download it from the github repository. On the right up corner, there is a download button.

    • Loiane

      Hi Hades, you can click on the download button and download the code. Thanks!

  21. HI LoINA YOUR BLOG IS SO SUPER PLS UPDATE THE EXAMPLE FOR FUTURE

  22. hi superb your blog pleas Update it for intrest

  23. RoX

    Thanks Loiane…..very great job ;)

  24. paul

    Hello Loiane, Great post there. I tried downloading the code to run on JBOSS 5.1GA and cant seem to get it to start i get all sorts of errors related to hibernate.properties and mapping errors although i changed the Tomcat7 dependencies to JBOSS 5. any ideas what i need to do to make it run ?

    thanks.

  25. panls

    Noob here.Very nice job but i have a problem.When i run the project on server i get the error  message ERROR org.hibernate.util.JDBCExceptionReporter – Access denied for user ‘root’@’localhost’ (using password: YES). The problem is that i execute the sql file but i cant make any changes to the database from the grid.So i think is a Tomcat problem.Any idea?

  26. You opened my noobish fuckin eyes.Had to open db-config.xml in a text editor and type my root password.Thnx Loiane Greetings from Greece.

  27. CarlosL

    Hi Loiane!!! Thank you very much!!!
    But, there are some things which did not understand like @mudassar…  
    I would greatly appreciate someone answer this:

    “which part of the code in crud-grid.js does the code create.action gets called? i.e after store.save() how does it reach create.action?Secondly the modelMap returned from the controller where does it go to in the crud-grid.js”

    Thanks, really!!

  28. nick

    Very good example! Do you have any plans in doing this same example using Extjs 4?
    I am new to Extjs and I am looking into using version 4. Thank you.

  29. Chals

    super post!!!
    Hello @Loiane, thank you very much, really!But one doubt me, I’ve been watching the web page says @AndyC and don’t understand … ie, just change the @requestparam for @requestbody? and how to remove the part where you call util.class?
    if someone has done it, I would like to see how classes are modified.I’m new and i’m lost.
    Thank you very much if someone has it!!!!

  30. Chals

    Super post!!!

    Hello @Loiane, thank you very much, really!But one doubt me, I’ve been watching the web page says @AndyC and I don’t understand … ie, just change the @requestparam for @requestbody? and how to remove the part where you call util.class?
    If someone has done it, I would like to see how classes are modified.I’m new and i’m lost.

    Thank you very much if someone has it!!!!

  31. Chals

    Hii @loiane!!! Many thanks, really! :D

  32. jjzd2w

    Loiane check the SyntaxHighlighter dont wroking good ..

    Nice blog !

  33. marckba

    Hi @Loiane!! One question in the Ext JS 4 example tha you’ve post without using the Utils.class, why do you use the ContactWrapper class? When you create, delete or edit a contact could you use “@RequestBody Contact data” instead of “@RequestBody ContactWrapper data” ?? If I change it the program give me an error. 
    Anyway I would like to know why is necessary to use the ContactWrapper class in order to get the parameter without errors. Thanks in advance!!

    Marckba

  34. marckba

    Yeah!! I’ve seen this example. 
    What I would like to know if there is any way to instead of using the ContactWrapper use the Contact object directly without using the wrapper.. In your example the code for create action will be like this:

    public @ResponseBody Map create(@RequestBody Contact data) throws Exception {
    try{

    List contacts = contactService.create(data);
    return ExtJSReturn.mapOK(contacts);
    } catch (Exception e) {; return ExtJSReturn.mapError(“Error trying to create contact.”); } }
    The fact is that this does not work because jackson library doesn’t know who to parse it. Do you know which would be the solution? Or why I can’t use the Contact Bean directly? Because In the reader and writer of the store I’m indicating that the model is Contact.
    Thanks in advance again ;)!!

  35. lakes

    Nothing was showing and no error in the logs when I built and deployed the project

  36. Thank you every much,that’s what I need!

  37. Alex

    Cool. But have a question. In project you does not have Hibernate mapping file… All work without him?

    • Loiane

      Hi Alex,
      I am using annotations in the project, so you do not need to use xml mapping file. :)
      Thanks