69
葉政達 Cheng Ta Yeh 12年7月19日星期四

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

Embed Size (px)

DESCRIPTION

2012 Java Developer Day

Citation preview

Page 1: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

葉政達Cheng Ta Yeh

12年7月19日星期四

Page 2: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

Integrate Spring MVC with RequireJS & Backbone.js

& Spring Data JPA

12年7月19日星期四

Page 3: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 4: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

Once upon a time...

12年7月19日星期四

Page 5: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

The Dream...

12年7月19日星期四

Page 6: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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

Page 7: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

What Am I Going To Talk About?

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

Overview

My Lessons Learned12年7月19日星期四

Page 8: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

Outlines

• Front-end development

• Integrate Backbone with Spring MVC

• Diving into Spring Data JPA

12年7月19日星期四

Page 9: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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

Page 10: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

Problems ...

• Few conventions and standards

12年7月19日星期四

Page 11: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

• 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日星期四

Page 12: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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

need efficient tools.

12年7月19日星期四

Page 13: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

Backbone.js12年7月19日星期四

Page 14: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

Who Use Backbone.jsBasecamp Mobile

12年7月19日星期四

Page 15: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

Backbone Components

DataSource

DOMUI

Router View Model / Collection

Template

EventHistory

DOM events

Model events

Render

syncFragmentrouting

Use

TalksHash tag

Dispatching routersMorninghashchange

12年7月19日星期四

Page 16: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 17: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

Backbone Model

• Data storage and business logic

• Changes on the attributes will fire `change` event

• Talks to server uses RESTful JSON

12年7月19日星期四

Page 18: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

My Real Case

12年7月19日星期四

Page 19: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

My Real Case

12年7月19日星期四

Page 20: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

Backbone Sample Code

ItemView

ItemDetailView

Use

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

ItemModel

12年7月19日星期四

Page 21: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 22: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

That’s It?

12年7月19日星期四

Page 23: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

How to build loose coupling scalable applications?

12年7月19日星期四

Page 24: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 25: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

Real life application

• Lots of Screens• Lots of Modules

• Complex dependency

12年7月19日星期四

Page 26: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

RequireJS12年7月19日星期四

Page 27: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 28: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

RequirJS AMD Loader

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

12年7月19日星期四

Page 29: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

AMD APIs

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

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

12年7月19日星期四

Page 30: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 31: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 32: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

<!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日星期四

Page 33: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 34: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 35: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 36: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

Integrates Backbone.js with Spring MVC

http://s.ctyeh.me/LGW3ug

12年7月19日星期四

Page 37: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 38: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 39: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

Spring MVC as REST Server

12年7月19日星期四

Page 40: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 41: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 42: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

Rarely Need Custom Parsers

• Automatically registers several converters.

12年7月19日星期四

Page 43: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

Controller Methods’Parameters and Return Values

The bullets between

Backbone Model & Spring MVC

12年7月19日星期四

Page 44: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 45: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 46: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 47: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 48: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 49: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 50: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

Diving into Spring Data JPA

12年7月19日星期四

Page 51: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

Layer Architecture

Backbone.js requireJS

Spring MVC

Spring Data JPA

Domain Model

HTML

12年7月19日星期四

Page 52: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

Between Spring MVC and Spring Data JPA

• Data conversion

• Design concept

12年7月19日星期四

Page 53: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 54: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 55: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 56: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 57: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

Let’s Begin With Plain JPA

12年7月19日星期四

Page 58: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

@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日星期四

Page 59: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

@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日星期四

Page 60: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 61: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

Refactoring to Spring Data JPA

12年7月19日星期四

Page 62: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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

The Repository (DAO)

12年7月19日星期四

Page 63: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 64: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

Strategy: CREATE• Split your query methods by prefix and

property names.

12年7月19日星期四

Page 65: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 66: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 67: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

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日星期四

Page 68: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

Put It All Together

Backbone.js

Spring MVC

Spring Data JPA

requireJS

12年7月19日星期四

Page 69: Integrate Spring MVC with RequireJS & Backbone.js & Spring Data JPA

The End & Thank You

http://about.me/ctyeh

12年7月19日星期四