SQL Zone is brought to you in partnership with:

Loiane Groner, Brazilian, works as a Java/ Sencha evangelist. She has 7+ years of experience in web development. She is the ESJUG (Espirito Santo Java Users Group) and CampinasJUG (Campinas Java Users Group) leader and coordinator. Loiane is passionate about technology and programming. Also author of ExtJS 4 First Look book. Loiane is a DZone MVB and is not an employee of DZone and has posted 42 posts at DZone. You can read more from them at their website. View Full User Profile

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

09.03.2010
| 70657 views |
  • submit to reddit

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! :)

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 Class – 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. :)

Happy coding!

From http://loianegroner.com/2010/09/extjs-spring-mvc-3-and-hibernate-3-5-crud-datagrid-example/

Published at DZone with permission of Loiane Groner, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Alexandre Strze... replied on Thu, 2010/09/23 - 2:11pm

Thanks you very much for your tutorial ! Its exactly what i was looking for. Pefect mix between extjs, springMVC, annotations configuration and hibernate.

Juan Zevallos replied on Wed, 2010/09/29 - 10:53am

Muchas Gracias Loiane por el aporte en este tutorial, es de muy buena ayuda, te pasaste..

Manjuka Soysa replied on Sun, 2010/10/03 - 1:05am

This is very useful. Would be interesting to see how CRUD operations are performed when the classes have relationships (one to one, one to many, etc) with other classes. I like the idea of manipulating a Javascript object model on the browser, but synchronizing it with a Hibernate object model on the server is still non-trivial (except for flat object models as in this example).

Talha Kabakuş replied on Tue, 2010/10/05 - 4:07pm

Thanks for these useful shares.. Especially this one gives a HUGE ease to developers. I'm using exactly same developing environment with you, this is great. Thanks again..

Faisal Noor replied on Sun, 2011/03/06 - 1:23pm in response to: Manjuka Soysa

Agree with you. There must also be another CRUD example that involves classes that have one to one or one to many relationships. I hope this comes ahead from loaine.

Tahir Zafar replied on Thu, 2011/03/17 - 7:53am

This code example takes care of essential bricks required for hello world crud operation using spring-hibernate ...

I really appreciate your work ... Thanks a lot

Andreas Hahn replied on Tue, 2011/05/31 - 2:40pm

just as a side note - there are other ways to achieve the same with spring and hibernate without ExtJs in a more flexible way and less verbose manner ... just visit http://shept.org 

King Sam replied on Fri, 2012/02/24 - 9:48am

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.

Suhashini Dharm... replied on Thu, 2012/07/26 - 4:10am

When i run this project in tomcat 6 it is working fine and add/edit/delete all are working fine. But when i run into jboss 4.2 it is working fine and displaying records. When i want add/edit/delete record it is giving below exception. Please help me.

 

 

org.springframework.web.bind.annotation.support.HandlerMethodInvocationException: Failed to invoke handler method [public java.util.Map com.loiane.web.ContactController.create(java.lang.Object) throws java.lang.Exception]; nested exception is java.lang.IllegalStateException: No parameter name specified for argument of type [java.lang.Object], and no parameter name information found in class file either.
	org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:179)
	org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:421)
	org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:409)
	org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:771)
	org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:716)
	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
	org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:560)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:710)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
	org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
</pre></p><p><b>root cause</b> <pre>java.lang.IllegalStateException: No parameter name specified for argument of type [java.lang.Object], and no parameter name information found in class file either.
	org.springframework.web.bind.annotation.support.HandlerMethodInvoker.getRequiredParameterName(HandlerMethodInvoker.java:711)
	org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveRequestParam(HandlerMethodInvoker.java:470)
	org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveHandlerArguments(HandlerMethodInvoker.java:330)
	org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:169)
	org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:421)
	org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:409)
	org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:771)
	org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:716)
	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
	org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:560)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:710)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
	org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)

Carlos Crosetti replied on Sat, 2012/08/11 - 12:15pm

Hi Folks, please check my blog to find install and configuration steps required before you cam run Loiane's DataGrid Example, regards, Carlos

http://carlos-crosetti.blogspot.com.ar/2012/08/revisiting-extjs-spring-mvc-3-and.html

Asanka Bandara replied on Thu, 2013/05/09 - 11:36am

Thank you very much Loiane,. i vs looking for this kind of example. all dat i need and working perfectly. thanks Carlos for the configuration steps.

John Lean replied on Thu, 2013/05/30 - 1:43am

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

Oi Loiane,

This CRUD example is one of the best that I came across so far. Many thank for creating and sharing the code. I tried many examples before and stuck with most of them for one reason or other. Your example by surprise used simple Java project rather than the maven. I’m new to maven and little hate it when I wouldn’t know why there are hell of .jars getting downloaded as they call transitive dependencies.

This example was clean and I could make them run within minutes. How brilliant you are J

Amo e muito obrigado Loiane,

John,

Raghu Jonn replied on Tue, 2014/07/15 - 1:55am

great example. Thank you

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.