| <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" |
| "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
| <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> |
| <head> |
| <!-- Matomo --> |
| <script> |
| var _paq = window._paq = window._paq || []; |
| /* tracker methods like "setCustomDimension" should be called before "trackPageView" */ |
| _paq.push(["setDoNotTrack", true]); |
| _paq.push(["disableCookies"]); |
| _paq.push(['trackPageView']); |
| _paq.push(['enableLinkTracking']); |
| (function() { |
| var u="https://analytics.apache.org/"; |
| _paq.push(['setTrackerUrl', u+'matomo.php']); |
| _paq.push(['setSiteId', '79']); |
| var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; |
| g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); |
| })(); |
| </script> |
| <!-- End Matomo Code --> |
| <title>13 The Service Layer 5.3.0</title> |
| <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> |
| <link rel="stylesheet" href="../css/main.css" type="text/css" media="screen, print" title="Style" charset="utf-8"/> |
| <link rel="stylesheet" href="../css/pdf.css" type="text/css" media="print" title="PDF" charset="utf-8"/> |
| <script type="text/javascript"> |
| function addJsClass() { |
| var classes = document.body.className.split(" "); |
| classes.push("js"); |
| document.body.className = classes.join(" "); |
| } |
| </script> |
| </head> |
| |
| <body class="body" onload="addJsClass();"> |
| <div id="navigation"> |
| <div class="navTitle"> |
| |
| The Grails Framework |
| </div> |
| <div class="navLinks"> |
| <ul> |
| <li> |
| <div id="nav-summary" onmouseover="toggleNavSummary(false)" onmouseout="toggleNavSummary(true)"> |
| <a href="../guide/index.html" class="button">Table of contents</a> |
| |
| <div id="nav-summary-childs" style="display:none;"> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/introduction.html"><strong>1</strong><span>Introduction</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/gettingStarted.html"><strong>2</strong><span>Getting Started</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/upgrading.html"><strong>3</strong><span>Upgrading from the previous versions</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/conf.html"><strong>4</strong><span>Configuration</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/commandLine.html"><strong>5</strong><span>The Command Line</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/profiles.html"><strong>6</strong><span>Application Profiles</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/GORM.html"><strong>7</strong><span>Object Relational Mapping (GORM)</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/theWebLayer.html"><strong>8</strong><span>The Web Layer</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/traits.html"><strong>9</strong><span>Traits</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/REST.html"><strong>10</strong><span>REST</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/async.html"><strong>11</strong><span>Asynchronous Programming</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/validation.html"><strong>12</strong><span>Validation</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/services.html"><strong>13</strong><span>The Service Layer</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/staticTypeCheckingAndCompilation.html"><strong>14</strong><span>Static Type Checking And Compilation</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/testing.html"><strong>15</strong><span>Testing</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/i18n.html"><strong>16</strong><span>Internationalization</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/security.html"><strong>17</strong><span>Security</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/plugins.html"><strong>18</strong><span>Plugins</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/spring.html"><strong>19</strong><span>Grails and Spring</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/scaffolding.html"><strong>20</strong><span>Scaffolding</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/deployment.html"><strong>21</strong><span>Deployment</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/contributing.html"><strong>22</strong><span>Contributing to Grails</span></a> |
| </div> |
| |
| </div> |
| </div> |
| </li> |
| <li class="separator selected"> |
| <a id="ref-button" onclick="localToggle(); return false;" href="#">Quick Reference</a> |
| </li> |
| </ul> |
| </div> |
| |
| |
| </div> |
| |
| <table id="colset" border="0" cellpadding="0" cellspacing="0"> |
| <tr> |
| <td id="col1"> |
| <div id="main" class="corner-all"> |
| |
| |
| <div class="toc-item prev-left"><a href="../guide/validation.html"><< <strong>12</strong><span>Validation</span></a></div> |
| |
| |
| <span id='toggle-col1' class="toggle">(<a href="#" onclick="localToggle(); return false;">Quick Reference</a>)</span> |
| |
| |
| <div class="toc-item next-right"><a href="../guide/staticTypeCheckingAndCompilation.html"><strong>14</strong><span>Static Type Checking And Compilation</span> >></a></div> |
| |
| |
| |
| <div class="project"> |
| <h1>13 The Service Layer</h1> |
| |
| <p><strong>Version:</strong> 5.3.0</p> |
| </div> |
| |
| |
| <div id="table-of-content"> |
| <h2>Table of Contents</h2> |
| |
| <div class="toc-item" style="margin-left:0px"><a href="#declarativeTransactions"><strong>13.1</strong><span>Declarative Transactions</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:10px"><a href="#transactionsMultiDataSource"><strong>13.1.1</strong><span>Transactions and Multi-DataSources</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:10px"><a href="#transactionsRollbackAndTheSession"><strong>13.1.2</strong><span>Transactions Rollback and the Session</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0px"><a href="#scopedServices"><strong>13.2</strong><span>Scoped Services</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0px"><a href="#dependencyInjectionServices"><strong>13.3</strong><span>Dependency Injection and Services</span></a> |
| </div> |
| |
| </div> |
| |
| |
| |
| <a name="11. The Service Layer"><!-- Legacy link --></a> |
| <h1 id="services">13 The Service Layer</h1> |
| |
| <div class='contribute-btn'> |
| <button type='button' class='btn btn-default' onclick='window.location.href="https://github.com/grails/grails-doc/edit/5.3.x/src/en/guide/services.adoc"'> |
| <i class='fa fa-pencil-square-o'></i> Improve this doc |
| </button> |
| </div> |
| |
| |
| <div class="paragraph"> |
| <p>Grails defines the notion of a service layer. The Grails team discourages the embedding of core application logic inside controllers, as it does not promote reuse and a clean separation of concerns.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Services in Grails are the place to put the majority of the logic in your application, leaving controllers responsible for handling request flow with redirects and so on.</p> |
| </div> |
| <div class="sect3"> |
| <h4 id="_creating_a_service">Creating a Service</h4> |
| <div class="paragraph"> |
| <p>You can create a Grails service by running the <a href="../ref/Command%20Line/create-service.html">create-service</a> command from the root of your project in a terminal window:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy">grails create-service helloworld.simple</code></pre> |
| </div> |
| </div> |
| <div class="admonitionblock note"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <i class="fa icon-note" title="Note"></i> |
| </td> |
| <td class="content"> |
| If no package is specified with the create-service script, Grails automatically uses the <code>grails.defaultPackage</code> defined in <code>grails-app/conf/application.yml</code> as the package name. |
| </td> |
| </tr> |
| </table> |
| </div> |
| <div class="paragraph"> |
| <p>The above example will create a service at the location <code>grails-app/services/helloworld/SimpleService.groovy</code>. A service’s name ends with the convention <code>Service</code>, other than that a service is a plain Groovy class:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> helloworld |
| |
| <span class="type">class</span> <span class="class">SimpleService</span> { |
| }</code></pre> |
| </div> |
| </div> |
| </div> |
| |
| <a name="11.1 Declarative Transactions"><!-- Legacy link --></a> |
| <h2 id="declarativeTransactions">13.1 Declarative Transactions</h2> |
| |
| <div class='contribute-btn'> |
| <button type='button' class='btn btn-default' onclick='window.location.href="https://github.com/grails/grails-doc/edit/5.3.x/src/en/guide/services/declarativeTransactions.adoc"'> |
| <i class='fa fa-pencil-square-o'></i> Improve this doc |
| </button> |
| </div> |
| |
| |
| <div class="sect2"> |
| <h3 id="_declarative_transactions">Declarative Transactions</h3> |
| <div class="paragraph"> |
| <p>Services are typically involved with coordinating logic between <a href="GORM.html">domain classes</a>, and hence often involved with persistence that spans large operations. Given the nature of services, they frequently require transactional behaviour. You can use programmatic transactions with the <a href="../ref/Domain%20Classes/withTransaction.html">withTransaction</a> method, however this is repetitive and doesn’t fully leverage the power of Spring’s underlying transaction abstraction.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Services enable transaction demarcation, which is a declarative way of defining which methods are to be made transactional. To enable transactions on a service use the <code>Transactional</code> transform:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">import</span> <span class="include">grails.gorm.transactions.*</span> |
| |
| <span class="annotation">@Transactional</span> |
| <span class="type">class</span> <span class="class">CountryService</span> { |
| |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>The result is that all methods are wrapped in a transaction and automatic rollback occurs if a method throws an exception (both Checked or Runtime exceptions) or an Error. The propagation level of the transaction is by default set to <a href="https://docs.spring.io/spring/docs/5.3.8/javadoc-api/org/springframework/transaction/TransactionDefinition.html#PROPAGATION_REQUIRED">PROPAGATION_REQUIRED</a>.</p> |
| </div> |
| <div class="admonitionblock warning"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <i class="fa icon-warning" title="Warning"></i> |
| </td> |
| <td class="content"> |
| Version Grails 3.2.0 was the first version to use GORM 6 by default. Checked exceptions did not roll back transactions before GORM 6. Only a method which threw a runtime exception (i.e. one that extends RuntimeException) rollbacked a transaction. |
| </td> |
| </tr> |
| </table> |
| </div> |
| <div class="admonitionblock warning"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <i class="fa icon-warning" title="Warning"></i> |
| </td> |
| <td class="content"> |
| Warning: <a href="#dependencyInjectionServices">dependency injection</a> is the <strong>only</strong> way that declarative transactions work. You will not get a transactional service if you use the <code>new</code> operator such as <code>new BookService()</code> |
| </td> |
| </tr> |
| </table> |
| </div> |
| <div class="sect3"> |
| <h4 id="_the_transactional_annotation_vs_the_transactional_property">The Transactional annotation vs the transactional property</h4> |
| <div class="paragraph"> |
| <p>In versions of Grails prior to Grails 3.1, Grails created Spring proxies and used the <code>transactional</code> property to enable and disable proxy creation. These proxies are disabled by default in applications created with Grails 3.1 and above in favor of the <code>@Transactional</code> transformation.</p> |
| </div> |
| <div class="paragraph"> |
| <p>For versions of Grails 3.1.x and 3.2.x, if you wish to renable this feature (not recommended) then you must set <code>grails.spring.transactionManagement</code> to true or remove the configuration in <code>grails-app/conf/application.yml</code> or <code>grails-app/conf/application.groovy</code>.</p> |
| </div> |
| <div class="paragraph"> |
| <p>In Grails 3.3.x Spring proxies for transaction management has been dropped completely, and you must use Grails' AST transforms. In Grails 3.3.x, if you wish to continue to use Spring proxies for transaction management you will have to configure them manually, using the appropriate Spring configuration.</p> |
| </div> |
| <div class="admonitionblock note"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <i class="fa icon-note" title="Note"></i> |
| </td> |
| <td class="content"> |
| In addition, prior to Grails 3.1 services were transactional by default, as of Grails 3.1 they are only transactional if the <code>@Transactional</code> transformation is applied. |
| </td> |
| </tr> |
| </table> |
| </div> |
| </div> |
| </div> |
| <div class="sect2"> |
| <h3 id="_custom_transaction_configuration">Custom Transaction Configuration</h3> |
| <div class="paragraph"> |
| <p>Grails also provides <code>@Transactional</code> and <code>@NotTransactional</code> annotations for cases where you need more fine-grained control over transactions at a per-method level or need to specify an alternative propagation level. For example, the <code>@NotTransactional</code> annotation can be used to mark a particular method to be skipped when a class is annotated with <code>@Transactional</code>.</p> |
| </div> |
| <div class="admonitionblock note"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <i class="fa icon-note" title="Note"></i> |
| </td> |
| <td class="content"> |
| Annotating a service method with <code>Transactional</code> disables the default Grails transactional behavior for that service (in the same way that adding <code>transactional=false</code> does) so if you use any annotations you must annotate all methods that require transactions. |
| </td> |
| </tr> |
| </table> |
| </div> |
| <div class="paragraph"> |
| <p>In this example <code>listBooks</code> uses a read-only transaction, <code>updateBook</code> uses a default read-write transaction, and <code>deleteBook</code> is not transactional (probably not a good idea given its name).</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">import</span> <span class="include">grails.gorm.transactions.Transactional</span> |
| |
| <span class="type">class</span> <span class="class">BookService</span> { |
| |
| <span class="annotation">@Transactional</span>(readOnly = <span class="predefined-constant">true</span>) |
| <span class="keyword">def</span> <span class="function">listBooks</span>() { |
| <span class="predefined-type">Book</span>.list() |
| } |
| |
| <span class="annotation">@Transactional</span> |
| <span class="keyword">def</span> <span class="function">updateBook</span>() { |
| <span class="comment">// ...</span> |
| } |
| |
| <span class="keyword">def</span> <span class="function">deleteBook</span>() { |
| <span class="comment">// ...</span> |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>You can also annotate the class to define the default transaction behavior for the whole service, and then override that default per-method:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">import</span> <span class="include">grails.gorm.transactions.Transactional</span> |
| |
| <span class="annotation">@Transactional</span> |
| <span class="type">class</span> <span class="class">BookService</span> { |
| |
| <span class="keyword">def</span> <span class="function">listBooks</span>() { |
| <span class="predefined-type">Book</span>.list() |
| } |
| |
| <span class="keyword">def</span> <span class="function">updateBook</span>() { |
| <span class="comment">// ...</span> |
| } |
| |
| <span class="keyword">def</span> <span class="function">deleteBook</span>() { |
| <span class="comment">// ...</span> |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>This version defaults to all methods being read-write transactional (due to the class-level annotation), but the <code>listBooks</code> method overrides this to use a read-only transaction:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">import</span> <span class="include">grails.gorm.transactions.Transactional</span> |
| |
| <span class="annotation">@Transactional</span> |
| <span class="type">class</span> <span class="class">BookService</span> { |
| |
| <span class="annotation">@Transactional</span>(readOnly = <span class="predefined-constant">true</span>) |
| <span class="keyword">def</span> <span class="function">listBooks</span>() { |
| <span class="predefined-type">Book</span>.list() |
| } |
| |
| <span class="keyword">def</span> <span class="function">updateBook</span>() { |
| <span class="comment">// ...</span> |
| } |
| |
| <span class="keyword">def</span> <span class="function">deleteBook</span>() { |
| <span class="comment">// ...</span> |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Although <code>updateBook</code> and <code>deleteBook</code> aren’t annotated in this example, they inherit the configuration from the class-level annotation.</p> |
| </div> |
| <div class="paragraph"> |
| <p>For more information refer to the section of the Spring user guide on <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html#transaction-declarative-annotations">Using @Transactional</a>.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Unlike Spring you do not need any prior configuration to use <code>Transactional</code>; just specify the annotation as needed and Grails will detect them up automatically.</p> |
| </div> |
| <div class="sect3"> |
| <h4 id="_transaction_status">Transaction status</h4> |
| <div class="paragraph"> |
| <p>An instance of <a href="https://docs.spring.io/spring/docs/5.3.8/javadoc-api/org/springframework/transaction/TransactionStatus.html">TransactionStatus</a> is available by default in Grails transactional service methods.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Example:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">import</span> <span class="include">grails.gorm.transactions.Transactional</span> |
| |
| <span class="annotation">@Transactional</span> |
| <span class="type">class</span> <span class="class">BookService</span> { |
| |
| <span class="keyword">def</span> <span class="function">deleteBook</span>() { |
| transactionStatus.setRollbackOnly() |
| } |
| }</code></pre> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| <a name="11.1.1 Transactions Multi-DataSource"><!-- Legacy link --></a> |
| <h2 id="transactionsMultiDataSource">13.1.1 Transactions and Multi-DataSources</h2> |
| |
| <div class='contribute-btn'> |
| <button type='button' class='btn btn-default' onclick='window.location.href="https://github.com/grails/grails-doc/edit/5.3.x/src/en/guide/services/declarativeTransactions/transactionsMultiDataSource.adoc"'> |
| <i class='fa fa-pencil-square-o'></i> Improve this doc |
| </button> |
| </div> |
| |
| |
| <div class="paragraph"> |
| <p>Given two domain classes such as:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="type">class</span> <span class="class">Movie</span> { |
| <span class="predefined-type">String</span> title |
| }</code></pre> |
| </div> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="type">class</span> <span class="class">Book</span> { |
| <span class="predefined-type">String</span> title |
| |
| <span class="directive">static</span> mapping = { |
| datasource <span class="string"><span class="delimiter">'</span><span class="content">books</span><span class="delimiter">'</span></span> |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>You can supply the desired data source to <code>@Transactional</code> or <code>@ReadOnly</code> annotations.</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">import</span> <span class="include">grails.gorm.transactions.ReadOnly</span> |
| <span class="keyword">import</span> <span class="include">grails.gorm.transactions.Transactional</span> |
| <span class="keyword">import</span> <span class="include">groovy.transform.CompileStatic</span> |
| |
| <span class="annotation">@CompileStatic</span> |
| <span class="type">class</span> <span class="class">BookService</span> { |
| |
| <span class="annotation">@ReadOnly</span>(<span class="string"><span class="delimiter">'</span><span class="content">books</span><span class="delimiter">'</span></span>) |
| <span class="predefined-type">List</span><<span class="predefined-type">Book</span>> findAll() { |
| <span class="predefined-type">Book</span>.where {}.findAll() |
| } |
| |
| <span class="annotation">@Transactional</span>(<span class="string"><span class="delimiter">'</span><span class="content">books</span><span class="delimiter">'</span></span>) |
| <span class="predefined-type">Book</span> save(<span class="predefined-type">String</span> title) { |
| <span class="predefined-type">Book</span> book = <span class="keyword">new</span> <span class="predefined-type">Book</span>(<span class="key">title</span>: title) |
| book.save() |
| book |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="annotation">@CompileStatic</span> |
| <span class="type">class</span> <span class="class">MovieService</span> { |
| |
| <span class="annotation">@ReadOnly</span> |
| <span class="predefined-type">List</span><Movie> findAll() { |
| Movie.where {}.findAll() |
| } |
| }</code></pre> |
| </div> |
| </div> |
| |
| <a name="11.1.1 Transactions Rollback and the Session"><!-- Legacy link --></a> |
| <h2 id="transactionsRollbackAndTheSession">13.1.2 Transactions Rollback and the Session</h2> |
| |
| <div class='contribute-btn'> |
| <button type='button' class='btn btn-default' onclick='window.location.href="https://github.com/grails/grails-doc/edit/5.3.x/src/en/guide/services/declarativeTransactions/transactionsRollbackAndTheSession.adoc"'> |
| <i class='fa fa-pencil-square-o'></i> Improve this doc |
| </button> |
| </div> |
| |
| |
| <div class="sect2"> |
| <h3 id="_understanding_transactions_and_the_hibernate_session">Understanding Transactions and the Hibernate Session</h3> |
| <div class="paragraph"> |
| <p>When using transactions there are important considerations you must take into account with regards to how the underlying persistence session is handled by Hibernate. When a transaction is rolled back the Hibernate session used by GORM is cleared. This means any objects within the session become detached and accessing uninitialized lazy-loaded collections will lead to a <code>LazyInitializationException</code>.</p> |
| </div> |
| <div class="paragraph"> |
| <p>To understand why it is important that the Hibernate session is cleared. Consider the following example:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="type">class</span> <span class="class">Author</span> { |
| <span class="predefined-type">String</span> name |
| <span class="predefined-type">Integer</span> age |
| |
| <span class="directive">static</span> hasMany = [<span class="key">books</span>: <span class="predefined-type">Book</span>] |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>If you were to save two authors using consecutive transactions as follows:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy">Author.withTransaction { status -> |
| <span class="keyword">new</span> Author(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Stephen King</span><span class="delimiter">"</span></span>, <span class="key">age</span>: <span class="integer">40</span>).save() |
| status.setRollbackOnly() |
| } |
| |
| Author.withTransaction { status -> |
| <span class="keyword">new</span> Author(<span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Stephen King</span><span class="delimiter">"</span></span>, <span class="key">age</span>: <span class="integer">40</span>).save() |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Only the second author would be saved since the first transaction rolls back the author <code>save()</code> by clearing the Hibernate session. If the Hibernate session were not cleared then both author instances would be persisted and it would lead to very unexpected results.</p> |
| </div> |
| <div class="paragraph"> |
| <p>It can, however, be frustrating to get a <code>LazyInitializationException</code> due to the session being cleared.</p> |
| </div> |
| <div class="paragraph"> |
| <p>For example, consider the following example:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="type">class</span> <span class="class">AuthorService</span> { |
| |
| <span class="type">void</span> updateAge(id, <span class="type">int</span> age) { |
| <span class="keyword">def</span> author = Author.get(id) |
| author.age = age |
| <span class="keyword">if</span> (author.isTooOld()) { |
| <span class="keyword">throw</span> <span class="keyword">new</span> AuthorException(<span class="string"><span class="delimiter">"</span><span class="content">too old</span><span class="delimiter">"</span></span>, author) |
| } |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="type">class</span> <span class="class">AuthorController</span> { |
| |
| <span class="keyword">def</span> authorService |
| |
| <span class="keyword">def</span> <span class="function">updateAge</span>() { |
| <span class="keyword">try</span> { |
| authorService.updateAge(params.id, params.int(<span class="string"><span class="delimiter">"</span><span class="content">age</span><span class="delimiter">"</span></span>)) |
| } |
| <span class="keyword">catch</span>(e) { |
| render <span class="string"><span class="delimiter">"</span><span class="content">Author books </span><span class="inline"><span class="inline-delimiter">${</span>e.author.books<span class="inline-delimiter">}</span></span><span class="delimiter">"</span></span> |
| } |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>In the above example the transaction will be rolled back if the age of the <code>Author</code> age exceeds the maximum value defined in the <code>isTooOld()</code> method by throwing an <code>AuthorException</code>. The <code>AuthorException</code> references the author but when the <code>books</code> association is accessed a <code>LazyInitializationException</code> will be thrown because the underlying Hibernate session has been cleared.</p> |
| </div> |
| <div class="paragraph"> |
| <p>To solve this problem you have a number of options. One is to ensure you query eagerly to get the data you will need:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="type">class</span> <span class="class">AuthorService</span> { |
| ... |
| void updateAge(id, <span class="type">int</span> age) { |
| <span class="keyword">def</span> author = Author.findById(id, [<span class="key">fetch</span>:[<span class="key">books</span>:<span class="string"><span class="delimiter">"</span><span class="content">eager</span><span class="delimiter">"</span></span>]]) |
| ...</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>In this example the <code>books</code> association will be queried when retrieving the <code>Author</code>.</p> |
| </div> |
| <div class="admonitionblock note"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <i class="fa icon-note" title="Note"></i> |
| </td> |
| <td class="content"> |
| This is the optimal solution as it requires fewer queries than the following suggested solutions. |
| </td> |
| </tr> |
| </table> |
| </div> |
| <div class="paragraph"> |
| <p>Another solution is to redirect the request after a transaction rollback:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="type">class</span> <span class="class">AuthorController</span> { |
| |
| AuthorService authorService |
| |
| <span class="keyword">def</span> <span class="function">updateAge</span>() { |
| <span class="keyword">try</span> { |
| authorService.updateAge(params.id, params.int(<span class="string"><span class="delimiter">"</span><span class="content">age</span><span class="delimiter">"</span></span>)) |
| } |
| <span class="keyword">catch</span>(e) { |
| flash.message = <span class="string"><span class="delimiter">"</span><span class="content">Can't update age</span><span class="delimiter">"</span></span> |
| redirect <span class="key">action</span>:<span class="string"><span class="delimiter">"</span><span class="content">show</span><span class="delimiter">"</span></span>, <span class="key">id</span>:params.id |
| } |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>In this case a new request will deal with retrieving the <code>Author</code> again. And, finally a third solution is to retrieve the data for the <code>Author</code> again to make sure the session remains in the correct state:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="type">class</span> <span class="class">AuthorController</span> { |
| |
| <span class="keyword">def</span> authorService |
| |
| <span class="keyword">def</span> <span class="function">updateAge</span>() { |
| <span class="keyword">try</span> { |
| authorService.updateAge(params.id, params.int(<span class="string"><span class="delimiter">"</span><span class="content">age</span><span class="delimiter">"</span></span>)) |
| } |
| <span class="keyword">catch</span>(e) { |
| <span class="keyword">def</span> author = Author.read(params.id) |
| render <span class="string"><span class="delimiter">"</span><span class="content">Author books </span><span class="inline"><span class="inline-delimiter">${</span>author.books<span class="inline-delimiter">}</span></span><span class="delimiter">"</span></span> |
| } |
| } |
| }</code></pre> |
| </div> |
| </div> |
| </div> |
| <div class="sect2"> |
| <h3 id="_validation_errors_and_rollback">Validation Errors and Rollback</h3> |
| <div class="paragraph"> |
| <p>A common use case is to rollback a transaction if there are validation errors. For example consider this service:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">import</span> <span class="include">grails.validation.ValidationException</span> |
| |
| <span class="type">class</span> <span class="class">AuthorService</span> { |
| |
| <span class="type">void</span> updateAge(id, <span class="type">int</span> age) { |
| <span class="keyword">def</span> author = Author.get(id) |
| author.age = age |
| <span class="keyword">if</span> (!author.validate()) { |
| <span class="keyword">throw</span> <span class="keyword">new</span> ValidationException(<span class="string"><span class="delimiter">"</span><span class="content">Author is not valid</span><span class="delimiter">"</span></span>, author.errors) |
| } |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>To re-render the same view that a transaction was rolled back in you can re-associate the errors with a refreshed instance before rendering:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">import</span> <span class="include">grails.validation.ValidationException</span> |
| |
| <span class="type">class</span> <span class="class">AuthorController</span> { |
| |
| <span class="keyword">def</span> authorService |
| |
| <span class="keyword">def</span> <span class="function">updateAge</span>() { |
| <span class="keyword">try</span> { |
| authorService.updateAge(params.id, params.int(<span class="string"><span class="delimiter">"</span><span class="content">age</span><span class="delimiter">"</span></span>)) |
| } |
| <span class="keyword">catch</span> (ValidationException e) { |
| <span class="keyword">def</span> author = Author.read(params.id) |
| author.errors = e.errors |
| render <span class="key">view</span>: <span class="string"><span class="delimiter">"</span><span class="content">edit</span><span class="delimiter">"</span></span>, <span class="key">model</span>: [<span class="key">author</span>:author] |
| } |
| } |
| }</code></pre> |
| </div> |
| </div> |
| </div> |
| |
| <a name="11.2 Scoped Services"><!-- Legacy link --></a> |
| <h2 id="scopedServices">13.2 Scoped Services</h2> |
| |
| <div class='contribute-btn'> |
| <button type='button' class='btn btn-default' onclick='window.location.href="https://github.com/grails/grails-doc/edit/5.3.x/src/en/guide/services/scopedServices.adoc"'> |
| <i class='fa fa-pencil-square-o'></i> Improve this doc |
| </button> |
| </div> |
| |
| |
| <div class="paragraph"> |
| <p>By default, access to service methods is not synchronised, so nothing prevents concurrent execution of those methods. In fact, because the service is a singleton and may be used concurrently, you should be very careful about storing state in a service. Or take the easy (and better) road and never store state in a service.</p> |
| </div> |
| <div class="paragraph"> |
| <p>You can change this behaviour by placing a service in a particular scope. The supported scopes are:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p><code>prototype</code> - A new service is created every time it is injected into another class</p> |
| </li> |
| <li> |
| <p><code>request</code> - A new service will be created per request</p> |
| </li> |
| <li> |
| <p><code>flash</code> - A new service will be created for the current and next request only</p> |
| </li> |
| <li> |
| <p><code>flow</code> - In web flows the service will exist for the scope of the flow</p> |
| </li> |
| <li> |
| <p><code>conversation</code> - In web flows the service will exist for the scope of the conversation. ie a root flow and its sub flows</p> |
| </li> |
| <li> |
| <p><code>session</code> - A service is created for the scope of a user session</p> |
| </li> |
| <li> |
| <p><code>singleton</code> (default) - Only one instance of the service ever exists</p> |
| </li> |
| </ul> |
| </div> |
| <div class="admonitionblock note"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <i class="fa icon-note" title="Note"></i> |
| </td> |
| <td class="content"> |
| If your service is <code>flash</code>, <code>flow</code> or <code>conversation</code> scoped it must implement <code>java.io.Serializable</code> and can only be used in the context of a Web Flow. |
| </td> |
| </tr> |
| </table> |
| </div> |
| <div class="paragraph"> |
| <p>To enable one of the scopes, add a static scope property to your class whose value is one of the above, for example</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="directive">static</span> scope = <span class="string"><span class="delimiter">"</span><span class="content">flow</span><span class="delimiter">"</span></span></code></pre> |
| </div> |
| </div> |
| <div class="admonitionblock note"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <i class="fa icon-note" title="Note"></i> |
| </td> |
| <td class="content"> |
| <div class="title">Upgrading</div> |
| <div class="paragraph"> |
| <p>Starting with Grails 2.3, new applications are generated with configuration that defaults the scope of controllers to <code>singleton</code>. |
| If <code>singleton</code> controllers interact with <code>prototype</code> scoped services, the services effectively behave as per-controller singletons. |
| If non-singleton services are required, controller scope should be changed as well.</p> |
| </div> |
| <div class="paragraph"> |
| <p>See <a href="theWebLayer.html#controllersAndScopes">Controllers and Scopes</a> in the user guide for more information.</p> |
| </div> |
| </td> |
| </tr> |
| </table> |
| </div> |
| <div class="sect3"> |
| <h4 id="_lazy_initialization">Lazy initialization</h4> |
| <div class="paragraph"> |
| <p>You can also configure whether the service is lazily initialized. By default, this is set to <code>true</code>, but you can disable this and make initialization eager with the <code>lazyInit</code> property:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="directive">static</span> lazyInit = <span class="predefined-constant">false</span></code></pre> |
| </div> |
| </div> |
| </div> |
| |
| <a name="11.3 Dependency Injection and Services"><!-- Legacy link --></a> |
| <h2 id="dependencyInjectionServices">13.3 Dependency Injection and Services</h2> |
| |
| <div class='contribute-btn'> |
| <button type='button' class='btn btn-default' onclick='window.location.href="https://github.com/grails/grails-doc/edit/5.3.x/src/en/guide/services/dependencyInjectionServices.adoc"'> |
| <i class='fa fa-pencil-square-o'></i> Improve this doc |
| </button> |
| </div> |
| |
| |
| <div class="sect3"> |
| <h4 id="_dependency_injection_basics">Dependency Injection Basics</h4> |
| <div class="paragraph"> |
| <p>A key aspect of Grails services is the ability to use <a href="http://www.springframework.org/">Spring Framework</a>'s dependency injection features. Grails supports "dependency injection by convention". In other words, you can use the property name representation of the class name of a service to automatically inject them into controllers, tag libraries, and so on.</p> |
| </div> |
| <div class="paragraph"> |
| <p>As an example, given a service called <code>BookService</code>, if you define a property called <code>bookService</code> in a controller as follows:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="type">class</span> <span class="class">BookController</span> { |
| <span class="keyword">def</span> bookService |
| ... |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>In this case, the Spring container will automatically inject an instance of that service based on its configured scope. All dependency injection is done by name. You can also specify the type as follows:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="type">class</span> <span class="class">AuthorService</span> { |
| BookService bookService |
| }</code></pre> |
| </div> |
| </div> |
| <div class="admonitionblock note"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <i class="fa icon-note" title="Note"></i> |
| </td> |
| <td class="content"> |
| NOTE: Normally the property name is generated by lower casing the first letter of the type. For example, an instance of the <code>BookService</code> class would map to a property named <code>bookService</code>. |
| </td> |
| </tr> |
| </table> |
| </div> |
| <div class="paragraph"> |
| <p>To be consistent with standard JavaBean conventions, if the first 2 letters of the class name are upper case, the property name is the same as the class name. For example, the property name of the <code>JDBCHelperService</code> class would be <code>JDBCHelperService</code>, not <code>jDBCHelperService</code> or <code>jdbcHelperService</code>.</p> |
| </div> |
| <div class="paragraph"> |
| <p>See section 8.8 of the JavaBean specification for more information on de-capitalization rules.</p> |
| </div> |
| <div class="admonitionblock note"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <i class="fa icon-note" title="Note"></i> |
| </td> |
| <td class="content"> |
| Only the top level object is subjected to injection as traversing all nested objects to perform injection would be a performance issue. |
| </td> |
| </tr> |
| </table> |
| </div> |
| <div class="paragraph"> |
| <p>Be careful when injecting the non-default datasources. For example, using this config:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="key">dataSources</span>: |
| <span class="key">dataSource</span>: |
| <span class="key">pooled</span>: <span class="predefined-constant">true</span> |
| <span class="key">jmxExport</span>: <span class="predefined-constant">true</span> |
| ..... |
| secondary: |
| <span class="key">pooled</span>: <span class="predefined-constant">true</span> |
| <span class="key">jmxExport</span>: <span class="predefined-constant">true</span> |
| .....</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>You can inject the primary <code>dataSource</code> like you would expect:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="java"><span class="type">class</span> <span class="class">BookSqlService</span> { |
| |
| def dataSource |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>But to inject the <code>secondary</code> datasource, you have to use Spring’s <code>Autowired</code> injection or <code>resources.groovy</code>.</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="java"><span class="type">class</span> <span class="class">BookSqlSecondaryService</span> { |
| |
| <span class="annotation">@Autowired</span> |
| <span class="annotation">@Qualifier</span>(<span class="string"><span class="delimiter">'</span><span class="content">dataSource_secondary</span><span class="delimiter">'</span></span>) |
| def dataSource2 |
| }</code></pre> |
| </div> |
| </div> |
| </div> |
| <div class="sect3"> |
| <h4 id="_dependency_injection_and_services">Dependency Injection and Services</h4> |
| <div class="paragraph"> |
| <p>You can inject services in other services with the same technique. If you had an <code>AuthorService</code> that needed to use the <code>BookService</code>, declaring the <code>AuthorService</code> as follows would allow that:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="type">class</span> <span class="class">AuthorService</span> { |
| <span class="keyword">def</span> bookService |
| }</code></pre> |
| </div> |
| </div> |
| </div> |
| <div class="sect3"> |
| <h4 id="_dependency_injection_and_domain_classes_tag_libraries">Dependency Injection and Domain Classes / Tag Libraries</h4> |
| <div class="paragraph"> |
| <p>You can even inject services into domain classes and tag libraries, which can aid in the development of rich domain models and views:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="type">class</span> <span class="class">Book</span> { |
| ... |
| def bookService |
| |
| <span class="keyword">def</span> <span class="function">buyBook</span>() { |
| bookService.buyBook(<span class="local-variable">this</span>) |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="admonitionblock warning"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <i class="fa icon-warning" title="Warning"></i> |
| </td> |
| <td class="content"> |
| Since Grails 3.2.8 this is not enabled by default. If you want to enable it again, take a look at <a href="https://grails.apache.org/docs/latest/ref/Domain%20Classes/Usage.html#_spring_autowiring_of_domain_instances">Spring Autowiring of Domain Instance</a> |
| </td> |
| </tr> |
| </table> |
| </div> |
| </div> |
| <div class="sect3"> |
| <h4 id="_service_bean_names">Service Bean Names</h4> |
| <div class="paragraph"> |
| <p>The default bean name which is associated with a service can be problematic if there are multiple services with the same name defined in different packages. For example consider the situation where an application defines a service class named <code>com.demo.ReportingService</code> and the application uses a plugin named <code>ReportingUtilities</code> and that plugin provides a service class named <code>com.reporting.util.ReportingService</code>.</p> |
| </div> |
| <div class="paragraph"> |
| <p>The default bean name for each of those would be <code>reportingService</code> so they would conflict with each other. Grails manages this by changing the default bean name for services provided by plugins by prefixing the bean name with the plugin name.</p> |
| </div> |
| <div class="paragraph"> |
| <p>In the scenario described above the <code>reportingService</code> bean would be an instance of the <code>com.demo.ReportingService</code> class defined in the application and the <code>reportingUtilitiesReportingService</code> bean would be an instance of the <code>com.reporting.util.ReportingService</code> class provided by the <code>ReportingUtilities</code> plugin.</p> |
| </div> |
| <div class="paragraph"> |
| <p>For all service beans provided by plugins, if there are no other services with the same name within the application or other plugins in the application then a bean alias will be created which does not include the plugin name and that alias points to the bean referred to by the name that does include the plugin name prefix.</p> |
| </div> |
| <div class="paragraph"> |
| <p>For example, if the <code>ReportingUtilities</code> plugin provides a service named <code>com.reporting.util.AuthorService</code> and there is no other <code>AuthorService</code> in the application or in any of the plugins that the application is using then there will be a bean named <code>reportingUtilitiesAuthorService</code> which is an instance of this <code>com.reporting.util.AuthorService</code> class and there will be a bean alias defined in the context named <code>authorService</code> which points to that same bean.</p> |
| </div> |
| </div> |
| |
| |
| <div style="clear:both;margin-top:15px;"></div> |
| |
| <div class="toc-item prev-left"><a href="../guide/validation.html"><< <strong>12</strong><span>Validation</span></a></div> |
| |
| <div class="toc-item next-right"><a href="../guide/staticTypeCheckingAndCompilation.html"><strong>14</strong><span>Static Type Checking And Compilation</span> >></a></div> |
| |
| <div style="clear:both"></div> |
| </div> |
| </td> |
| <td id="col2"> |
| <div class="local clearfix"> |
| <div class="local-title"> |
| <a href="../guide/index.html" target="mainFrame">Quick Reference</a> |
| <span class="toggle">(<a href="#" onclick="localToggle(); return false;">hide</a>)</span> |
| </div> |
| <div class="menu"> |
| |
| <div class="menu-block"><h1 class="menu-title" onclick="toggleRef(this.parentNode.childNodes[1])">Command Line</h1><div class="menu-sub"> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/Usage.html">Usage</a></div> |
| |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/bug-report.html">bug-report</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/clean.html">clean</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/compile.html">compile</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/console.html">console</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-app.html">create-app</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-command.html">create-command</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-controller.html">create-controller</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-domain-class.html">create-domain-class</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-functional-test.html">create-functional-test</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-hibernate-cfg-xml.html">create-hibernate-cfg-xml</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-integration-test.html">create-integration-test</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-interceptor.html">create-interceptor</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-plugin.html">create-plugin</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-profile.html">create-profile</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-script.html">create-script</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-service.html">create-service</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-taglib.html">create-taglib</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-unit-test.html">create-unit-test</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/dependency-report.html">dependency-report</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/docs.html">docs</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/generate-all.html">generate-all</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/generate-controller.html">generate-controller</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/generate-views.html">generate-views</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/help.html">help</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/install-templates.html">install-templates</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/list-plugins.html">list-plugins</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/list-profiles.html">list-profiles</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/package-plugin.html">package-plugin</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/package.html">package</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/plugin-info.html">plugin-info</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/profile-info.html">profile-info</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/run-app.html">run-app</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/run-command.html">run-command</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/run-script.html">run-script</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/schema-export.html">schema-export</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/shell.html">shell</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/stats.html">stats</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/stop-app.html">stop-app</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/test-app.html">test-app</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/war.html">war</a> |
| </div> |
| |
| </div> |
| </div> |
| |
| <div class="menu-block"><h1 class="menu-title" onclick="toggleRef(this.parentNode.childNodes[1])">Constraints</h1><div class="menu-sub"> |
| |
| <div class="menu-item"><a href="../ref/Constraints/Usage.html">Usage</a></div> |
| |
| |
| <div class="menu-item"><a href="../ref/Constraints/attributes.html">attributes</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/bindable.html">bindable</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/blank.html">blank</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/creditCard.html">creditCard</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/email.html">email</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/inList.html">inList</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/matches.html">matches</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/max.html">max</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/maxSize.html">maxSize</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/min.html">min</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/minSize.html">minSize</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/notEqual.html">notEqual</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/nullable.html">nullable</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/range.html">range</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/scale.html">scale</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/size.html">size</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/unique.html">unique</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/url.html">url</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/validator.html">validator</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/widget.html">widget</a> |
| </div> |
| |
| </div> |
| </div> |
| |
| <div class="menu-block"><h1 class="menu-title" onclick="toggleRef(this.parentNode.childNodes[1])">Controllers</h1><div class="menu-sub"> |
| |
| <div class="menu-item"><a href="../ref/Controllers/Usage.html">Usage</a></div> |
| |
| |
| <div class="menu-item"><a href="../ref/Controllers/actionName.html">actionName</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/allowedMethods.html">allowedMethods</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/bindData.html">bindData</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/chain.html">chain</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/controllerName.html">controllerName</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/defaultAction.html">defaultAction</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/errors.html">errors</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/flash.html">flash</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/forward.html">forward</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/grailsApplication.html">grailsApplication</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/hasErrors.html">hasErrors</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/header.html">header</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/namespace.html">namespace</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/params.html">params</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/redirect.html">redirect</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/render.html">render</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/request.html">request</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/respond.html">respond</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/response.html">response</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/responseFormats.html">responseFormats</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/scope.html">scope</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/servletContext.html">servletContext</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/session.html">session</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/withForm.html">withForm</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/withFormat.html">withFormat</a> |
| </div> |
| |
| </div> |
| </div> |
| |
| <div class="menu-block"><h1 class="menu-title" onclick="toggleRef(this.parentNode.childNodes[1])">Database Mapping</h1><div class="menu-sub"> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/Usage.html">Usage</a></div> |
| |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/autoImport.html">autoImport</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/autoTimestamp.html">autoTimestamp</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/batchSize.html">batchSize</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/cache.html">cache</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/cascade.html">cascade</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/column.html">column</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/comment.html">comment</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/discriminator.html">discriminator</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/dynamicInsert.html">dynamicInsert</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/dynamicUpdate.html">dynamicUpdate</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/fetch.html">fetch</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/id.html">id</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/ignoreNotFound.html">ignoreNotFound</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/indexColumn.html">indexColumn</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/insertable.html">insertable</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/joinTable.html">joinTable</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/lazy.html">lazy</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/order.html">order</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/sort.html">sort</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/table.html">table</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/type.html">type</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/updateable.html">updateable</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/version.html">version</a> |
| </div> |
| |
| </div> |
| </div> |
| |
| <div class="menu-block"><h1 class="menu-title" onclick="toggleRef(this.parentNode.childNodes[1])">Domain Classes</h1><div class="menu-sub"> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/Usage.html">Usage</a></div> |
| |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/addTo.html">addTo</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/attach.html">attach</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/belongsTo.html">belongsTo</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/clearErrors.html">clearErrors</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/constraints.html">constraints</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/count.html">count</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/countBy.html">countBy</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/createCriteria.html">createCriteria</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/delete.html">delete</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/discard.html">discard</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/embedded.html">embedded</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/errors.html">errors</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/executeQuery.html">executeQuery</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/executeUpdate.html">executeUpdate</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/exists.html">exists</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/fetchMode.html">fetchMode</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/find.html">find</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/findAll.html">findAll</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/findAllBy.html">findAllBy</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/findAllWhere.html">findAllWhere</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/findBy.html">findBy</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/findOrCreateBy.html">findOrCreateBy</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/findOrCreateWhere.html">findOrCreateWhere</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/findOrSaveBy.html">findOrSaveBy</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/findOrSaveWhere.html">findOrSaveWhere</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/findWhere.html">findWhere</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/first.html">first</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/get.html">get</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/getAll.html">getAll</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/getDirtyPropertyNames.html">getDirtyPropertyNames</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/getPersistentValue.html">getPersistentValue</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/hasErrors.html">hasErrors</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/hasMany.html">hasMany</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/hasOne.html">hasOne</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/ident.html">ident</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/instanceOf.html">instanceOf</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/isAttached.html">isAttached</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/isDirty.html">isDirty</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/last.html">last</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/list.html">list</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/listOrderBy.html">listOrderBy</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/load.html">load</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/lock.html">lock</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/mapWith.html">mapWith</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/mappedBy.html">mappedBy</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/mapping.html">mapping</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/merge.html">merge</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/namedQueries.html">namedQueries</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/properties.html">properties</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/read.html">read</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/refresh.html">refresh</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/removeFrom.html">removeFrom</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/save.html">save</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/transients.html">transients</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/validate.html">validate</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/where.html">where</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/whereAny.html">whereAny</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/withCriteria.html">withCriteria</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/withNewSession.html">withNewSession</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/withSession.html">withSession</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/withTransaction.html">withTransaction</a> |
| </div> |
| |
| </div> |
| </div> |
| |
| <div class="menu-block"><h1 class="menu-title" onclick="toggleRef(this.parentNode.childNodes[1])">Plug-ins</h1><div class="menu-sub"> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/Usage.html">Usage</a></div> |
| |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/URL%20mappings.html">URL mappings</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/codecs.html">codecs</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/controllers.html">controllers</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/core.html">core</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/dataSource.html">dataSource</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/domainClasses.html">domainClasses</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/hibernate.html">hibernate</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/i18n.html">i18n</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/logging.html">logging</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/scaffolding.html">scaffolding</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/services.html">services</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/servlets.html">servlets</a> |
| </div> |
| |
| </div> |
| </div> |
| |
| <div class="menu-block"><h1 class="menu-title" onclick="toggleRef(this.parentNode.childNodes[1])">Services</h1><div class="menu-sub"> |
| |
| <div class="menu-item"><a href="../ref/Services/Usage.html">Usage</a></div> |
| |
| |
| <div class="menu-item"><a href="../ref/Services/scope.html">scope</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Services/transactional.html">transactional</a> |
| </div> |
| |
| </div> |
| </div> |
| |
| <div class="menu-block"><h1 class="menu-title" onclick="toggleRef(this.parentNode.childNodes[1])">Servlet API</h1><div class="menu-sub"> |
| |
| |
| <div class="menu-item"><a href="../ref/Servlet%20API/request.html">request</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Servlet%20API/response.html">response</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Servlet%20API/servletContext.html">servletContext</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Servlet%20API/session.html">session</a> |
| </div> |
| |
| </div> |
| </div> |
| |
| </div> |
| </div> |
| </td> |
| </tr> |
| </table> |
| |
| <div id="footer"> |
| Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically. |
| |
| </div> |
| |
| <script type="text/javascript" src="../js/docs.js"></script> |
| |
| </body> |
| </html> |