文章目录
【Java设计模式】元数据映射模式
一、概述
元数据映射设计模式旨在以一种使数据库模式和对象模型解耦且易于管理的方式,管理数据库记录与Java对象之间的映射。
二、详细解释及实际示例
- 实际示例:
- 在线零售系统是元数据映射设计模式的一个类似的现实世界示例。在这样的系统中,产品通常根据其类别具有不同的属性。例如,电子产品可能具有电池寿命和屏幕尺寸等属性,而服装可能具有尺寸和面料类型等属性。使用元数据映射,系统可以动态地将这些不同的属性映射到产品对象,而无需修改底层类结构。这种灵活性允许在引入新的类别和属性时轻松更新和管理产品属性,确保系统能够随着不断变化的产品格局而发展。
- 通俗解释:
- 元数据映射指定了类和表之间的映射,以便我们可以将任何数据库的表视为Java类。
- 维基百科解释:
- 创建一个“虚拟对象数据库”,可以在编程语言中使用。
三、Java中元数据映射模式的编程示例
Hibernate ORM工具使用元数据映射模式来指定类和表之间的映射,无论是使用XML还是在代码中使用注释。
我们给出一个关于访问h2
数据库中user_account
表信息的示例。首先,我们使用h2
创建user_account
表:
@Slf4j
public class DatabaseUtil {
private static final String DB_URL = "jdbc:h2:mem:metamapping";
private static final String CREATE_SCHEMA_SQL = "DROP TABLE IF EXISTS `user_account`;"
+ "CREATE TABLE `user_account` (\n"
+ " `id` int(11) NOT NULL AUTO_INCREMENT,\n"
+ " `username` varchar(255) NOT NULL,\n"
+ " `password` varchar(255) NOT NULL,\n"
+ " PRIMARY KEY (`id`)\n"
+ ");";
static {
LOGGER.info("创建h2数据库");
var source = new JdbcDataSource();
source.setURL(DB_URL);
try (var statement = source.getConnection().createStatement()) {
statement.execute(CREATE_SCHEMA_SQL);
} catch (SQLException e) {
LOGGER.error("无法创建h2数据源", e);
}
}
}
相应地,这里是基本的User
实体。
@Setter
@Getter
@ToString
public class User {
private Integer id;
private String username;
private String password;
public User(String username, String password) {
this.username = username;
this.password = password;
}
}
然后,我们编写一个xml
文件来展示表和对象之间的映射:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.iluwatar.metamapping.model.User" table="user_account">
<id name="id" type="java.lang.Integer" column="id">
<generator class="native"/>
</id>
<property name="username" column="username" type="java.lang.String"/>
<property name="password" column="password" type="java.lang.String"/>
</class>
</hibernate-mapping>
我们使用Hibernate
来解析映射并连接到我们的数据库,这里是它的配置:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- JDBC数据库连接设置 -->
<property name="connection.url">jdbc:h2:mem:metamapping</property>
<property name="connection.driver_class">org.h2.Driver</property>
<!-- JDBC连接池设置...使用内置测试池 -->
<property name="connection.pool_size">1</property>
<!-- 选择我们的SQL方言 -->
<property name="dialect">org.hibernate.dialect.H2Dialect</property>
<!-- 将SQL输出到控制台 -->
<property name="show_sql">false</property>
<!-- 在启动时删除并重新创建数据库模式 -->
<property name="hbm2ddl.auto">create-drop</property>
<mapping resource="com/iluwatar/metamapping/model/User.hbm.xml" />
</session-factory>
</hibernate-configuration>
然后,我们可以使用Hibernate
像访问对象一样访问表,这里是一些CRUD操作:
@Slf4j
public class UserService {
private static final SessionFactory factory = HibernateUtil.getSessionFactory();
public List<User> listUser() {
LOGGER.info("列出所有用户。");
List<User> users = new ArrayList<>();
try (var session = factory.openSession()) {
var tx = session.beginTransaction();
List<User> userIter = session.createQuery("FROM User").list();
for (var iterator = userIter.iterator(); iterator.hasNext();) {
users.add(iterator.next());
}
tx.commit();
} catch (HibernateException e) {
LOGGER.debug("获取用户失败", e);
}
return users;
}
// 其他CRUD操作 ->
//...
public void close() {
HibernateUtil.shutdown();
}
}
这里是我们的App
类,带有main
函数用于运行示例。
@Slf4j
public class App {
public static void main(String[] args) {
// 获取服务
var userService = new UserService();
// 使用创建服务添加用户
for (var user : generateSampleUsers()) {
var id = userService.createUser(user);
LOGGER.info("在" + id + "处添加用户" + user + "。");
}
// 使用列表服务获取用户
var users = userService.listUser();
LOGGER.info(String.valueOf(users));
// 使用获取服务获取一个用户
var user = userService.getUser(1);
LOGGER.info(String.valueOf(user));
// 更改用户1的密码
user.setPassword("new123");
// 使用更新服务更新用户1
userService.updateUser(1, user);
// 使用删除服务删除用户2
userService.deleteUser(2);
// 关闭服务
userService.close();
}
public static List<User> generateSampleUsers() {
final var user1 = new User("ZhangSan", "zhs123");
final var user2 = new User("LiSi", "ls123");
final var user3 = new User("WangWu", "ww123");
return List.of(user1, user2, user3);
}
}
控制台输出:
14:44:17.792 [main] INFO org.hibernate.Version - HHH000412: Hibernate ORM核心版本 5.6.12.Final
14:44:17.977 [main] INFO o.h.annotations.common.Version - HCANN000001: Hibernate Commons注释 {5.1.2.Final}
14:44:18.216 [main] WARN o.hibernate.orm.connections.pooling - HHH10001002: 使用Hibernate内置连接池(不用于生产用途!)
14:44:18.217 [main] INFO o.hibernate.orm.connections.pooling - HHH10001005: 使用驱动程序 [org.h2.Driver] 在 URL [jdbc:h2:mem:metamapping]
14:44:18.217 [main] INFO o.hibernate.orm.connections.pooling - HHH10001001: 连接属性: {}
14:44:18.217 [main] INFO o.hibernate.orm.connections.pooling - HHH10001003: 自动提交模式: false
14:44:18.219 [main] INFO o.h.e.j.c.i.DriverManagerConnectionProviderImpl - HHH000115: Hibernate连接池大小: 1 (min=1)
14:44:18.276 [main] INFO org.hibernate.dialect.Dialect - HHH000400: 使用方言: org.hibernate.dialect.H2Dialect
14:44:18.463 [main] INFO o.hibernate.orm.connections.access - HHH10001501: 从JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@73a8e994]获取的用于(非JTA)DDL执行的连接不在自动提交模式;连接“本地事务”将被提交,并且连接将被设置为自动提交模式。
14:44:18.465 [main] INFO o.hibernate.orm.connections.access - HHH10001501: 从JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@7affc159]获取的用于(非JTA)DDL执行的连接不在自动提交模式;连接“本地事务”将被提交,并且连接将被设置为自动提交模式。
14:44:18.470 [main] INFO o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000490: 使用JtaPlatform实现: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
14:44:18.473 [main] INFO c.i.metamapping.service.UserService - 创建用户: ZhangSan
14:44:18.508 [main] INFO c.i.metamapping.service.UserService - 在1处创建用户ZhangSan
14:44:18.508 [main] INFO com.iluwatar.metamapping.App - 在1处添加用户User(id=1, username=ZhangSan, password=zhs123)。
14:44:18.508 [main] INFO c.i.metamapping.service.UserService - 创建用户: LiSi
14:44:18.509 [main] INFO c.i.metamapping.service.UserService - 在2处创建用户LiSi
14:44:18.509 [main] INFO com.iluwatar.metamapping.App - 在2处添加用户User(id=2, username=LiSi, password=ls123)。
14:44:18.509 [main] INFO c.i.metamapping.service.UserService - 创建用户: WangWu
14:44:18.512 [main] INFO c.i.metamapping.service.UserService - 在3处创建用户WangWu
14:44:18.512 [main] INFO com.iluwatar.metamapping.App - 在3处添加用户User(id=3, username=WangWu, password=ww123)。
14:44:18.512 [main] INFO c.i.metamapping.service.UserService - 列出所有用户。
14:44:18.542 [main] INFO com.iluwatar.metamapping.App - [User(id=1, username=ZhangSan, password=zhs123), User(id=2, username=LiSi, password=ls123), User(id=3, username=WangWu, password=ww123)]
14:44:18.542 [main] INFO c.i.metamapping.service.UserService - 获取用户在: 1
14:44:18.545 [main] INFO com.iluwatar.metamapping.App - User(id=1, username=ZhangSan, password=zhs123)
14:44:18.545 [main] INFO c.i.metamapping.service.UserService - 在1处更新用户
14:44:18.548 [main] INFO c.i.metamapping.service.UserService - 在2处删除用户
14:44:18.550 [main] INFO o.h.t.s.i.SchemaDropperImpl$DelayedDropActionImpl - HHH000477: 开始延迟清除模式作为SessionFactory关闭的一部分'
14:44:18.550 [main] INFO o.hibernate.orm.connections.access - HHH10001501: 从JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@7b5cc918]获取的用于(非JTA)DDL执行的连接不在自动提交模式;连接“本地事务”将被提交,并且连接将被设置为自动提交模式。
14:44:18.551 [main] INFO o.hibernate.orm.connections.pooling - HHH10001008: 清理连接池 [jdbc:h2:mem:metamapping]
四、何时在Java中使用元数据映射模式
当您需要在Java应用程序中弥合面向对象的域模型和关系数据库之间的差距,而无需将数据库查询硬编码到域逻辑中时,使用元数据映射设计模式。
五、元数据映射模式在Java中的实际应用
- 对象 - 关系映射(ORM)框架,如Hibernate、JPA、EclipseLink和MyBatis,经常使用元数据映射设计模式将Java对象映射到数据库表。
- 在企业应用程序中将数据库行映射到域对象。
六、元数据映射模式的好处和权衡
好处:
- 解耦对象模型和数据库模式,允许独立演进。
- 减少与数据访问相关的样板代码。
- 集中映射逻辑,使更改更易于管理。
权衡:
- 由于增加了一层抽象,增加了复杂性。
- 如果未正确优化,可能会影响性能。