baeldung 2018-10-14
1. 简介
Ebean 是一个用 Java 编写的对象关系映射(ORM)工具。
尽管它支持标准的 JPA 注解来声明实体,但它提供了一个更简洁的 API 来进行数据持久化。值得一提的是,Ebean 的架构是**无会话(sessionless)**的,因此它不会完全管理实体。此外,Ebean 允许我们使用原生 SQL 查询所有主流数据库,例如 Oracle、PostgreSQL、MySQL、H2 等。
接下来,我们将从创建和持久化数据开始,然后使用 Ebean 和 H2 数据库进行实体查询。
2. 环境配置
首先,我们需要在项目中添加必要的依赖项。
2.1 Maven 依赖
在 pom.xml 文件中添加以下依赖:
<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean</artifactId>
<version>13.25.2</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.1.214</version>
</dependency>
请确保使用 Ebean 和 H2 的最新版本。
2.2 字节码增强(Enhancements)
Ebean 需要对实体类进行字节码增强,以便服务器能够管理它们。为此,我们需要在 pom.xml 中添加 ebean-maven-plugin 插件:
<plugin>
<groupId>io.ebean</groupId>
<artifactId>ebean-maven-plugin</artifactId>
<version>13.25.2</version>
<executions>
<execution>
<id>main</id>
<phase>process-classes</phase>
<configuration>
<transformArgs>debug=1</transformArgs>
</configuration>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>
此外,我们还需要告诉 Maven 插件哪些包中包含实体类和使用事务的类。为此,创建一个名为 ebean.mf 的文件:
entity-packages: com.baeldung.ebean.model
transactional-packages: com.baeldung.ebean.app
2.3 日志配置
最后,创建 logback.xml 文件,并将某些包的日志级别设置为 TRACE,以便查看执行的 SQL 语句:
<logger name="io.ebean.DDL" level="TRACE"/>
<logger name="io.ebean.SQL" level="TRACE"/>
<logger name="io.ebean.TXN" level="TRACE"/>
3. 配置数据库服务器
接下来,我们将创建一个 Database 实例,用于保存实体或执行数据库查询。有两种方式可以创建服务器实例:使用默认属性文件或通过编程方式配置。下面分别介绍。
3.1 使用默认属性文件
Ebean 会自动查找名为 application.properties、ebean.properties 或 application.yml 的配置文件(支持 .properties 和 .yaml 格式)。
除了提供数据库连接信息外,还可以指示 Ebean 自动生成并执行 DDL 语句。
示例配置如下(ebean.properties):
ebean.db.ddl.generate=true
ebean.db.ddl.run=true
datasource.db.username=sa
datasource.db.password=
datasource.db.databaseUrl=jdbc:h2:mem:customer
datasource.db.databaseDriver=org.h2.Driver
3.2 使用 DatabaseConfig 编程配置
也可以通过 DatabaseFactory 和 DatabaseConfig 以编程方式创建相同的服务器:
DatabaseConfig cfg = new DatabaseConfig();
Properties properties = new Properties();
properties.put("ebean.db.ddl.generate", "true");
properties.put("ebean.db.ddl.run", "true");
properties.put("datasource.db.username", "sa");
properties.put("datasource.db.password", "");
properties.put("datasource.db.databaseUrl", "jdbc:h2:mem:app2");
properties.put("datasource.db.databaseDriver", "org.h2.Driver");
cfg.loadFromProperties(properties);
Database server = DatabaseFactory.create(cfg);
3.3 默认服务器实例
每个 Database 实例对应一个数据库。根据需求,可以创建多个 Database 实例。
如果只创建了一个服务器实例,Ebean 会自动将其注册为默认服务器实例,可以通过 DB 类的静态方法在应用任意位置访问:
Database server = DB.getDefault();
如果有多个数据库实例,也可以显式指定某个实例为默认服务器:
cfg.setDefaultServer(true);
4. 创建实体
Ebean 完全支持 JPA 注解,同时也提供了自己的注解以实现额外功能。
首先,我们创建一个 BaseModel 基类,包含所有实体共有的属性:
@MappedSuperclass
public abstract class BaseModel {
@Id
protected long id;
@Version
protected long version;
@WhenCreated
protected Instant createdOn;
@WhenModified
protected Instant modifiedOn;
// getters and setters
}
这里使用了 JPA 的 @MappedSuperclass 注解定义基类,并使用 Ebean 特有的 @WhenCreated 和 @WhenModified 注解(来自 io.ebean.annotation 包)实现自动审计功能。
接着,创建两个继承 BaseModel 的实体类:Customer 和 Address。
@Entity
public class Customer extends BaseModel {
public Customer(String name, Address address) {
super();
this.name = name;
this.address = address;
}
private String name;
@OneToOne(cascade = CascadeType.ALL)
Address address;
// getters and setters
}
@Entity
public class Address extends BaseModel {
public Address(String addressLine1, String addressLine2, String city) {
super();
this.addressLine1 = addressLine1;
this.addressLine2 = addressLine2;
this.city = city;
}
private String addressLine1;
private String addressLine2;
private String city;
// getters and setters
}
在 Customer 中,我们定义了与 Address 的一对一关系,并设置了 CascadeType.ALL,这样在保存或更新 Customer 时,关联的 Address 也会被自动处理。
5. 基本 CRUD 操作
前面我们已经完成了数据库配置和实体定义,现在来执行一些基本的增删改查(CRUD)操作。
我们将使用默认服务器实例来持久化和访问数据。DB 类也提供了静态方法来代理默认服务器的操作:
Address a1 = new Address("5, Wide Street", null, "New York");
Customer c1 = new Customer("John Wide", a1);
Database server = DB.getDefault();
server.save(c1);
c1.setName("Jane Wide");
c1.setAddress(null);
server.save(c1);
Customer foundC1 = DB.find(Customer.class, c1.getId());
DB.delete(foundC1);
- 首先创建
Customer对象,并通过save()方法保存。 - 然后修改客户信息并再次调用
save()进行更新。 - 使用
DB.find()根据 ID 查询客户。 - 最后调用
delete()删除该客户。
6. 查询
Ebean 提供了强大的查询 API,可用于构建带过滤条件和谓词的对象图。我们可以使用 Ebean 或 EbeanServer 来创建和执行查询。
下面是一个查询示例:按城市查找客户,并仅加载部分字段:
Customer customer = DB.find(Customer.class)
.select("name")
.fetch("address", "city")
.where()
.eq("city", "San Jose")
.findOne();
find(Customer.class):指定查询的实体类型。select("name"):仅加载Customer的name字段。fetch("address", "city"):同时加载关联的Address实体,并仅获取其city字段。where().eq("city", "San Jose"):添加查询条件。findOne():限制结果为单条记录。
7. 事务
Ebean 默认为每条语句或查询开启一个新事务。
但在某些场景下,我们希望将一组操作放在同一个事务中执行。此时,可以使用 @Transactional 注解(来自 io.ebean.annotations 包):
@Transactional
public static void insertAndDeleteInsideTransaction() {
Customer c1 = getCustomer();
Database server = DB.getDefault();
server.save(c1);
Customer foundC1 = server.find(Customer.class, c1.getId());
server.delete(foundC1);
}
该方法内的所有数据库操作将在同一个事务中执行。
8. 构建项目
最后,使用以下 Maven 命令构建项目并执行字节码增强:
mvn compile io.ebean:ebean-maven-plugin:enhance
9. 总结
本文介绍了 Ebean ORM 的基本功能,包括:
- 环境配置与依赖管理
- 实体定义(支持 JPA 与 Ebean 特有注解)
- CRUD 操作
- 高级查询(字段选择、关联加载、条件过滤)
- 事务控制
Ebean 凭借其简洁的 API、无会话设计以及对原生 SQL 的良好支持,成为 Java 应用中轻量级且高效的 ORM 选择。