blob: 7d852db10c212b50809444402a24f539a7ea8c20 [file] [log] [blame]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.3">
<link rel="apple-touch-icon" sizes="180x180" href="/img/favicon/apple-touch-icon-04cb17e028.png">
<link rel="icon" type="image/png" sizes="32x32" href="/img/favicon/favicon-32x32-12431ee8eb.png">
<link rel="icon" type="image/png" sizes="16x16" href="/img/favicon/favicon-16x16-4f316e4d55.png">
<link rel="manifest" href="/img/favicon/manifest-65e6aaa49e.json">
<link rel="mask-icon" href="/img/favicon/safari-pinned-tab-558c1991b1.svg" color="#dc5656">
<link rel="shortcut icon" href="/img/favicon/favicon-6cef91375b.ico">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="/img/favicon/mstile-144x144-34e7696278.png">
<meta name="msapplication-config" content="/img/favicon/browserconfig-82ff158058.xml">
<meta name="theme-color" content="#ffffff">
<link rel="stylesheet" href="https://cayenne.apache.org/css/styles-9ee2e6e330.css"/>
<script src="https://cayenne.apache.org/js/bundle-c0e6356367.js"></script>
<title>Cayenne Guide 5.0 · Customizing Cayenne Runtime &middot; Apache Cayenne</title>
</head>
<body class="cd-head">
<header class="page-header">
<nav id="topbar" class="bg-dark" aria-label="breadcrumb" role="navigation">
<ul class="breadcrumb breadcrumb-sm breadcrumb-dark container mb-0">
<li class="breadcrumb-item dropdown">
<a class="dropdown-toggle text-nowrap pr-1" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<img class="mw-15px mr-1" src="/img/feather-641aa69d09.svg" />Apache Software Foundation</a>
<div class="dropdown-menu rounded-0" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="https://www.apache.org">Apache Homepage</a>
<a class="dropdown-item" href="https://www.apache.org/licenses/">License</a>
<a class="dropdown-item" href="https://www.apache.org/foundation/sponsorship.html">Sponsorship</a>
<a class="dropdown-item" href="https://www.apache.org/foundation/thanks.html">Thanks</a>
<a class="dropdown-item" href="https://www.apache.org/security/">Security</a>
<a class="dropdown-item" href="https://privacy.apache.org/policies/privacy-policy-public.html">Privacy</a>
<a class="ml-1 mt-1 acevent" data-format="wide" data-mode="dark" data-width="120"></a>
</div>
</li>
</ul>
</nav>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="https://cayenne.apache.org/">
<img src="/img/logo_mono_full-d7a19eef61.svg" alt="Apache Cayenne" />
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#mainMenu" aria-controls="mainMenu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="mainMenu">
<ul class="navbar-nav mt-3 mt-lg-0 mr-auto">
<li class="nav-item">
<a class="nav-link" href="/download/">DOWNLOAD</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/docs/4.2/getting-started-guide/">DOCUMENTATION</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/about/support/">SUPPORT</a>
</li>
</ul>
<ul class="navbar-nav flex-row justify-content-center mt-2 mt-lg-0 mb-2 mb-lg-0 " id="social-links-menu">
<li class="nav-item d-flex">
<a class="nav-link d-flex justify-content-center align-items-center" href="https://github.com/apache/cayenne">
<img src="/img/icon_octocat_stars-c24dac94b8.svg" alt="GitHub" />
</a>
</li>
<li class="nav-item d-flex">
<a class="nav-link d-flex justify-content-center align-items-center" href="https://twitter.com/ApacheCayenne">
<img src="/img/icon_twitter-220a129d14.svg" alt="Twitter" />
</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<main>
<div class="cd-top-sidebar bb">
<div class="container">
<div class="row no-gutters">
<div class="col-12 col-lg-4 col-xl-3 br cd-sidebar1">
<ul class="nav" role="tablist">
<li class="nav-item dropdown mw-100">
<a class="nav-link dropdown-toggle text-truncate" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">
Cayenne Version 5.0
</a>
<div class="dropdown-menu">
<a class="dropdown-item" href="/docs/5.0/cayenne-guide/">Version 5.0 (Alpha)</a><a class="dropdown-item" href="/docs/4.2/getting-started-guide/">Version 4.2 (Stable)</a><a class="dropdown-item" href="/docs/4.1/getting-started-guide/">Version 4.1 (Stable)</a><a class="dropdown-item" href="/docs/4.0/getting-started-guide/">Version 4.0 (Aging)</a><a class="dropdown-item" href="/docs/3.1/getting-started-guide/">Version 3.1 (Legacy)</a>
</div>
</li>
</ul>
</div>
<div class="col-12 col-lg-8 col-xl-9"> </div>
</div>
</div>
</div>
<div class="container">
<div class="row no-gutters ">
<div class="col-12 col-lg-4 col-xl-3 br py-2 bg-gray-100 cd-sidebar">
<div class="tab-content" id="cd-docs-nav">
<div class="cd-toc-item">
<span class="cd-toc-link active">Cayenne Guide 5.0</span><div id="toc" class="toc toc-side">
<div id="toctitle">
Table of Contents
</div>
<ul class="sectlevel1 nav">
<li><a href="/docs/5.0/cayenne-guide/object-relational-mapping-with-cayenne" class="nav-link" id="object-relational-mapping-with-cayenne">1. Object Relational Mapping with Cayenne</a>
<ul class="sectlevel2 nav">
<li><a href="/docs/5.0/cayenne-guide/setup" class="nav-link" id="setup">1.1. Setup</a></li>
<li><a href="/docs/5.0/cayenne-guide/cayenne-mapping-structure" class="nav-link" id="cayenne-mapping-structure">1.2. Cayenne Mapping Structure</a></li>
<li><a href="/docs/5.0/cayenne-guide/cayenne-modeler" class="nav-link" id="cayenne-modeler">1.3. CayenneModeler Application</a></li>
</ul></li>
<li><a href="/docs/5.0/cayenne-guide/cayenne-framework" class="nav-link" id="cayenne-framework">2. Cayenne Framework</a>
<ul class="sectlevel2 nav">
<li><a href="/docs/5.0/cayenne-guide/including-cayenne-in-project" class="nav-link" id="including-cayenne-in-project">2.1. Including Cayenne in a Project</a></li>
<li><a href="/docs/5.0/cayenne-guide/starting-cayenne" class="nav-link" id="starting-cayenne">2.2. Starting Cayenne</a></li>
<li><a href="/docs/5.0/cayenne-guide/persistent-objects-objectcontext" class="nav-link" id="persistent-objects-objectcontext">2.3. Persistent Objects and ObjectContext</a></li>
<li><a href="/docs/5.0/cayenne-guide/expressions" class="nav-link" id="expressions">2.4. Expressions</a></li>
<li><a href="/docs/5.0/cayenne-guide/orderings" class="nav-link" id="orderings">2.5. Orderings</a></li>
<li><a href="/docs/5.0/cayenne-guide/queries" class="nav-link" id="queries">2.6. Queries</a></li>
<li><a href="/docs/5.0/cayenne-guide/lifecycle-events" class="nav-link" id="lifecycle-events">2.7. Lifecycle Events</a></li>
<li><a href="/docs/5.0/cayenne-guide/performance-tuning" class="nav-link" id="performance-tuning">2.8. Performance Tuning</a></li>
<li><a href="/docs/5.0/cayenne-guide/customizing-cayenne-runtime" class="nav-link" id="customizing-cayenne-runtime">2.9. Customizing Cayenne Runtime</a></li>
</ul></li>
<li><a href="/docs/5.0/cayenne-guide/db-first-flow" class="nav-link" id="db-first-flow">3. DB-First Flow</a>
<ul class="sectlevel2 nav">
<li><a href="/docs/5.0/cayenne-guide/re-introduction" class="nav-link" id="re-introduction">3.1. Introduction</a></li>
<li><a href="/docs/5.0/cayenne-guide/re-filtering" class="nav-link" id="re-filtering">3.2. Filtering</a></li>
<li><a href="/docs/5.0/cayenne-guide/re-relationships-loading-control" class="nav-link" id="re-relationships-loading-control">3.3. Other Settings</a></li>
<li><a href="/docs/5.0/cayenne-guide/re-modeler" class="nav-link" id="re-modeler">3.4. Reverse Engineering in Cayenne Modeler</a></li>
</ul></li>
<li><a href="/docs/5.0/cayenne-guide/additional-modules" class="nav-link" id="additional-modules">4. Additional Modules</a>
<ul class="sectlevel2 nav">
<li><a href="/docs/5.0/cayenne-guide/ext-cache-invalidation" class="nav-link" id="ext-cache-invalidation">4.1. Cache Invalidation Extension</a></li>
<li><a href="/docs/5.0/cayenne-guide/ext-commit-log" class="nav-link" id="ext-commit-log">4.2. Commit log extension</a></li>
<li><a href="/docs/5.0/cayenne-guide/ext-crypto" class="nav-link" id="ext-crypto">4.3. Crypto extension</a></li>
<li><a href="/docs/5.0/cayenne-guide/ext-jcache" class="nav-link" id="ext-jcache">4.4. JCache integration</a></li>
<li><a href="/docs/5.0/cayenne-guide/ext-project-compatibility" class="nav-link" id="ext-project-compatibility">4.5. Project compatibility extension</a></li>
<li><a href="/docs/5.0/cayenne-guide/ext-velocity" class="nav-link" id="ext-velocity">4.6. Apache Velocity Extension</a></li>
<li><a href="/docs/5.0/cayenne-guide/ext-osgi" class="nav-link" id="ext-osgi">4.7. Cayenne OSGI extension</a></li>
</ul></li>
<li><a href="/docs/5.0/cayenne-guide/build_tools" class="nav-link" id="build_tools">5. Build Tools</a>
<ul class="sectlevel2 nav">
<li><a href="/docs/5.0/cayenne-guide/maven_plugin" class="nav-link" id="maven_plugin">5.1. Maven Plugin</a></li>
<li><a href="/docs/5.0/cayenne-guide/gradle_plugin" class="nav-link" id="gradle_plugin">5.2. Gradle Plugin</a></li>
<li><a href="/docs/5.0/cayenne-guide/ant_tasks" class="nav-link" id="ant_tasks">5.3. Ant Tasks</a></li>
</ul></li>
<li><a href="/docs/5.0/cayenne-guide/appendix-a-configuration-properties" class="nav-link" id="appendix-a-configuration-properties">6. Appendix A. Configuration Properties</a></li>
<li><a href="/docs/5.0/cayenne-guide/appendix-b-service-collections" class="nav-link" id="appendix-b-service-collections">7. Appendix B. Service Collections</a></li>
</ul>
</div>
</div>
<div class="cd-toc-item">
</div>
</div>
</div>
<div class="col-12 col-lg-8 col-xl-9 py-3 pl-lg-5 cd-content">
<article>
<header>
</header>
<section>
<div class="sect2">
<h3 id="customizing-cayenne-runtime"><a class="anchor" href="#customizing-cayenne-runtime"></a>2.9. Customizing Cayenne Runtime</h3>
<div class="sect3">
<h4 id="dependency-injection-container"><a class="anchor" href="#dependency-injection-container"></a>2.9.1. Dependency Injection Container</h4>
<div class="paragraph">
<p>Cayenne runtime is built around a small powerful dependency injection (DI) container. Just like other popular DI technologies, such as Spring or Guice, Cayenne DI container manages sets of interdependent objects and allows users to configure them. These objects are regular Java objects. We are calling them "services" in this document to distinguish from all other objects that are not configured in the container and are not managed. DI container is responsible for service instantiation, injecting correct dependencies, maintaining service instances scope, and dispatching scope events to services.</p>
</div>
<div class="paragraph">
<p>The services are configured in special Java classes called "modules". Each module defines binding of service interfaces to implementation instances, implementation types or providers of implementation instances. There are no XML configuration files, and all the bindings are type-safe. The container supports injection into instance variables and constructor parameters based on the <code>@Inject</code> annotation. This mechanism is very close to Google Guice.</p>
</div>
<div class="paragraph">
<p>The discussion later in this chapter demonstrates a standalone DI container. But keep in mind that Cayenne already has a built-in Injector, and a set of default modules. A Cayenne user would normally only use the API below to write custom extension modules that will be loaded in that existing container when creating CayenneRuntime. See "Starting and Stopping CayenneRuntime" chapter for an example of passing an extension module to Cayenne.</p>
</div>
<div class="paragraph">
<p>Cayenne DI probably has ~80% of the features expected in a DI container and has no dependency on the rest of Cayenne, so in theory can be used as an application-wide DI engine. But its primary purpose is still to serve Cayenne. Hence there are no plans to expand it beyond Cayenne needs. It is an ideal "embedded" DI that does not interfere with Spring, Guice or any other such framework present elsewhere in the application.</p>
</div>
<div class="sect4">
<h5 id="di-bindings-api"><a class="anchor" href="#di-bindings-api"></a>2.9.1.1. DI Bindings API</h5>
<div class="paragraph">
<p>To have a working DI container, we need three things: service interfaces and classes, a module that describes service bindings, a container that loads the module, and resolves the dependencies. Let’s start with service interfaces and classes:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-Java Java" data-lang="Java">public interface Service1 {
String getString();
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-Java Java" data-lang="Java">public interface Service2 {
int getInt();
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>A service implementation using instance variable injection:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-Java Java" data-lang="Java">public class Service1Impl implements Service1 {
@Inject
private Service2 service2;
public String getString() {
return service2.getInt() + "_Service1Impl";
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Same thing, but using constructor injection:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-Java Java" data-lang="Java">public class Service1Impl implements Service1 {
private Service2 service2;
public Service1Impl(@Inject Service2 service2) {
this.service2 = service2;
}
public String getString() {
return service2.getInt() + "_Service1Impl";
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-Java Java" data-lang="Java">public class Service2Impl implements Service2 {
private int i;
public int getInt() {
return i++;
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Now let’s create a module implementing <code>org.apache.cayenne.tutorial.di.Module</code> interface that will contain DI configuration. A module binds service objects to keys that are reference. Binder provided by container implements fluent API to connect the key to implementation, and to configure various binding options (the options, such as scope, are demonstrated later in this chapter). The simplest form of a key is a Java Class object representing service interface. Here is a module that binds Service1 and Service2 to corresponding default implementations:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-Java Java" data-lang="Java">public class Module1 implements Module {
public void configure(Binder binder) {
binder.bind(Service1.class).to(Service1Impl.class);
binder.bind(Service2.class).to(Service2Impl.class);
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Once we have at least one module, we can create a DI container. <code>org.apache.cayenne.di.Injector</code> is the container class in Cayenne:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-Java Java" data-lang="Java">Injector injector = DIBootstrap.createInjector(new Module1());</code></pre>
</div>
</div>
<div class="paragraph">
<p>Now that we have created the container, we can obtain services from it and call their methods:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-Java Java" data-lang="Java">Service1 s1 = injector.getInstance(Service1.class);
for (int i = 0; i &lt; 5; i++) {
System.out.println("S1 String: " + s1.getString());
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>This outputs the following lines, demonstrating that s1 was Service1Impl and Service2 injected into it was Service2Impl:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>0_Service1Impl
1_Service1Impl
2_Service1Impl
3_Service1Impl
4_Service1Impl</code></pre>
</div>
</div>
<div class="paragraph">
<p>There are more flavors of bindings:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-Java Java" data-lang="Java">// binding to instance - allowing user to create and configure instance
// inside the module class
binder.bind(Service2.class).toInstance(new Service2Impl());
// binding to provider - delegating instance creation to a special
// provider class
binder.bind(Service1.class).toProvider(Service1Provider.class);
// binding to provider instance
binder.bind(Service1.class).toProviderInstance(new Service1Provider());
// multiple bindings of the same type using Key
// injection can reference the key name in annotation:
// @Inject("i1")
// private Service2 service2;
binder.bind(Key.get(Service2.class, "i1")).to(Service2Impl.class);
binder.bind(Key.get(Service2.class, "i2")).to(Service2Impl.class);</code></pre>
</div>
</div>
<div class="paragraph">
<p>Another types of configuration that can be bound in the container are lists and maps. They will be discussed in the following chapters.</p>
</div>
</div>
<div class="sect4">
<h5 id="service-lifecycle"><a class="anchor" href="#service-lifecycle"></a>2.9.1.2. Service Lifecycle</h5>
<div class="paragraph">
<p>An important feature of the Cayenne DI container is instance scope. The default scope (implicitly used in all examples above) is "singleton", meaning that a binding would result in creation of only one service instance, that will be repeatedly returned from <code>Injector.getInstance(..)</code>, as well as injected into classes that declare it as a dependency.</p>
</div>
<div class="paragraph">
<p>Singleton scope dispatches a "BeforeScopeEnd" event to interested services. This event occurs before the scope is shutdown, i.e. when <code>Injector.shutdown()</code> is called. Note that the built-in Cayenne injector is shutdown behind the scenes when <code>CayenneRuntime.shutdown()</code> is invoked. Services may register as listeners for this event by annotating a no-argument method with <code>@BeforeScopeEnd</code> annotation. Such method should be implemented if a service needs to clean up some resources, stop threads, etc.</p>
</div>
<div class="paragraph">
<p>Another useful scope is "no scope", meaning that every time a container is asked to provide a service instance for a given key, a new instance will be created and returned:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-Java Java" data-lang="Java">binder.bind(Service2.class).to(Service2Impl.class).withoutScope();</code></pre>
</div>
</div>
<div class="paragraph">
<p>Users can also create their own scopes, e.g. a web application request scope or a session scope. Most often than not custom scopes can be created as instances of <code>org.apache.cayenne.di.spi.DefaultScope</code> with startup and shutdown managed by the application (e.g. singleton scope is a DefaultScope managed by the Injector) .</p>
</div>
</div>
<div class="sect4">
<h5 id="overriding-services"><a class="anchor" href="#overriding-services"></a>2.9.1.3. Overriding Services</h5>
<div class="paragraph">
<p>Cayenne DI allows to override services already defined in the current module, or more commonly - some other module in the the same container. Actually there’s no special API to override a service, you’d just bind the service key again with a new implementation or provider. The last binding for a key takes precedence. This means that the order of modules is important when configuring a container. The built-in Cayenne injector ensures that Cayenne standard modules are loaded first, followed by optional user extension modules. This way the application can override the standard services in Cayenne.</p>
</div>
</div>
</div>
<div class="sect3">
<h4 id="customization-strategies"><a class="anchor" href="#customization-strategies"></a>2.9.2. Customization Strategies</h4>
<div class="paragraph">
<p>The previous section discussed how Cayenne DI works in general terms. Since Cayenne users will mostly be dealing with an existing Injector provided by CayenneRuntime, it is important to understand how to build custom extensions to a preconfigured container. As shown in "Starting and Stopping CayenneRuntime" chapter, custom extensions are done by writing an application DI module (or multiple modules) that configures service overrides. This section shows all the configuration possibilities in detail, including changing properties of the existing services, contributing services to standard service lists and maps, and overriding service implementations. All the code examples later in this section are assumed to be placed in an application module "configure" method:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-Java Java" data-lang="Java">public class MyExtensionsModule implements Module {
public void configure(Binder binder) {
// customizations go here...
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-Java Java" data-lang="Java">Module extensions = new MyExtensionsModule();
CayenneRuntime runtime = CayenneRuntime.builder()
.addConfig("com/example/cayenne-mydomain.xml")
.addModule(extensions)
.build();</code></pre>
</div>
</div>
<div class="sect4">
<h5 id="changing-properties-of-existing-services"><a class="anchor" href="#changing-properties-of-existing-services"></a>2.9.2.1. Changing Properties of Existing Services</h5>
<div class="paragraph">
<p>Many built-in Cayenne services change their behavior based on a value of some environment property. A user may change Cayenne behavior without even knowing which services are responsible for it, but setting a specific value of a known property. Supported property names are listed in "Appendix A".</p>
</div>
<div class="paragraph">
<p>There are two ways to set service properties. The most obvious one is to pass it to the JVM with -D flag on startup. E.g.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>$ java -Dcayenne.contexts_sync_strategy=false ...</code></pre>
</div>
</div>
<div class="paragraph">
<p>A second one is to contribute a property to <code>o.a.c.configuration.DefaultRuntimeProperties.properties</code> map (see the next section on how to do that). This map contains the default property values and can accept application-specific values, overriding the defaults.</p>
</div>
<div class="paragraph">
<p>Note that if a property value is a name of a Java class, when this Java class is instantiated by Cayenne, the container performs injection of instance variables. So even the dynamically specified Java classes can use @Inject annotation to get a hold of other Cayenne services.</p>
</div>
<div class="paragraph">
<p>If the same property is specified both in the command line and in the properties map, the command-line value takes precedence. The map value will be ignored. This way Cayenne runtime can be reconfigured during deployment.</p>
</div>
</div>
<div class="sect4">
<h5 id="contributing-to-service-collections"><a class="anchor" href="#contributing-to-service-collections"></a>2.9.2.2. Contributing to Service Collections</h5>
<div class="paragraph">
<p>Cayenne can be extended by adding custom objects to named maps or lists bound in DI. We are calling these lists/maps "service collections". A service collection allows things like appending a custom strategy to a list of built-in strategies. E.g. an application that needs to install a custom DbAdapter for some database type may contribute an instance of custom DbAdapterDetector to a <code>o.a.c.configuration.runtime.DefaultDbAdapterFactory.detectors</code> list:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-Java Java" data-lang="Java">public class MyDbAdapterDetector implements DbAdapterDetector {
public DbAdapter createAdapter(DatabaseMetaData md) throws SQLException {
// check if we support this database and return custom adapter
...
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-Java Java" data-lang="Java">CoreModule.extend(binder)
.addAdapterDetector(MyDbAdapterDetector.class);</code></pre>
</div>
</div>
<div class="paragraph">
<p>The names of built-in collections are listed in "Appendix B".</p>
</div>
</div>
<div class="sect4">
<h5 id="alternative-service-implementations"><a class="anchor" href="#alternative-service-implementations"></a>2.9.2.3. Alternative Service Implementations</h5>
<div class="paragraph">
<p>As mentioned above, custom modules are loaded by CayenneRuntime after the built-in modules. So it is easy to redefine a built-in service in Cayenne by rebinding desired implementations or providers. To do that, first we need to know what those services to redefine are. While we describe some of them in the following sections, the best way to get a full list is to check the source code of the Cayenne version you are using and namely look in <code>org.apache.cayenne.configuration.runtime.CoreModule</code> - the main built-in module in Cayenne.</p>
</div>
<div class="paragraph">
<p>Now an example of overriding <code>JdbcEventLogger</code> service. The default implementation of this service is provided by <code>Slf4jJdbcEventLogger</code>. But if we want to use <code>FormattedSlf4jJdbcEventLogger</code> (a logger with basic SQL formatting), we can define it like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-Java Java" data-lang="Java">binder.bind(JdbcEventLogger.class)
.to(FormattedSlf4jJdbcEventLogger.class);</code></pre>
</div>
</div>
</div>
</div>
<div class="sect3">
<h4 id="using-custom-data-types"><a class="anchor" href="#using-custom-data-types"></a>2.9.3. Using custom data types</h4>
<div class="sect4">
<h5 id="value-object-type"><a class="anchor" href="#value-object-type"></a>2.9.3.1. Value object type</h5>
<div class="paragraph">
<p><code>ValueObjectType</code> is a new and lightweight alternative to the Extended Types API described in the following section. In most cases is should be preferred as is it easier to understand and use. Currently only one case is known when <code>ExtendedType</code> should be used: when your value object can be mapped on different JDBC types.</p>
</div>
<div class="paragraph">
<p>In order to use your custom data type you should implement <code>ValueObjectType</code> describing it in terms of some type already known to Cayenne (e.g. backed by system or user ExtendedType). Let’s assume we want to support some data type called <code>Money</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-Java Java" data-lang="Java">public class Money {
private BigDecimal value;
public Money(BigDecimal value) {
this.value = value;
}
public BigDecimal getValue() {
return value;
}
// .. some other business logic ..
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Here is how <code>ValueObjectType</code> that will allow to store our <code>Money</code> class as <code>BigDecimal</code> can be implemented:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-Java Java" data-lang="Java">public class MoneyValueObjectType implements ValueObjectType&lt;Money, BigDecimal&gt; {
@Override
public Class&lt;BigDecimal&gt; getTargetType() {
return BigDecimal.class;
}
@Override
public Class&lt;Money&gt; getValueType() {
return Money.class;
}
@Override
public Money toJavaObject(BigDecimal value) {
return new Money(value);
}
@Override
public BigDecimal fromJavaObject(Money object) {
return object.getValue();
}
@Override
public String toCacheKey(Money object) {
return object.getValue().toString();
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Last step is to register this new type in <code>CayenneRuntime</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-Java Java" data-lang="Java">CayenneRuntime runtime = CayenneRuntime.builder()
.addConfig("cayenne-project.xml")
.addModule(binder -&gt;
CoreModule.extend(binder)
.addValueObjectType(MoneyValueObjectType.class))
.build();</code></pre>
</div>
</div>
<div class="paragraph">
<p>More examples of implementation you can find in <a href="https://github.com/apache/cayenne/blob/master/cayenne/src/main/java/org/apache/cayenne/access/types/LocalDateValueType.java">cayenne core</a>.</p>
</div>
</div>
<div class="sect4">
<h5 id="extended-types"><a class="anchor" href="#extended-types"></a>2.9.3.2. Extended Types</h5>
<div class="paragraph">
<p>JDBC specification defines a set of "standard" database column types (defined in java.sql.Types class) and a very specific mapping of these types to Java Object Types, such as java.lang.String, java.math.BigDecimal, etc. Sometimes there is a need to use a custom Java type not known to JDBC driver and Cayenne allows to configure it. For this Cayenne needs to know how to instantiate this type from a database "primitive" value, and conversely, how to transform an object of the custom type to a JDBC-compatible object.</p>
</div>
<div class="sect5">
<h6 id="supporting-non-standard-types"><a class="anchor" href="#supporting-non-standard-types"></a>Supporting Non-Standard Types</h6>
<div class="paragraph">
<p>For supporting non-standard type you should define it via an interface <code>org.apache.cayenne.access.types.ExtendedType</code>. An implementation must provide <code>ExtendedType.getClassName()</code> method that returns a fully qualified Java class name for the supported custom type, and a number of methods that convert data between JDBC and custom type. The following example demonstrates how to add a custom DoubleArrayType to store <code>java.lang.Double[]</code> as a custom string in a database:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-Java Java" data-lang="Java">/**
* Defines methods to read Java objects from JDBC ResultSets and write as parameters of
* PreparedStatements.
*/
public class DoubleArrayType implements ExtendedType {
private final String SEPARATOR = ",";
/**
* Returns a full name of Java class that this ExtendedType supports.
*/
@Override
public String getClassName() {
return Double[].class.getCanonicalName();
}
/**
* Initializes a single parameter of a PreparedStatement with object value.
*/
@Override
public void setJdbcObject(PreparedStatement statement, Object value,
int pos, int type, int scale) throws Exception {
String str = StringUtils.join((Double[]) value, SEPARATOR);
statement.setString(pos, str);
}
/**
* Reads an object from JDBC ResultSet column, converting it to class returned by
* 'getClassName' method.
*
* @throws Exception if read error occurred, or an object can't be converted to a
* target Java class.
*/
@Override
public Object materializeObject(ResultSet rs, int index, int type) throws Exception {
String[] str = rs.getString(index).split(SEPARATOR);
Double[] res = new Double[str.length];
for (int i = 0; i &lt; str.length; i++) {
res[i] = Double.valueOf(str[i]);
}
return res;
}
/**
* Reads an object from a stored procedure OUT parameter, converting it to class
* returned by 'getClassName' method.
*
* @throws Exception if read error ocurred, or an object can't be converted to a
* target Java class.
*/
@Override
public Object materializeObject(CallableStatement rs, int index, int type) throws Exception {
String[] str = rs.getString(index).split(SEPARATOR);
Double[] res = new Double[str.length];
for (int i = 0; i &lt; str.length; i++) {
res[i] = Double.valueOf(str[i]);
}
return res;
}
}</code></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-Java Java" data-lang="Java">// add DoubleArrayType to list of user types
CayenneRuntime runtime = CayenneRuntime.builder()
.addConfig("cayenne-project.xml")
.addModule(binder -&gt;
CoreModule.contributeUserTypes(binder)
.add(new DoubleArrayType()))
.build();</code></pre>
</div>
</div>
</div>
<div class="sect5">
<h6 id="dbadapters-and-extended-types"><a class="anchor" href="#dbadapters-and-extended-types"></a>DbAdapters and Extended Types</h6>
<div class="paragraph">
<p>As shown in the example above, ExtendedTypes are stored by DbAdapter. In fact DbAdapters often install their own extended types to address incompatibilities, incompleteness and differences between JDBC drivers in handling "standard" JDBC types. For instance some drivers support reading large character columns (CLOB) as java.sql.Clob, but some other - as "character stream", etc. Adapters provided with Cayenne override <code>configureExtendedTypes()</code> method to install their own types, possibly substituting Cayenne defaults. Custom DbAdapters can use the same technique.</p>
</div>
</div>
</div>
</div>
<div class="sect3">
<h4 id="noteworthy-built-in-services"><a class="anchor" href="#noteworthy-built-in-services"></a>2.9.4. Noteworthy Built-in Services</h4>
<div class="sect4">
<h5 id="jdbceventlogger"><a class="anchor" href="#jdbceventlogger"></a>2.9.4.1. JdbcEventLogger</h5>
<div class="paragraph">
<p><code>org.apache.cayenne.log.JdbcEventLogger</code> is the service that defines logging API for Cayenne internals. It provides facilities for logging queries, commits, transactions, etc. The default implementation is <code>org.apache.cayenne.log.Slf4jJdbcEventLogger</code> that performs logging via slf4j-api library. Cayenne library includes another potentially useful logger - <code>org.apache.cayenne.log.FormattedSlf4jJdbcEventLogger</code> that produces formatted multiline SQL output that can be easier to read.</p>
</div>
</div>
<div class="sect4">
<h5 id="datasourcefactory"><a class="anchor" href="#datasourcefactory"></a>2.9.4.2. DataSourceFactory</h5>
<div class="paragraph">
<p>Factory that returns <code>javax.sql.DataSource</code> object based on the configuration provided in the "nodeDescriptor".</p>
</div>
</div>
<div class="sect4">
<h5 id="datachannelsyncfilter-and-datachannelqueryfilter"><a class="anchor" href="#datachannelsyncfilter-and-datachannelqueryfilter"></a>2.9.4.3. DataChannelSyncFilter and DataChannelQueryFilter</h5>
<div class="paragraph">
<p>Interfaces of filters that allow to intercept DataChannel operations. Filters allow to implement chains of custom processors around a DataChannel, that can be used for security, monitoring, business logic, providing context to lifecycle event listeners, etc.</p>
</div>
</div>
<div class="sect4">
<h5 id="querycache"><a class="anchor" href="#querycache"></a>2.9.4.4. QueryCache</h5>
<div class="paragraph">
<p>Defines API of a cache that stores query results.</p>
</div>
</div>
</div>
</div>
</section>
<footer>
<div class="row">
<div class="col-6 col-md-3 text-center text-md-left">
&nbsp;
</div>
<div class="col-18 col-md-9 text-center text-md-right">
<a class="btn btn-link" href='/docs/5.0/cayenne-guide/db-first-flow/'>
<span class="d-block d-md-none text-muted">Next: </span>
Next: Cayenne Guide 5.0 · DB-First Flow
<i class="small fa fa-chevron-right ml-3l2 d-none d-md-inline"></i>
</a>
</div>
</div>
</footer>
</article>
<img referrerpolicy="no-referrer-when-downgrade" src="https://static.scarf.sh/a.png?x-pxid=5c836e39-be6b-4a21-a946-a97b5b69f172" />
</div>
</div>
</div>
</main>
<footer class="bg-dark">
<div class="footer-nav container text-center text-lg-left pb-3">
<div class="row pt-5 pb-3">
<div class="col-sm-6 col-lg-3">
<h4>About</h4>
<ul class="list-unstyled">
<li>
<a href="/why-cayenne.html">Why Cayenne?</a>
</li>
<li>
<a href="/download/">Download</a>
</li>
<li>
<a href="/success-stories.html">Success Stories</a>
</li>
<li>
<a href="/about/support/">Support</a>
</li>
</ul>
</div>
<div class="col-sm-6 col-lg-3">
<h4>Documentation</h4>
<ul class="list-unstyled">
<li>
<a href="/docs/4.0/getting-started-guide/">Getting Started (4.0)</a>
</li>
<li>
<a href="/docs/4.1/getting-started-guide/">Getting Started (4.1)</a>
</li>
<li>
<a href="/docs/4.2/getting-started-guide/">Getting Started (4.2)</a>
</li>
<li>
<a href="/docs/4.0/cayenne-guide/">Cayenne Guide (4.0)</a>
</li>
<li>
<a href="/docs/4.1/cayenne-guide/">Cayenne Guide (4.1)</a>
</li>
<li>
<a href="/docs/4.2/cayenne-guide/">Cayenne Guide (4.2)</a>
</li>
<li>
<a href="/docs/4.1/getting-started-db-first/">Database First tutorial (4.1)</a>
</li>
<li>
<a href="/docs/4.2/getting-started-db-first/">Database First tutorial (4.2)</a>
</li>
<li>
<a href="/legacy/legacy-docs/">Legacy Documentation</a>
</li>
</ul>
</div>
<div class="col-sm-6 col-lg-3">
<h4>Collaboration</h4>
<ul class="list-unstyled">
<li>
<a href="https://issues.apache.org/jira/browse/CAY">Bug/Feature Tracker</a>
</li>
<li>
<a href="/mailing-lists.html">Mailing Lists</a>
</li>
<li>
<a href="/dev/code-repository.html">Code Repository</a>
</li>
<li>
<a href="/dev/">Developer Guide</a>
</li>
<li>
<a href="/how-can-i-help.html">How can I help?</a>
</li>
<li>
<a href="/contributors.html">Contributors</a>
</li>
<li>
<a href="/thanks.html">Thanks</a>
</li>
</ul>
</div>
<div class="col-sm-6 col-lg-3">
<h4>News</h4>
<ul class="list-multiline-items list-unstyled mb-0">
<li>
<time datetime="2023-05-25 18:00:00 &#43;0300 &#43;0300" class="xsmall d-block">May 25, 2023</time>
<a href="/2023/05/cayenne-42-final-released/">Cayenne 4.2 Final Released</a>
</li>
<li>
<time datetime="2023-03-02 12:00:00 &#43;0300 &#43;0300" class="xsmall d-block">Mar 02, 2023</time>
<a href="/2023/03/cayenne-403-released/">Cayenne 4.0.3 Released</a>
</li>
<li>
<time datetime="2022-12-05 12:00:00 &#43;0300 &#43;0300" class="xsmall d-block">Dec 05, 2022</time>
<a href="/2022/12/cayenne-42rc2-released/">Cayenne 4.2 Release Candidate 2 Released</a>
</li>
</ul>
<a class="btn-link text-uppercase xsmall" href="https://cayenne.apache.org/news">
More news
<i class="fa fa-lg fa-long-arrow-right" aria-hidden="true"></i>
</a>
</div>
</div>
<hr class="mt-0 mb-3" />
<p class="copy xsmall text-center mw-75 mx-auto mb-0">
Copyright © 2001-2024 Apache Software Foundation. Apache Cayenne, Cayenne, Apache, the Apache feather logo, and the Apache Cayenne project logo are trademarks of The Apache Software Foundation.
<a href="https://privacy.apache.org/policies/privacy-policy-public.html">Privacy policy</a>.
<img class="d-block mx-auto mt-2" src="/img/logo_mono-3302daa3cf.svg" alt="Apache Cayenne" />
</p>
</div>
</footer>
</body>
</html>