CINXE.COM

Best practice | Docs | Ebean

<!doctype html> <html lang="en"> <head> <title>Best practice | Docs | Ebean</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <link rel="shortcut icon" href="/images/favicon.ico"> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto|Source+Sans+Pro|Ubuntu&display=swap"> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/all.css" integrity="sha384-lKuwvrZot6UHsBSfcMvOkWwlCMgc0TaWr+30HWe3a4ltaBwTZhyTEggF5tJv8tbt" crossorigin="anonymous"> <link rel="stylesheet" href="/css/reset3.css"> <link rel="stylesheet" href="/css/site3.css"> <link rel="stylesheet" href="/css/pygments3.css"> </head> <body> <div id="main"> <div id="banner"> <header> <nav id="top"> <h1 id="breadcrumb"> <a class="nav-logo" href="/"><img src="/images/logo-200.png" height="35"></a>&nbsp;&nbsp;<a href="/docs">Documentation</a><span class="sep">&nbsp;/&nbsp;</span><span class="last">Best Practice</span> </h1> <ul> <li><a onclick="toggleTheme();" title="switch dark light theme"><i class="fas fa-adjust"></i></a></li> </ul> </nav> </header> </div> <div class="grid grid-docs"> <aside> <nav class="side"> <ul> <li class="nav0 "> <a href="/docs/getting-started">Getting started</a> </li> <li class="nav0 "> <a href="/docs/intro">Introduction</a> </li> <li class="nav0 active"> <a class="active" href="/docs">Documentation</a> <ul> <li class="nav1 active"> <a class="active" href="/docs/best-practice">Best practice</a> <ul class="nav nav-scroll"> <li > <a href="#identity">Identity</a> </li> <li > <a href="#kotlin-data-class">Kotlin data classes</a> </li> <li > <a href="#list">List vs Set</a> </li> <li > <a href="#join-column">@JoinColumn</a> </li> <li > <a href="#column-name">@Column(name=...)</a> </li> <li > <a href="#mapped-superclass">@MappedSuperclass</a> </li> <li > <a href="#ddl-generation">DDL generation</a> </li> <li > <a href="#not-null">NOT NULL</a> </li> <li > <a href="#use-constructors">Use Constructors</a> </li> <li > <a href="#getters-setters">Getters Setters</a> </li> <li > <a href="#builder-pattern">Builder pattern</a> </li> <li > <a href="#bulk-update">Bulk update queries</a> </li> <li > <a href="#reference-beans">Reference beans</a> </li> <li > <a href="#naming-entities">Naming entity beans</a> </li> <li > <a href="#database-design-mindset">Database design mindset</a> </li> </ul> </li> <li class="nav1 "> <a href="/docs/query">Query</a> </li> <li class="nav1 "> <a href="/docs/persist">Persist</a> </li> <li class="nav1 "> <a href="/docs/transactions">Transactions</a> </li> <li class="nav1 "> <a href="/docs/mapping">Mapping</a> </li> <li class="nav1 "> <a href="/docs/ddl-generation">DDL & Migrations</a> </li> <li class="nav1 "> <a href="/docs/logging">Logging</a> </li> <li class="nav1 "> <a href="/docs/testing">Testing</a> </li> <li class="nav1 "> <a href="/docs/read-replicas">Read Replicas</a> </li> <li class="nav1 "> <a href="/docs/database">Database platforms</a> </li> <li class="nav1 "> <a href="/docs/multi-database">Multiple databases</a> </li> <li class="nav1 "> <a href="/docs/kotlin">Kotlin</a> </li> <li><a href="/docs/tuning">Tuning</a></li> <li class="nav1 "> <a href="/docs/features">Features</a> </li> </ul> </li> <li class="nav0 "> <a href="/support">Getting help</a> </li> <li class="nav0 "> <a target="_blank" href="/apidoc/13">API Javadoc</a> </li> <li class="nav0 "> <a href="/videos">Videos</a> </li> <li class="nav0 "> <a href="/docs/upgrading">Upgrading</a> </li> <li class="nav0 "> <a href="/releases">Releases</a> </li> </ul> </nav> </aside> <article> <form action="https://www.google.com/search" method="get" class="inline-form"> <input type="hidden" name="as_sitesearch" value="ebean.io"> <div id="page-search"> <div class="input-group"> <input class="frm" name="q" id="searchinput" type="text" placeholder="Search... (press 's' to focus)" data-placeholder-focus="Search... (use '↑', '↓' and '⏎' to select results)" data-placeholder-blur="Search... (press 's' to focus)" autocomplete="off"> <div class="input-group-btn"> <button class="frm" type="submit"><i class="fas fa-search"></i></button> </div> </div> <div id="page-search-results" style="display: none;"> <ul id="search-results-container" class="search-results"><li class=" active"><a href="/docs" title="Docs"><span style="color:#777;">Docs</span> Documentation </a></li><li class=""><small style="color:#999;">And 101 more...</small></li></ul> </div> </div> </form> <h2 id="identity">Identity - equals/hashCode</h2> <p> Do not implement <code>equals()</code> and <code>hashCode()</code> on <code>@Entity</code> beans (but instead leave that to Ebean enhancement). </p> <p> Ebean will enhance entity beans with an optimal implementation of <code>equals()</code> and <code>hashCode()</code> and we should not try and do better and instead just leave it to Ebean. </p> <h2 id="toString">toString() - avoid getters</h2> <p> Avoid using getter methods in <code>toString()</code>. We want to avoid invoking any accidental lazy loading when using the debugger. That is, using the debugger and inspecting entity beans in the debugger will implicitly call toString() methods on entity beans. We don't want to have different behaviour when stepping through with a debugger. Ebean does not enhance toString() methods but if the toString() implementation uses getter methods then those methods could invoke lazy loading and we generally want to avoid that. </p> <h2 id="kotlin-data-class">Kotlin data classes</h2> <p> Do not use Kotlin data classes for <code>@Entity</code> beans as the equals/hashCode implementation is not desirable. Instead use normal Kotlin classes for entity beans. </p> <p> Do use Kotlin data classes for <code>@EmbeddedId</code> beans. </p> <h2 id="list">Prefer List over Set</h2> <p> For <code>@OneToMany</code> and <code>@ManyToMany</code> collections prefer the use of List over Set. The use of Set will implicitly calls <code>equals()</code> and <code>hashCode()</code> and generally it's preferable to not call those methods until entity beans have Id values. </p> <h2 id="join-column">@JoinColumn</h2> <p> Don't use <code>@JoinColumn</code> or <code>@JoinTable</code> unless we have to. The naming convention will provide good names of foreign key columns and join tables so only use these annotations when there are existing foreign keys etc and these do not match the naming convention. </p> <h2 id="column-name">@Column(name=...)</h2> <p> Do not use <code>@Column(name=...)</code> to map database column names but instead use the naming convention. Only specify explicit <code>@Column(name=...)</code> when it for some reason does not match the naming convention used. </p> <ul class="code nav nav-tabs mytabs"> <li role="presentation" class="javaActive active"><a onclick="setLang('java');">java</a></li> <li role="presentation" class="kotlinActive"><a onclick="setLang('kt');">kotlin</a></li> </ul> <div class="code-java"> <div class="syntax java"><div class="highlight"><pre><span></span><span class="nd">@Column</span><span class="o">(</span><span class="nx">name</span><span class="o">=</span><span class="s">&quot;when_activated&quot;</span><span class="o">)</span> <span class="c1">// This is redundant, just adds &quot;annotation noise&quot;</span> <span class="n">OffsetDateTime</span> <span class="n">whenActivated</span><span class="o">;</span> </pre></div> </div> </div> <div class="code-kt"> <div class="syntax kotlin"><div class="highlight"><pre><span></span><span class="nd">@Column</span><span class="p">(</span><span class="nx">name</span><span class="p">=</span><span class="s">&quot;when_activated&quot;</span><span class="p">)</span> <span class="c1">// This is redundant, just adds &quot;annotation noise&quot;</span> <span class="k">val</span> <span class="py">whenActivated</span><span class="p">:</span> <span class="n">OffsetDateTime</span><span class="p">?</span> <span class="p">=</span> <span class="k">null</span> </pre></div> </div> </div> <h2 id="mapped-superclass">Use @MappedSuperclass</h2> <p> Make use of <code>@MappedSuperclass</code> to hold common properties. A common mapped superclass might have: </p> <ul class="code nav nav-tabs mytabs"> <li role="presentation" class="javaActive active"><a onclick="setLang('java');">java</a></li> <li role="presentation" class="kotlinActive"><a onclick="setLang('kt');">kotlin</a></li> </ul> <div class="code-java"> <div class="syntax java"><div class="highlight"><pre><span></span><span class="o">...</span> <span class="nd">@MappedSuperclass</span> <span class="kd">public</span> <span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">BaseDomain</span> <span class="kd">extends</span> <span class="n">Model</span> <span class="o">{</span> <span class="nd">@Id</span> <span class="kt">long</span> <span class="n">id</span><span class="o">;</span> <span class="nd">@Version</span> <span class="kt">long</span> <span class="n">version</span><span class="o">;</span> <span class="nd">@WhenCreated</span> <span class="n">Instant</span> <span class="n">whenCreated</span><span class="o">;</span> <span class="nd">@WhenModified</span> <span class="n">Instant</span> <span class="n">whenModified</span><span class="o">;</span> <span class="c1">// getters and setters</span> <span class="o">...</span> <span class="o">}</span> </pre></div> </div> </div> <div class="code-kt"> <div class="syntax kotlin"><div class="highlight"><pre><span></span><span class="p">...</span> <span class="nd">@MappedSuperclass</span> <span class="k">open</span> <span class="k">class</span> <span class="nc">BaseDomain</span> <span class="p">:</span> <span class="n">Model</span><span class="p">()</span> <span class="p">{</span> <span class="nd">@Id</span> <span class="k">var</span> <span class="py">id</span><span class="p">:</span> <span class="n">Long</span> <span class="p">=</span> <span class="m">0</span> <span class="nd">@Version</span> <span class="k">var</span> <span class="py">version</span><span class="p">:</span> <span class="n">Long</span> <span class="p">=</span> <span class="m">0</span> <span class="nd">@WhenModified</span> <span class="k">lateinit</span> <span class="k">var</span> <span class="py">whenModified</span><span class="p">:</span> <span class="n">Instant</span> <span class="nd">@WhenCreated</span> <span class="k">lateinit</span> <span class="k">var</span> <span class="py">whenCreated</span><span class="p">:</span> <span class="n">Instant</span> <span class="p">}</span> </pre></div> </div> </div> <h2 id="ddl-generation">DDL Generation</h2> <p> Use Ebean to generate all DDL including DB migrations - aka prefer forward generation of DDL. This keeps all the DDL "in sync" for testing and db migrations and makes it easier to support multiple database platforms or migrate between database platforms (e.g. MySql to Postgres). </p> <p> If we hand craft DDL we increase the chance of variation between the model and actual database schema and can make testing harder. </p> <h2 id="not-null">Promote use of NOT NULL constraint</h2> <p> Make as much of the model NOT NULL as we can - prefer DB columns to have the NOT NULL constraint if possible. Reduce the amount of 3 valued logic required - have a "tighter" model. </p> <h2 id="use-constructors">Use Constructors</h2> <p> Use constructors to help enforce non nullable properties (Kotlin) or promote non nullable properties (Java). </p> <p> For example, if a Customer should always have a name define a constructor that takes the name property. </p> <ul class="code nav nav-tabs mytabs"> <li role="presentation" class="javaActive active"><a onclick="setLang('java');">java</a></li> <li role="presentation" class="kotlinActive"><a onclick="setLang('kt');">kotlin</a></li> </ul> <div class="code-java"> <div class="syntax java"><div class="highlight"><pre><span></span><span class="nd">@Entity</span> <span class="kd">public</span> <span class="kd">class</span> <span class="nc">Customer</span> <span class="kd">extends</span> <span class="n">BaseModel</span> <span class="o">{</span> <span class="nd">@NotNull</span> <span class="nd">@Length</span><span class="o">(</span><span class="mi">100</span><span class="o">)</span> <span class="kd">private</span> <span class="n">String</span> <span class="n">name</span><span class="o">;</span> <span class="o">...</span> <span class="kd">public</span> <span class="nf">Customer</span><span class="o">(</span><span class="n">String</span> <span class="n">name</span><span class="o">)</span> <span class="o">{</span> <span class="k">this</span><span class="o">.</span><span class="na">name</span> <span class="o">=</span> <span class="n">name</span><span class="o">;</span> <span class="o">}</span> <span class="c1">// getters and setters</span> <span class="o">}</span> </pre></div> </div> </div> <div class="code-kt"> <div class="syntax kotlin"><div class="highlight"><pre><span></span><span class="p">...</span> <span class="nd">@Entity</span> <span class="k">class</span> <span class="nc">Customer</span><span class="p">(</span><span class="n">name</span> <span class="p">:</span> <span class="n">String</span><span class="p">)</span> <span class="p">:</span> <span class="n">BaseModel</span><span class="p">()</span> <span class="p">{</span> <span class="nd">@Length</span><span class="p">(</span><span class="m">100</span><span class="p">)</span> <span class="k">var</span> <span class="py">name</span><span class="p">:</span> <span class="n">String</span> <span class="p">=</span> <span class="n">name</span> <span class="c1">// Ebean knows this is Kotlin non-nullable type</span> <span class="p">}</span> </pre></div> </div> </div> <p> Now when we create a new Customer we must create it with a name. </p> <p class="code-kt"> For Kotlin we should make <em>name</em> a <code>non-nullable type</code>. Ebean will treat Kotlin non-nullable types as <em>NOT NULL</em> from a database perspective as well giving us a tighter model. </p> <h2 id="getters-setters">Getters Setters</h2> <p> Ebean does NOT need getters and setters as it adds it's own accessor methods via enhancement. </p> <p> We can omit getter and setter methods as desired. We can have setter methods follow a fluid style returning this if desired. </p> <h2 id="builder-pattern">Builder pattern</h2> <p> If we want to use the Builder pattern rather than generate an additional builder class for a given entity (that then duplicates all the properties of the entity making things harder to maintain) a simpler approach is to have the "setter" methods on the entity bean use the fluid style and return this. </p> <div class="syntax java"><div class="highlight"><pre><span></span><span class="nd">@Entity</span> <span class="kd">public</span> <span class="kd">class</span> <span class="nc">Customer</span> <span class="kd">extends</span> <span class="n">BaseModel</span> <span class="o">{</span> <span class="nd">@NotNull</span> <span class="nd">@Length</span><span class="o">(</span><span class="mi">100</span><span class="o">)</span> <span class="kd">private</span> <span class="n">String</span> <span class="n">name</span><span class="o">;</span> <span class="kd">private</span> <span class="n">String</span> <span class="n">notes</span><span class="o">;</span> <span class="kd">private</span> <span class="kt">int</span> <span class="n">level</span><span class="o">;</span> <span class="o">...</span> <span class="kd">public</span> <span class="nf">Customer</span><span class="o">(</span><span class="n">String</span> <span class="n">name</span><span class="o">)</span> <span class="o">{</span> <span class="k">this</span><span class="o">.</span><span class="na">name</span> <span class="o">=</span> <span class="n">name</span><span class="o">;</span> <span class="o">}</span> <span class="c1">// accessors</span> <span class="kd">public</span> <span class="n">Customer</span> <span class="nf">setNotes</span><span class="o">(</span><span class="n">String</span> <span class="n">notes</span><span class="o">)</span> <span class="o">{</span> <span class="c1">// fluid style</span> <span class="k">this</span><span class="o">.</span><span class="na">notes</span> <span class="o">=</span> <span class="n">notes</span><span class="o">;</span> <span class="k">return</span> <span class="k">this</span><span class="o">;</span> <span class="o">}</span> <span class="kd">public</span> <span class="n">Customer</span> <span class="nf">setLevel</span><span class="o">(</span><span class="kt">int</span> <span class="n">level</span><span class="o">)</span> <span class="o">{</span> <span class="c1">// fluid style</span> <span class="k">this</span><span class="o">.</span><span class="na">level</span> <span class="o">=</span> <span class="n">level</span><span class="o">;</span> <span class="k">return</span> <span class="k">this</span><span class="o">;</span> <span class="o">}</span> <span class="o">...</span> <span class="o">}</span> <span class="c1">// using fluid style</span> <span class="n">Customer</span> <span class="n">customer</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Customer</span><span class="o">(</span><span class="s">&quot;Roberto&quot;</span><span class="o">)</span> <span class="o">.</span><span class="na">setNotes</span><span class="o">(</span><span class="s">&quot;An example&quot;</span><span class="o">)</span> <span class="o">.</span><span class="na">setLevel</span><span class="o">(</span><span class="mi">42</span><span class="o">);</span> </pre></div> </div> <h2 id="bulk-update">Bulk update queries</h2> <p> Prefer the use of <a href="/docs/query/update">bulk update</a> and <a href="/docs/query/delete">delete</a> statements where appropriate. Avoid fetching beans just to iterate and delete them or iterate and update them all in the same way. </p> <h2 id="reference-beans">Reference beans</h2> <p> When we have the id value, use a reference bean rather than execute a query to find by id - unless we really want to query the database of course. </p> <p> That is, for inserts and updates when we have the <code>@Id</code> value we can use reference bean for the <em>foreign key value</em>> rather than execute an extra query against the database. </p> <h4>Do <b>NOT</b> do this - as it executes an extra database query</h4> <ul class="code nav nav-tabs mytabs"> <li role="presentation" class="javaActive active"><a onclick="setLang('java');">java</a></li> <li role="presentation" class="kotlinActive"><a onclick="setLang('kt');">kotlin</a></li> </ul> <div class="code-java"> <div class="syntax java"><div class="highlight"><pre><span></span><span class="n">Order</span> <span class="n">order</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Order</span><span class="o">()</span> <span class="n">order</span><span class="o">.</span><span class="na">setCustomer</span><span class="o">(</span><span class="n">database</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="n">Customer</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="mi">42</span><span class="o">));</span> <span class="c1">// extra db query for customer</span> <span class="o">...</span> <span class="n">database</span><span class="o">.</span><span class="na">save</span><span class="o">(</span><span class="n">order</span><span class="o">);</span> </pre></div> </div> </div> <div class="code-kt"> <div class="syntax kotlin"><div class="highlight"><pre><span></span><span class="k">val</span> <span class="py">order</span> <span class="p">=</span> <span class="n">Order</span><span class="p">()</span> <span class="n">order</span><span class="p">.</span><span class="n">setCustomer</span><span class="p">(</span><span class="n">database</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="n">Customer</span><span class="p">.</span><span class="k">class</span><span class="p">,</span> <span class="m">42</span><span class="p">));</span> <span class="p">...</span> <span class="n">database</span><span class="p">.</span><span class="n">save</span><span class="p">(</span><span class="n">order</span><span class="p">)</span> </pre></div> </div> </div> <h4>Do <b>THIS</b> - use reference bean instead</h4> <ul class="code nav nav-tabs mytabs"> <li role="presentation" class="javaActive active"><a onclick="setLang('java');">java</a></li> <li role="presentation" class="kotlinActive"><a onclick="setLang('kt');">kotlin</a></li> </ul> <div class="code-java"> <div class="syntax java"><div class="highlight"><pre><span></span><span class="n">Order</span> <span class="n">order</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Order</span><span class="o">()</span> <span class="n">order</span><span class="o">.</span><span class="na">setCustomer</span><span class="o">(</span><span class="n">database</span><span class="o">.</span><span class="na">getReference</span><span class="o">(</span><span class="n">Customer</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="mi">42</span><span class="o">));</span> <span class="c1">// no extra db query</span> <span class="o">...</span> <span class="n">database</span><span class="o">.</span><span class="na">save</span><span class="o">(</span><span class="n">order</span><span class="o">);</span> </pre></div> </div> </div> <div class="code-kt"> <div class="syntax kotlin"><div class="highlight"><pre><span></span><span class="k">val</span> <span class="py">order</span> <span class="p">=</span> <span class="n">Order</span><span class="p">()</span> <span class="n">order</span><span class="p">.</span><span class="n">setCustomer</span><span class="p">(</span><span class="n">database</span><span class="p">.</span><span class="n">getReference</span><span class="p">(</span><span class="n">Customer</span><span class="p">.</span><span class="k">class</span><span class="p">,</span> <span class="m">42</span><span class="p">));</span> <span class="p">...</span> <span class="n">database</span><span class="p">.</span><span class="n">save</span><span class="p">(</span><span class="n">order</span><span class="p">)</span> </pre></div> </div> </div> <h2 id="naming-entities">Naming entity beans</h2> <p> As a "Rob Preference" rather than strictly a "best practice" I have found that I often prefer to give entity beans a <code>D</code> prefix (Hungarian notation) like: </p> <ul> <li>DCustomer instead of Customer</li> <li>DProduct instead of Product</li> <li>DOrder instead of Order </li> </ul> <p> Entity beans are generally considered <em>internal</em> and not publicly exposed. Entity bean names often match/clash with types/names that we want to use in the public API and we frequently want to map to/from the publicly exposed API types and our internal entity beans. </p> <p> Giving the entity beans a <em>D</em> prefix (D for Domain) generally: </p> <ul> <li>Avoids name clashes with public API types</li> <li>Avoids needing full package qualified types in mappers</li> <li>Can be more obvious when code is using <em>internal</em> entity beans (persistence domain objects)</li> </ul> <p> Generally entity beans are in a <code>domain</code> package. The beans being generally related to each other via <em>@OneToMany</em>, <em>@ManyToOne</em> etc means that I prefer to keep them together as a holistic model. </p> <h2 id="database-design-mindset">Database design mindset</h2> <p> When we are building / modelling entity beans I strongly subscribe to being in a "database design mindset". What this means is that we are primarily focused on normalisation and good database design principals with a resulting database design that we are happy will last for the long term and be good regardless of any ORM or persistence layer we are using to interact with the database. </p> <p> Design for the long term. </p> <nav class="next"> <p class="edit-page"> <a href="https://github.com/ebean-orm/website-source/blob/master/docs/best-practice/index.html"><i class="fab fa-github"></i> Edit Page</a> </p> <p class="next"> <a href="/docs/query" class="btn btn-info">Next: Queries</a> </p> </nav> </article> </div> </div> <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script> <script src="/js/site3.js"></script> <script src="/js/search3.js"></script> <script async src="https://www.googletagmanager.com/gtag/js?id=UA-75181644-1"></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'UA-75181644-1'); </script> </body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10