BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル (アップしません)Grails + EJBドメインモデルをステップごとに説明

(アップしません)Grails + EJBドメインモデルをステップごとに説明

ブックマーク

Ruby on Railsはソフトウェアエンジニアの世界では引き続き興味の的ですが、企業の現場ではどちらかというと、いまだ納得していません。なぜでしょう。「スクリプティング言語の上に構築されたフレームワークが、我が社のエンタープライズアプリケーションにどうやって適合するのだろうか?!」といぶかしんでいるのです。Ruby on Railsの場合、よく議論されるのは、エンタープライズサービス(例えば、分散型トランザクション、メッセージングなど)に対するサポートの欠如です。多数の企業にとって、こうしたサービスのないプラットフォームは選択肢にすら入らないのです。

Grails(サイト・英語)はこうした懸念に対応することを目的とし、企業によるRAD(rapid application development)を実現させます。Groovy(サイト・英語)上に構築されたGrailsは、Javaとのシームレスな統合を提供します。業務に必要なエンタープライズサービスに直接アクセスを提供すると同時に、既存のツールキットに強力かつダイナミックな言語構成要素を追加します。

Grailsのエンタープライズ・インテグレーション能力の素晴らしい一例を挙げると、EJB3の既存Entity Beanを土台にしたWebアプリケーションを迅速かつ簡単に構築できます。しかし、それで終わりではありません。GrailsはEntity Beanの大幅な機能増強を図りますが、EJBソースコードをまったく変更することなく、完全にダイナミックに行うのです。GORM(Grails Object Relational Mapping)はHibernate 3上に構築されており(最終的にはJava Persistence APIをサポート予定)、MOP(Groovy's Meta Object Protocol)を使い、さもなければ静的なEntity Beanにあらゆる種類の便利なダイナミックメソッド(source)を追加します。そして、こうしたメソッドの利用元はGrailsとGroovyに制限されているわけではありません。自分のJavaコードでこうしたメソッドを呼び出せるのです! JEE/EJB3の企業レベルの全機能と、RADによるWebアプリケーション開発の利点すべてが突然手に入るのです!

では、EJB3 Entity Beanを土台にしたGrailsアプリケーション構築に何が必要かをみてみましょう。以下のステップでは、新規Grailsアプリケーションを作成し、そのアプリケーションにEntity Beanをインポート、Entity Bean用のデフォルトWebインターフェースを迅速構築するためのスカッフォールディングを生成、その後にGrailsがEntity Beanに追加した便利なダイナミックメソッドのいくつかを実際に使ってみます。

まず、EJB3アプリケーションが必要です(開始時点でEJB3アプリケーションを持っていることがGrailsの必要条件というわけではありません。しかし、この記事で一般に想定しているのは、既存のEJB3プロジェクトへのRAD Web開発を組み入れに関心のある読者です)。EJB3のEntity Beanで企業の従業員(EmployeeBean)(source)と、その従業員に割り当てたコンピュータ(ComputerBean)(source)をモデル化しているとします。(途中で問題が起こったら資料(source)の項を参照してください。このGrailsアプリケーションの全ソースコード、当該Entity Beanを使用しているサンプルJEEアプリケーション、その他の役立つアーチファクトをご覧頂けます。)

<画が挿入>

下の2つの表はここで使用するEntity Beanをサポートしています。(この例ではMySQL 5.0(サイト・英語)を使います。このスクリプト(source)を使って、2つの表でポピュレートしたejb3exampleという名称の新規データベースを作成できます。)

<画が挿入>

それでは、会社の働きバチ諸君と皆が使っているハードウェアの雑然とした一覧をちょっと見てみましょう。

<画が挿入>

このデータと相互作用し、既存のコードベースと一体化するWebインターフェースの構築にはどれほどの時間がかかるのでしょう。それほど長い時間はかからないでしょうが、これまでは、かなりの労力が必要であると肝に銘じてきたのではないでしょうか。Grailsでは、そんな必要はないのです。

ステップ1 - Grailsのインストール

EJB3はJDK 5(サイト・英語)に依存しているので、JDK 5がインストールされており、JAVA_HOME環境変数がJDK 5インストレーションを指摘するようにしておいた方がよいでしょう。

以下の簡単なステップ(source)に従ってGrailsをシステムにインストールしましょう。(この記事ではGrails 0.2.1を使っています。原稿執筆時点で最新の安定したリリースです。)(もし*nix systemをお使いで、インストレーションで問題が生じたら、こちらのスレッド(source)をチェックしてください。)

ステップ2 - 「初めまして、Grails!」

  1. コマンドプロンプトから、Grailsアプリケーションを作成したいディレクトリに進んでください。そうしたら、grails create-appを入力します。アプリケーション名を尋ねられたら、ejb3_grailsと入力してください。
    jMac:~/dev jason$ grails create-app ... create-app:     [input] Enter application name: ejb3_grails ... BUILD SUCCESSFUL Total time: 4 seconds 
  2. お使いの環境で申し分なく動くか確かめるために、アプリケーションを起動してみましょう。アプリケーション用に新しく作成したディレクトリに移動し、grails run-appを入力してアプリケーションを起動します。enter grails run-app to start the application.
    jMac:~/dev jason$ cd ejb3_grails jMac:~/dev/ejb3_grails jason$ grails run-app ... run-app:watch-context: 
  3. これでアプリケーションはリクエスト待ちの状態になっています。ブラウザで http://localhost:8080/ejb3_grails/ (英語)を開けば、このフレンドリーな歓迎メッセージが表示されるはずです。
    <画が挿入>

Step 3 - Import the Entity Beans

  1. Grails comes pre-packaged with HSQLDB, but since we're using MySQL, we have a few quick steps to tell Grails how to talk to our database. First, download the Java MySQL driver from http://www.mysql.com/products/connector/j/. I opted for the current production-ready version which, as of this writing, is 3.1.13.

  2. Open the zip file and extract the mysql-connector-java-3.1.13-bin.jar file into the lib directory of your Grails application - in our case, that's ejb3_grails/lib. (Please note that the exact name of the JAR file may differ based on the version of the driver you downloaded.)

  3. Now we're ready to tell Grails where to find our database. Open ApplicationDataSource.groovy in your editor of choice, and modify it to match the settings below. You'll find this file in ejb3_grails/grails-app/conf/. (Note that you'll need to change the username and password to the appropriate values for your MySQL account.)

    import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration

    class ApplicationDataSource {
    def configClass = GrailsAnnotationConfiguration.class
    boolean pooling = true
    //String dbCreate = "create-drop" // one of 'create', 'create-drop','update'
    String url = "jdbc:mysql://localhost/ejb3example"
    String driverClassName = "com.mysql.jdbc.Driver"
    String username = "ejb3example"
    String password = "ejb3example"
    }

    In addition to specifying the connectivity settings, we also need to define the configClass member to allow Grails to support the annotations used in our entity beans.

    Lastly, we want to comment out the dbCreate setting. This setting allows you to have Grails update your database schema at runtime to synchronize it with your domain classes. While this is a powerful option, we simply don't need it for this example. By commenting out this property, we're instructing Grails to leave the schema as is.

  4. Next, we need to copy the entity beans - EmployeeBean and ComputerBean - into our Grails project. Grails looks for Java classes in the src/java directory. Be sure to create the full directory structure matching the package name for these classes.

    jMac:~/dev/ejb3_grails/src/java/com/jasonrudolph/ejb3example/entity jason$ ls
    ComputerBean.java EmployeeBean.java

    Long-term, you'll want to make sure that these files stay in sync with the official copy of these classes (in the source tree of your JEE project). To do so, you could easily instruct your build script to copy these files from the JEE project into the Grails project at build time.

  5. Grails allows us to work with any Java classes, but we want Grails to give special treatment to these particular Java classes (i.e., our entity beans). We want Grails to recognize these classes as our domain classes and to provide all the ORM and dynamic method goodness that comes with a Grails domain class. To do so, we'll register these classes by adding the following hibernate.cfg.xml file to the hibernate directory of our application.
                                              

Step 4 - Generate the Scaffolding

Now we're ready for the real time savings to begin. There is just no escaping most of the things we've done so far. (No matter how smart your framework is, you'll always have to tell it where to find your database.) However, building a good and functional starting point for your user interface is no longer a manual task.

  1. Make sure you're in the project's root directory - in our case, it's ejb3_grails. Then, enter grails generate-controller. When asked for the domain class name, enter the fully-qualified class name for our first entity bean - com.jasonrudolph.ejb3example.entity.EmployeeBean.
    jMac:~/dev/ejb3_grails jason$ grails generate-controller ... input-domain-class:     [input] Enter domain class name: com.jasonrudolph.ejb3example.entity.EmployeeBean ... [java] Generating controller for domain class [com.jasonrudolph.ejb3example.entity.EmployeeBean]      [java] Controller generated at ./grails- app/controllers/EmployeeBeanController.groovy  BUILD SUCCESSFUL Total time: 10 seconds 
  2. Now that we have the controller, let's generate the corresponding views. Enter grails generate-views. When asked for the domain class name, enter com.jasonrudolph.ejb3example.entity.EmployeeBean.
    jMac:~/dev/ejb3_grails jason$ grails generate-views ... input-domain-class:     [input] Enter domain class name: com.jasonrudolph.ejb3example.entity.EmployeeBean ... [java] Generating views for domain class [com.jasonrudolph.ejb3example.entity.EmployeeBean]      [java] Generating list view for domain class [com.jasonrudolph.ejb3example.entity.EmployeeBean]      [java] list view generated at /Users/jason/dev/ejb3_grails/./grails- app/views/employeeBean/list.gsp      [java] Generating show view for domain class [com.jasonrudolph.ejb3example.entity.EmployeeBean]      [java] Show view generated at /Users/jason/dev/ejb3_grails/./grails- app/views/employeeBean/show.gsp      [java] Generating edit view for domain class [com.jasonrudolph.ejb3example.entity.EmployeeBean]      [java] Edit view generated at /Users/jason/dev/ejb3_grails/./grails- app/views/employeeBean/edit.gsp      [java] Generating create view for domain class [com.jasonrudolph.ejb3example.entity.EmployeeBean]      [java] Create view generated at /Users/jason/dev/ejb3_grails/./grails- app/views/employeeBean/create.gsp  BUILD SUCCESSFUL Total time: 11 seconds 
  3. Repeat this process to generate the controller and views for the other entity bean (i.e., com.jasonrudolph.ejb3example.entity.ComputerBean).

  4. Now let's run our application and see just how much we can get for such little effort. Enter grails run-app, and we're ready to go. Open your browser to http://localhost:8080/ejb3_grails/.

  5. OK. We have our two controllers. And, as is suggested by the text here, we'll eventually want to replace this page with our own custom landing page. For now, let's move on to the EmployeeBeanController.

We see most of the information we'd want here. Obviously the Computers column needs some work, but otherwise this page can probably be ready to go with just a few cosmetic changes.

Take some time to look around the application and see what we have so far. Try to create a new employee, edit an employee, and then (if you're feeling particularly powerful) terminate one of those poor souls. Try out similar features for managing computers. Along the way, make note of the things you'd want to change as well as the things we need to change. At the same time, be sure to consider those things that already meet your needs. Those are the features you got for free!

...

Done? OK. Here's my take on the list of things we need, and then we can talk about those nice-to-have items later.

  • Relationship Management - The scaffolding clearly tries in this area, but it just doesn't meet our needs. Luckily, this is pretty easy to remedy. (Heck. Even the Ruby on Rails scaffolding doesn't give you relationship management for free!)
  • Validation - It's just missing. Plain and simple. But, we can quickly add that as well.

That's it. If we can implement proper relationship management and validation, we will have a fully-functioning web app to manage our entity beans. After that, the rest is just gravy. (Though we'll be sure to sample some of that tasty gravy before we're finished.)

Step 5 - Add Relationship Management

What do we expect from the application in regards to relationship management? Well, I'd say we should reasonably be able to ...

  • View all computers assigned to an employee.
  • View the details for a single computer (including its assignment status).
  • Create, update, and delete a computer (including its assignment status).

Ready? Let's get started.

  1. It doesn't really make sense to list computers on the Employees page, so let's remove that column. Open grails-app/views/employeeBean/list.gsp, and remove that column. Now just refresh the browser, and verify the updated page.

  2. Next, click the Show link to view the details for an employee.

    At the very least, we need to clean up the text listed for each computer. But perhaps we don't want to see the computers directly on this page at all. Instead of showing the computers on this page, let's include a link to this employee's list of computers.

    Open the template for this page (i.e., grails-app/views/employeeBean/show.gsp), and remove the row that currently displays the employee's computers. Then, add the following row to link to a separate page showing the computers for this employee.

          	   Show Computers        

    We're using the Grails tag library to help us out here. The link tag will generate a link to the ComputerBeanController and invoke a new action that we need to define called showComputersByEmployee. That link will also include the ID for the employee in question.

    Let's refresh the browser and see our changes.

    OK. We have our link. Now we need to define the new action for that link. Open grails-app/controllers/ComputerBeanController.groovy in your editor. Because the new action will search for computers by employee, we first need to add an import statement for the EmployeeBean class.

    import com.jasonrudolph.ejb3example.entity.EmployeeBean

    Then we just add the new action.

    def showComputersByEmployee = {
    render(view:'list', model:[ computerBeanList:
    ComputerBean.findAllByEmployeeBean(EmployeeBean.get(params.id)) ])
    }

    This action gives us a good look at just how much you can say in Groovy (and Grails) in a few concise lines. In those few lines, we're telling Grails that any calls to showComputersByEmployee should ...

    • Get the employee ID from the request using params.id
    • Get the EmployeeBean for that employee ID using the EmployeeBean#get method
    • Find all computers assigned to that employee using the ComputerBean#findAllByEmployeeBean method
    • Put the results in an object named computerBeanList
    • render the view, using the computerBeanList object as the model, in a template named list

    Remember defining the get method in EmployeeBean and the findAllByEmployeeBean method in ComputerBean? No? You're right. Those methods are just a small sampling of the many dynamic methods that Grails provides for your domain classes. We'll explore these items more later. In the meantime, we're ready to click on the Show Computers link.

    We're getting close. We still need to change the text in the Employee Bean column. We certainly want something with a bit more human meaning here.

    Also, we might prefer to identify the employee to which these computers belong. We're currently reusing the list template (generated as part of the scaffolding for the ComputerBean), and that template is designed primarily to list all computers. However, we could always define a separate template for displaying this subset of computers or perhaps make the list template more dynamic in order to support both scenarios. For now, we'll tuck this feature away as nice-to-have but not essential.

    That should complete all the changes we need for managing employees. Now we just need to clean up the computer management features.

  3. Since we're here, let's clean up the computer list template. Instead of the current text in the Employee Bean column, let's change it to display the employee's network ID. Open grails-app/views/computerBean/list.gsp. Find the Groovy scriptlet that renders the text in the Employee Bean column ...
    ${it.employeeBean}

    ... and change it to render the employee's network ID.

    ${it.employeeBean.networkId}

    Refresh the browser and let's see how it looks. (The Employee column seemed a little awkward between the Brand and the Model columns, so I switched things around a bit. Feel free to do the same.)

    The list template is complete. On the show template.

  4. Click the Show link, and let's assess what we need to change.

    It looks like we just need to change the text for the employee link. We're almost pros at this by now. So, open grails-app/views/computerBean/show.gsp, and find the scriptlet that renders the current text for the link.

    ${computerBean?.employeeBean}

    Just like we did for the list template, change the code to display the employee's network ID.

    ${computerBean?.employeeBean.networkId}

    Let's refresh the browser again and verify our changes. (Again, the Employee row seemed a little awkward between the Brand and the Model rows, so you're welcome to rearrange the rows as you see fit.)

  5. Let's move on the edit functionality.

    We're starting to see a theme here. We need to change the select box to provide a list of network IDs. Find the tag in grails-app/views/computerBean/edit.gsp, and tweak it like so.

       

    By adding the optionValue parameter to the tag, the text in the select box has taken on a more meaningful form.

    The view is correct now, but the update functionality requires a bit of effort outside of the view as well. We also need to enhance the controller (i.e., ComputerBeanController.groovy). If the user changes the employee associated with a computer, we need to make sure we persist those association changes properly. In other words, we need to disassociate the computer from the current employee and assign it to the new employee. The enhanced update method requires just a few additional lines of code.

    def update = {
    def computerBean = ComputerBean.get( params.id )
    if(computerBean) {
    if (computerBean.employeeBean) {
    computerBean.employeeBean.computers.remove(computerBean)
    }

    computerBean.properties = params

    def employeeBean = EmployeeBean.get(params.employeeId)
    employeeBean.computers.add(computerBean)
    computerBean.employeeBean = employeeBean

    if(computerBean.save()) {
    redirect(action:show,id:computerBean.id)
    }
    else {
    render(view:'edit',model:[computerBean:computerBean])
    }
    }
    else {
    flash.message = "ComputerBean not found with id ${params.id}"
    redirect(action:edit,id:params.id)
    }
    }

    Save your changes, and we're ready to try it out. Let's change the brand for this computer - it's a Lenovo now - and let's reassign it to John Doe.

  6. Of course, we also need to be able to add new computers to our inventory. So, let's click on New ComputerBean.

    After our last change to the Edit page, we're well-qualified to clean up this page. We need to make the same adjustments to the select box. Open grails-app/views/computerBean/create.gsp, adjust the tag, and refresh your browser.

       

    As we saw for the edit functionality, we need to make a slight enhancement to the controller. When we create a new computer, we need to make sure we assign it to an employee before we save it. Edit ComputerBeanController.groovy to include this updated save method.

    def save = {
    def computerBean = new ComputerBean()
    computerBean.properties = params

    def employeeBean = EmployeeBean.get(params.employeeId)
    employeeBean.computers.add(computerBean)
    computerBean.employeeBean = employeeBean

    if(computerBean.save()) {
    redirect(action:show,id:computerBean.id)
    }
    else {
    render(view:'create',model:[computerBean:computerBean])
    }
    }

    Back in the browser, we're ready to create a new computer. Fill in the empty fields and create away.

    Jane now has a hot new laptop.

  7. And the last piece we need is the delete functionality. We don't need any template changes this time. We just need to add one line to the controller. When we delete a computer in ComputerBeanController.groovy, we also need to remove the computer's association to the employee. The fourth line below will take care of that.

    def delete = {
    def computerBean = ComputerBean.get( params.id )

    if(computerBean) {
    computerBean.employeeBean.getComputers().remove(computerBean)
    computerBean.delete()
    flash.message = "ComputerBean ${params.id} deleted."
    redirect(action:list)
    }
    else {
    flash.message = "ComputerBean not found with id ${params.id}"
    redirect(action:list)
    }
    }

    It looks like Jane's new MacBook has been recalled. Shall we delete it?

    And there we have it! In just a few quick steps, we now have a functioning web application built on top of our entity beans. It can still use a little polish, of course. At the very least, it's completely sufficient as a working prototype. Better yet, it's a prototype that is fully capable of growing into the finished product.

Step 6 - Define Validation Rules

One thing we certainly need in the near term is some validation logic. Fortunately, Grails offers a very convenient declarative validation mechanism. For each of our entity beans, we simply need to add a Groovy script to define the appropriate constraints. By convention, Grails looks for a script with the same name as your domain class but ending with the word "Constraints" (e.g., EmployeeBeanConstraints.groovy).

So, let's define the constraints for each of our entity beans. At a minimum we need our constraints to match our data model. We'll also add in some basic business validation. For example, the database requires that network IDs not exceed eight characters in length, but we'll also enforce our business rule that a network ID must be no fewer than six characters in length.

Grails looks for the constraints declarations in the same directory as our domain class. For the EmployeeBean class, we need to create a file named EmployeeBeanConstraints.groovy and place it in ejb3_grails/src/java/com/jasonrudolph/ejb3example/entity/. The file should include the following rules:

constraints = {
networkId(length:6..8,blank:false,unique:true)
firstName(maxLength:20,blank:false)
lastName(maxLength:20,blank:false)
startDate(nullable:false)
}

Next, we'll define the constraints for the ComputerBean class. The file goes in the same directory, and we need to name it ComputerBeanConstraints.groovy.

constraints = {
serialNumber(maxLength:20,blank:false)
brand(maxLength:20,blank:false)
model(maxLength:20,blank:false)
}

To make these constraints take effect, we need to restart the application. In the console where you started the application, enter Control-C to stop the application. Then, enter grails run-app, and we're ready to test the validation.

Now, if we try to sneak in a new employee with an invalid network ID, our application knows not to accept it.

Naturally we'll want change the error message to something more business-focused and less technical. Grails provides a message resources file for you to define your own custom error message.

The constraints we defined represent just a small sample of the validation that Grails offers. Be sure to check out the Grails documentation for the complete list of available constraints.

Step 7 - Get Dynamic

Everything up to this point has shown us that we can build web applications more quickly than we might have thought, and we've seen some impressive features of this up-and-coming framework. That said, here's where the real magic begins. Recall our brief mention of dynamic methods back when we added the ability to see all computers assigned to a user? Let's take a deeper at look at this feature.

Once we have some basic CRUD functionality like this, one of the first requests we get is, "I need a way to see all employees by _______." Or, "Show me all computers made by _______ with serial numbers starting with _______." These are reasonable enough requests, but how much effort typically goes into building these simple reports? Too often it requires a new method in some session bean, a new mapping in one of my web framework's configuration files, perhaps even some SQL, etc. Not only can we get by without those things, we can do so and be ready to quickly adapt to the next change.

For starters, let's say we need to be able to find all employees by last name. We'll add a new menu option to the Employee List page to take us to our new query page. Add this block to the menu section of grails-app/views/employeeBean/list.gsp.

 	Search Employees   

Clicking this menu option will invoke a new action in our controller named search. We don't need that action to do anything more than render our search input page. So, add an empty method in EmployeeBeanController.groovy to accept these requests.

def search = {
}

This empty action tells Grails that search is a valid action for this controller. When Grails receives a request for this action, it will simply look for a template with the same name (i.e., search.gsp) in grails-app/views/employeeBean/ and render its content.

Since we really just want a simple form with input fields for an employee, we can use the create.gsp template as a good staring point. When we're finished, our new search.gsp template should look like this:

                                                          

Search Employees

If you're thinking that we shouldn't be repeating so much of the wrapper content (e.g., the <head> tag, etc.), you're right. Grails offers various layout mechanisms to avoid this duplication. (We'll consider that an exercise for the reader.) In the meantime, our search page awaits. (While Grails generally provides dynamic deployment of such changes, some readers have reported the need to restart the application in order to successfully access the new search page. To do so, enter Control-C in the console where you started the application. Then, enter grails run-app, and make your way to the search page.)

You'll notice that in search.gsp we specified that the form should post all requests to a new action named (plainly enough) showSearchResults. So, we need to define that new action in EmployeeBeanController.groovy.

def showSearchResults = {
render(view:'list', model:[ employeeBeanList:
EmployeeBean.findAllByLastNameLike("%" + params.lastName + "%") ])
}

Because we just want to display a list of matching employees, we can safely use the existing list.gsp template. This is another example of Grails's ability to provide significant functionality in a few succinct commands. All requests to the showSearchResults action will ...

  • Get the search criteria from the request using params.lastName
  • Get a list of EmployeeBean objects for employees matching that last name
  • Put the results in an object named employeeBeanList
  • render the view, using the employeeBeanList object as the model, in the list template

When we click Search, sure enough, we get the matching results.

Of course, if we can search employees by last name, why not search by first name as well? We can easily add a new input field to our search page.

                                         

That's trivial enough, but what do we have to change in our controller? Very little. Instead of using the findAllByLastNameLike method, we'll now invoke findAllByLastNameLikeAndFirstNameLike.

def showSearchResults = {
render(view:'list', model:[ employeeBeanList:
EmployeeBean.findAllByLastNameLikeAndFirstNameLike("%" + params.lastName + "%",
"%" + params.firstName + "%") ])
}

Grails automatically provides dynamic finders for just about every query combination you can think of! Try it out. Our search from above yields just what we'd expect given our updated query.

Step 8 - Build Your Own Criteria

Over time, you may find that your requirements grow in complexity. How can we handle queries that need to group various sets of criteria? For example, suppose I want to find all employees where...

  • The network ID partially matches some value with case-sensitivity, or
  • The first name and the last name exactly match their respective criteria.

That kind of query is beyond the capabilities of the dynamic finders that we just saw. No worries. That doesn't mean that your application has to take on any additional complexity. Grails offers the immensely flexible Hibernate Criteria Builder to satisfy that need without sacrificing any of the functionality we've seen so far.

First, let's modify our search page to capture the input for our new requirements. If we edit search.gsp and replace the current input fields with the following fields, it should give us what we're looking for.

                          -- or --                                    

Now let's look at the actual query logic. The following code satisfies our (admittedly odd) requirements in just a few short lines. (This code should replace our current showSearchResults method in EmployeeBeanController.groovy.)

def showSearchResults = {
def criteria = EmployeeBean.createCriteria()

def results = criteria {
or {
ilike("networkId", "%" + params.networkId + "%")
and {
eq("firstName", params.firstName)
eq("lastName", params.lastName)
}
}
}

render(view:'list', model:[ employeeBeanList: results.adaptee ])
}

Even if you've never used Hibernate or Grails before, you can quickly get the gist of what's going on here. In a Groovy builder, each component is referred to as a "node." The or node tells us that we'll match any rows that satisfy one or more of the criteria specified by its child nodes. In this case, it has two child nodes.

The first child node returns results that satisfy a case-sensitive like operation on the networkId attribute.

ilike("networkId", "%" + params.networkId + "%")

The second child node is an and node. It returns results that match all of its child nodes. Its child nodes look for rows that exactly match (eq) the firstName and the lastName attributes.

and {
eq("firstName", params.firstName)
eq("lastName", params.lastName)
}

Together, these nodes fulfill our query requirements.

If the grouping operators (and, or, etc.) seem oddly positioned at first, it's because the Hibernate Criteria Builder somewhat resembles a Polish notation syntax, where you specify the operator before the operands. That's certainly a departure from Java's use of relational operators, but it should become relatively intuitive after your first few queries.

Now let's see it in action.

Here we're looking for all employees where the network ID contains "jr" or the employee's name is "John Doe".

And when we click Search, we get exactly that.

Summary

How does this development process compare to the approach you use today? With each feature we added along the way, how many components would you touch to make that same change in your existing framework? XML files? DAOs? Form/view classes? Others? And what do you gain by having those additional components to maintain? Ultimate flexibility? If so, do you really need it? Some shops definitely do require that flexibility, but for the other 80%, aren't some sensible defaults (al a convention over configuration) well worth the productivity gains?

We've written very little code in this exercise, and yet we have a functioning and flexible application. We didn't change a single line in our existing entity beans. And because we've written such little code to get to this point, we have less code to maintain. We'll be writing less code tomorrow when the requirements change again. Just think how quickly you can respond to evolving business needs when your application is this agile!

Resources

About the Author

Jason Rudolph is an Application Architect at Railinc, where he develops software that helps keep trains moving efficiently throughout North America. He recently delivered an industry-wide inspection reporting and management system relied on for operational safety by Fortune 500 railroads, equipment leasing companies, and the Federal Railroad Administration. Jason's interests include dynamic languages, lightweight development methodologies, improving developer productivity, and a quest to keep programming fun. Jason holds a degree in Computer Science from the University of Virginia. He currently lives in Raleigh, NC with his wife (who can take a functional web app and make it actually look good) and his dog (who can outrun him, but is no match for squirrels). You can find Jason online at http://jasonrudolph.com.

この記事に星をつける

おすすめ度
スタイル

BT