Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

Preview:

DESCRIPTION

2012 Java Developer Day

Citation preview

葉政達Cheng Ta Yeh

12年7月19日星期四

Integrate Spring MVC with RequireJS & Backbone.js

& Spring Data JPA

12年7月19日星期四

Who Am I• 葉政達 Cheng-Ta Yeh

• Co-founder of Humus Technology (優沃科技)

• Founder of GOGOSamba (Android app & Calorie Management Service)

• Speaker of TWJUG 2012/5

• Speaker of Google Developer Day 2008

• Speaker of Java One Tokyo 2005

• 12 years Java related experiences

12年7月19日星期四

Once upon a time...

12年7月19日星期四

The Dream...

12年7月19日星期四

Start lean, get bigger later12年7月19日星期四

What Am I Going To Talk About?

Building a Scalable Single-Page app from front-end to backend.

Overview

My Lessons Learned12年7月19日星期四

Outlines

• Front-end development

• Integrate Backbone with Spring MVC

• Diving into Spring Data JPA

12年7月19日星期四

Front-end development12年7月19日星期四

Problems ...

• Few conventions and standards

12年7月19日星期四

• jQuery is cool, it has plenty of selectors and callbacks, but ...

http://mysistersjar.wordpress.com/2008/04/02/wanted-moving-boxes/

We have to tie data to the DOM heavily

We have to deal with callbacks’ callbacks’...,

to manipulate DOM, animations, events......

Problems ...

12年7月19日星期四

Problems ...• Between server-side and client-side, we

need efficient tools.

12年7月19日星期四

Backbone.js12年7月19日星期四

Who Use Backbone.jsBasecamp Mobile

12年7月19日星期四

Backbone Components

DataSource

DOMUI

Router View Model / Collection

Template

EventHistory

DOM events

Model events

Render

syncFragmentrouting

Use

TalksHash tag

Dispatching routersMorninghashchange

12年7月19日星期四

Backbone View

• Owns a DOM element

• Talks to its model or collection

• Observe model events (change,...)

• Handle user’s inputs (DOM events)

12年7月19日星期四

Backbone Model

• Data storage and business logic

• Changes on the attributes will fire `change` event

• Talks to server uses RESTful JSON

12年7月19日星期四

My Real Case

12年7月19日星期四

My Real Case

12年7月19日星期四

Backbone Sample Code

ItemView

ItemDetailView

Use

window.ItemModel = Backbone.Model.extend();

ItemModel

12年7月19日星期四

Backbone Sample Code

ItemViewwindow.ItemDetailView = Backbone.View.extend({

events: {

"click #item_input": "doEdit"

},

initialize: function() { this.model.bind('change', this.setTitle, this);},setTitle: function() { $(this.el).html(this.model.get('name'));},

//....

//....

});

window.ItemModel = Backbone.Model.extend();

ItemModel

window.itemModel.save( {name: this.$('#input').val()});

ItemDetailView

window.ItemView = Backbone.View.extend({initialize: function() { this.model.bind('change', this.setItemName, this);},setItemName: function() { $(this.el).html(this.model.get('name'));},

});

change name change event

12年7月19日星期四

That’s It?

12年7月19日星期四

How to build loose coupling scalable applications?

12年7月19日星期四

Apply “Module Pattern”, but ...

var testModule = (function () {

var _counter = 0; //private variable

function privateMethod(){ //..... }

return {

incrementCounter: function () { return __counter++; },

resetCounter: function () { console.log( "counter value prior to reset: " + _counter ); _counter = 0; } };

})();

12年7月19日星期四

Real life application

• Lots of Screens• Lots of Modules

• Complex dependency

12年7月19日星期四

RequireJS12年7月19日星期四

Standards for Modules

• CommonJS Modules 1.1.1

• Targeted at server-side environments

• AMD (Asynchronous Module Definition)

• A format for writing modular javascript in the browser

CommonJS

12年7月19日星期四

RequirJS AMD Loader

Other AMD loaders:• curl• lsjs• Dojo 1.7 and MooTools 2.0 each have their own

12年7月19日星期四

AMD APIs

• Facilitating module definition • define (module_id?, dependencies?, factory);

• Handling dependency async loading • require (dependencies?, callback);

12年7月19日星期四

Defining Backbone’s Classes into Modules

define(function () {  return new Backbone.Model.extend({  // ...  });});

define([../models/itemModel],function (itemModel) {  return new Backbone.View.extend({ model: itemModel; render: function(){ this.model.fetch(); //... }, // ...  });});

./models/itemModel.js

./views/itemView.js

12年7月19日星期四

Your App’s Entry Pointrequire.config({

// .....})

require(['jquery','app'], function ($,app) { app.initial();});

define([../views/itemView],function (itemView) {

  return {initial: function(){

// Do initialization.....

// Render first App’s screen.

itemView.render();

},

  };});

./main.js

./app.js

1. Set up requireJS configuration.2. Start to run your app.

1. As a top level container. 2. Initialize your app.4. Render your first screen.

./views/itemView.js

LoadLoad

Execute

12年7月19日星期四

<!DOCTYPE html><html> <head> <title>jQuery+RequireJS Sample Page</title> <script data-main="scripts/main" src="scripts/require-jquery.js"></script> </head> <body> <h1>Single Page Application Sample Page</h1>

<div id=”app_body”> Your DOM elements will be inserted dynamically. </div> </body></html>

Set up your HTML Page

12年7月19日星期四

Using requireJS Optimizer

• Optimizing all the CSS and JS files in your project

> r.js -o app.build.js

({ appDir: "../", baseUrl: "scripts", dir: "../../appdirectory-build", modules: [ { name: "main" } ]})

app.build.js

12年7月19日星期四

Tips & Lessons Learned

• Prevent Memory Leaks• All models, collections, views implement destructors.

• Create an abstraction layer for those destructors.

Backbone.View.prototype.close = function () { console.log("Closing a View...”);

if (this.beforeClose) { this.beforeClose(); } this.remove(); this.unbind(); };

12年7月19日星期四

Tips & Lessons Learned

• Package Pattern• File structure

• src/ui/widgets.js• src/ui/widgets/ooxx.js

define(['./widgets/selectors', './widgets/slider', './widgets/button'],        function(selectors, slider, button) {    return {        ToolSelector: selectors.ToolSelector,        OptionSelector: selectors.OptionSelector,        Slider: slider.Slider,        Button: button.Button    };});

src/ui/widgets.js

< Just like Java’s package

12年7月19日星期四

Integrates Backbone.js with Spring MVC

http://s.ctyeh.me/LGW3ug

12年7月19日星期四

Backbone Model & RESTful JSON Inteface

* POST or PUT base on the id value of Model.

itemModel.save();

itemModel.fetch();

itemModel.save();

itemModel.clear();

C

R

U

D

• POST /items

• GET /items[/{id}]

• PUT /items/{id}

• DELETE /items/{id}

* Global ajax error handler is useful

12年7月19日星期四

Define RESTful Interface• Nouns: URI, addressed resources

• /sites

• /sites/{id}

• /sites/{id}/items

• /sites/{id}/items/{todoId}.

The REST Triangle

Find your resources BEFORE designing Backbone Models and Spring MVC Controller methods.

12年7月19日星期四

Spring MVC as REST Server

12年7月19日星期四

Receiving Values in the URL Path• The RESTful way

• @PathVariable in Controller methods

@RequestMapping(value="/sites/{siteId}/item/{itemId}",method= {RequestMethod.PUT})public @ResponseBody UIJentoItem updateModule(@PathVariable long siteId,@PathVariable Long itemId,@Valid @RequestBody UIJentoItem item) { UIJentoItem uiObject = new UIJentoItem(); //......

return uiObject;}

12年7月19日星期四

Sending HTTP Response Codes to Clients

• @ResponseStatus

• @ExecptionHandler.

public class AbstractController {

@ExceptionHandler(UserNotSigninException.class) @ResponseStatus(UNAUTHORIZED) public @ResponseBody UIMessage handleUserNotSigninException(UserNotSigninException e, HttpServletResponse response) { return UIMessage.fail("err.data.usernotsignin"); } @ExceptionHandler(DataNotFoundException.class) @ResponseStatus(NOT_FOUND) public @ResponseBody UIMessage handleDataNotFoundException(DataNotFoundException e, HttpServletResponse response) { return UIMessage.fail("err.data.notfound"); } @ExceptionHandler(Exception.class) @ResponseStatus(INTERNAL_SERVER_ERROR) public @ResponseBody UIMessage handleException(Exception e, HttpServletResponse response) { return UIMessage.fail("err.internal"); }

}

AbstractController

ControllerA

ControllerB

ControllerC

extends

// HTTP 401

// HTTP 404

// HTTP 505

12年7月19日星期四

Rarely Need Custom Parsers

• Automatically registers several converters.

12年7月19日星期四

Controller Methods’Parameters and Return Values

The bullets between

Backbone Model & Spring MVC

12年7月19日星期四

Parameters and Return Values

• Domain Entity

@RequestMapping(value="/sites/{id}/items",method= {RequestMethod.GET}) @ResponseStatus(OK) public @ResponseBody List<Item> getItems(@PathVariable long id) {

//....

return itemList; }

@RequestMapping(value="/sites/{id}/items",method= {RequestMethod.POST}) @ResponseStatus(OK) public @ResponseBody String createItem( @PathVariable long id,@RequestBody Item item) {

//....

}

In

Out

Controller

Controller

12年7月19日星期四

Parameters and Return Values

• DTO (Data Transfer Object)

@RequestMapping(value="/sites/{id}/items",method= {RequestMethod.GET}) @ResponseStatus(OK) public @ResponseBody List<DTOItem> getItems(@PathVariable long id) {

//....

return itemList; } @RequestMapping(value="/sites/{id}/items",method= {RequestMethod.POST})

@ResponseStatus(OK) public @ResponseBody String createItem( @PathVariable long id,@RequestBody DTOItem item) {

//....

}In

Out

Controller

Controller

12年7月19日星期四

Parameters and Return Values

• JSONObject

import net.sf.json.JSONObject;

@RequestMapping(value="/sites/{id}/items",method= {RequestMethod.GET}) @ResponseStatus(OK) public @ResponseBody List<JSONObject> getItems(@PathVariable long id) {

//....

return itemList; }

@RequestMapping(value="/sites/{id}/items",method= {RequestMethod.POST}) @ResponseStatus(OK) public @ResponseBody String createItem( @PathVariable long id, @RequestBody JSONObject itemJSON) {

//....

}In

Out

Controller

Controller

12年7月19日星期四

Parameters and Return Values

• Domain Entity

• Pros: No extra effort for creation and maintenance & readable

• Cons: Might waste extra bandwidth for transferring

• DTO

• Pros: readable

• Cons: Extra efforts for maintenance, transferring from domain object to DTO.

• JSONObject

• Pros: No extra effort for creation and maintenance

• Cons: Not readable, Unit testing is mandatory, transferring from domain entity to JSONObject!

12年7月19日星期四

Unit Test for Spring MVC Controller

• mockito - Mocking framework• http://code.google.com/p/mockito/

• Spring MVC Test Support• https://github.com/SpringSource/spring-test-mvc

12年7月19日星期四

Unit Test for Spring MVC Controller

public class ItemControllerTest {

private static MockMvc mockMvc; private UserSerivce userService; private ItemSerivce itemService;

@Before public void setUp() { userService = mock(UserService.class); itemService = mock(ItemService.class);

mockMvc = standaloneSetup(new ItemController()).build();

// userService.getCurrentUser() will be called in ItemController for URI "/sites/{id}/items" when(userService.getCurrentUser()).thenReturn(new User()); Item item = new Item();

item.setName("This is item name"); when(itemService.getItem(any(Long.class))).thenReturn(item);

}

@Test public void getItemsBySiteId() throws Exception { mockMvc.perform(get("/sites/1/items")) .andExpect(status().isOk()) .andExpect(content().type("application/json;charset=UTF-8")) .andExpect(jsonPath("$.[0].id").value(1)) .andExpect(jsonPath("$.[0].itemName").value("This is item name")) ;

Your URI

tell mocks when some calls happened return something

Create a mock service

Setup mockMVC for target Controller

ItemController

ItemService UserService

useuse

12年7月19日星期四

Diving into Spring Data JPA

12年7月19日星期四

Layer Architecture

Backbone.js requireJS

Spring MVC

Spring Data JPA

Domain Model

HTML

12年7月19日星期四

Between Spring MVC and Spring Data JPA

• Data conversion

• Design concept

12年7月19日星期四

Between Spring MVC and Spring Data JPA

• Data conversion• Using Factory Method in Domain entity or separate Factory classes.

DTO JSONObject

Domain Object

Spring MVC

Spring Data JPA

12年7月19日星期四

Between Spring MVC and Spring Data JPA

• Data conversion

• Using Factory Method in Domain entity or separate Factory classes.

DTO JSONObject

Domain Object

@Entitypublic class Item implements Serializable { private String name;

//.....

public JSONObject toJSON(){ JSONObject object = new JSONObject();

//Converting Item to JSONObject. object.put("name", this.name);

return object;}

public static Item fromJSON(JSONObject json){

//Converting JSONObject to Item.

return item; }}

12年7月19日星期四

Between Spring MVC and Spring Data JPA

• Design concept• Domain driven design in Domain Layer

• Entity

• Service

• Value Object

• Aggregate

• Repository Spring Data borrows this concept.

12年7月19日星期四

Spring Data Overview• Abstracts away basic data management concepts

• Support for RDB, NoSQL: Graph, Key-Value and Map-Reduce types.

• Current implementations• JPA/JDBC

• Hadoop

• GemFire

• REST

• Redis/Riak

• MongoDB

• Neo4j

• Blob (AWS S3, Rackspace, Azure,...)

• Plan for HBase and Cassandra

12年7月19日星期四

Let’s Begin With Plain JPA

12年7月19日星期四

@Entitypublic class Item { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id;

@ManyToOne private Site site;

    // … methods omitted}

@Entitypublic class Site {

@Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id;

// … methods omitted}

The Domain Entities

Site Item1*

12年7月19日星期四

@Repositorypublic class TodoDAO {"

  @PersistenceContext  private EntityManager em;   @Override  @Transactional  public Item save(Item item) {     if (item.getId() == null) {      em.persist(item);      return item;    } else {      return em.merge(item);    }  }   @Override  public List<Item> findBySiteId(Long siteId) {     TypedQuery query = em.createQuery("select i from Item a where i.site.id = ?1", Item.class);    query.setParameter(1, siteId);     return query.getResultList();  }

}

The DAO

12年7月19日星期四

The Domain Service@Service@Transactional(readOnly = true)class ItemListServiceImpl implements ItemListService {   @Autowired  private ItemDAO itemDAO;   @Override  @Transactional  public Item save(Item item) {      return itemDAO.save(item);  }

  @Override  public List<Item> findItems(Long siteId) {      return itemDAO.findBySiteId(siteId);  }

 }

12年7月19日星期四

Refactoring to Spring Data JPA

12年7月19日星期四

public interface ItemRepository extends JpaRepository<Item, Long> {   List<Item> findBySite(Site site);}

The Repository (DAO)

12年7月19日星期四

The Service@Service@Transactional(readOnly = true)class ItemListServiceImpl implements ItemListService {   @Autowired  private ItemRepository repository;

   @Override  @Transactional  public Item save(Item item) {      return repository.save(item);  }

  @Override  public List<Item> findItems(Site site) {      return repository.findBySite(site);  } }

12年7月19日星期四

Strategy: CREATE• Split your query methods by prefix and

property names.

12年7月19日星期四

Strategy: USE_DECLARED_QUERY• @Query

public interface UserRepository extends JpaRepository<User, Long> {

@Query("select u from User u where u.emailAddress = ?1") User findByEmailAddress(String emailAddress);}

From Spring Data JPA 1.1.0, native SQL is allowed to be executed. Just set nativeQuery flag to true.

But, not support pagination and dynamic sort

12年7月19日星期四

Strategy: USE_DECLARED_QUERY• @Modify

@Modifying@Query("update User u set u.firstname = ?1 where u.lastname = ?2")int setFixedFirstnameFor(String firstname, String lastname);

@Modifying@Query("update User u set u.firstname = :firstName where u.lastname = :lastName")int setFixedFirstnameFor(@Param("firstName") String firstname, @Param("lastName") String lastname);

12年7月19日星期四

Configurations in Spring 3.1<!-- Activate Spring Data JPA repository support --> <jpa:repositories base-package="com.humus.domain.repo" />

<!-- Declare a JPA entityManagerFactory --> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <property name="showSql" value="true" /> </bean> </property> <property name="jpaProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.H2Dialect</prop> <prop key="hibernate.hbm2ddl.auto">create</prop> </props>

</property> <property name="packagesToScan"> <list> <value>com.humus.domain.entity</value> </list> </property>

</bean>

No persistence.xml any more~

12年7月19日星期四

Put It All Together

Backbone.js

Spring MVC

Spring Data JPA

requireJS

12年7月19日星期四

The End & Thank You

http://about.me/ctyeh

12年7月19日星期四

Recommended