From 8403fdacdde6f7c9ed09733311ca4c8c9859ff16 Mon Sep 17 00:00:00 2001 From: DragonDreamer Date: Sun, 2 Jan 2022 02:46:20 +0800 Subject: [PATCH] feature: Metadata Mapping pattern (#1932) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * metadata-mapping * Update README.md * add class diagram * update README.md * fix identation * Update pom.xml * fix indentation * fix ci * remove e.printstack * fix ci * update class diagram * fix ci * fix ci * fix sc * fix smells * Update DatabaseUtil.java * fix coverage * Update DatabaseUtil.java * Update DatabaseUtil.java * Update DatabaseUtil.java * Update metadata-mapping/README.md Co-authored-by: Ilkka Seppälä * fix review * fix review * Update App.java * Update App.java * fix review Co-authored-by: Ilkka Seppälä --- metadata-mapping/README.md | 182 ++++++++++++++++++ metadata-mapping/etc/metamapping.png | Bin 0 -> 49739 bytes metadata-mapping/etc/metamapping.puml | 32 +++ metadata-mapping/pom.xml | 87 +++++++++ .../java/com/iluwatar/metamapping/App.java | 72 +++++++ .../com/iluwatar/metamapping/model/User.java | 29 +++ .../metamapping/service/UserService.java | 114 +++++++++++ .../metamapping/utils/DatabaseUtil.java | 39 ++++ .../metamapping/utils/HibernateUtil.java | 45 +++++ .../iluwatar/metamapping/model/User.hbm.xml | 14 ++ .../src/main/resources/hibernate.cfg.xml | 20 ++ .../com/iluwatar/metamapping/AppTest.java | 20 ++ pom.xml | 7 + 13 files changed, 661 insertions(+) create mode 100644 metadata-mapping/README.md create mode 100644 metadata-mapping/etc/metamapping.png create mode 100644 metadata-mapping/etc/metamapping.puml create mode 100644 metadata-mapping/pom.xml create mode 100644 metadata-mapping/src/main/java/com/iluwatar/metamapping/App.java create mode 100644 metadata-mapping/src/main/java/com/iluwatar/metamapping/model/User.java create mode 100644 metadata-mapping/src/main/java/com/iluwatar/metamapping/service/UserService.java create mode 100644 metadata-mapping/src/main/java/com/iluwatar/metamapping/utils/DatabaseUtil.java create mode 100644 metadata-mapping/src/main/java/com/iluwatar/metamapping/utils/HibernateUtil.java create mode 100644 metadata-mapping/src/main/resources/com/iluwatar/metamapping/model/User.hbm.xml create mode 100644 metadata-mapping/src/main/resources/hibernate.cfg.xml create mode 100644 metadata-mapping/src/test/java/com/iluwatar/metamapping/AppTest.java diff --git a/metadata-mapping/README.md b/metadata-mapping/README.md new file mode 100644 index 000000000..5dcd932a7 --- /dev/null +++ b/metadata-mapping/README.md @@ -0,0 +1,182 @@ +--- +layout: pattern +title: Metadata Mapping +folder: metadata-mapping +permalink: /patterns/metadata-mapping/ +categories: Architectural +language: en +tags: + - Data access +--- + +## Intent + +Holds details of object-relational mapping in the metadata. + +## Explanation + +Real world example + +> Hibernate ORM Tool uses Metadata Mapping Pattern to specify the mapping between classes and tables either using XML or annotations in code. + +In plain words + +> Metadata Mapping specifies the mapping between classes and tables so that we could treat a table of any database like a Java class. + +Wikipedia says + +> Create a "virtual [object database](https://en.wikipedia.org/wiki/Object_database)" that can be used from within the programming language. + +**Programmatic Example** + +We give an example about visiting the information of `USER` table in `h2` database. Firstly, we create `USER` table with `h2`: + +```java +@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`;" + + "CREATE TABLE `user` (\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" + + ");"; + + /** + * Create database. + */ + static { + LOGGER.info("create h2 database"); + var source = new JdbcDataSource(); + source.setURL(DB_URL); + try (var statement = source.getConnection().createStatement()) { + statement.execute(CREATE_SCHEMA_SQL); + } catch (SQLException e) { + LOGGER.error("unable to create h2 data source", e); + } + } +} +``` + +Correspondingly, here's the basic `User` entity. + +```java +@Setter +@Getter +@ToString +public class User { + private Integer id; + private String username; + private String password; + + /** + * Get a user. + * @param username user name + * @param password user password + */ + public User(String username, String password) { + this.username = username; + this.password = password; + } +} +``` + +Then we write a `xml` file to show the mapping between the table and the object: + +```xml + + + + + + + + + + + + +``` + +We use `Hibernate` to resolve the mapping and connect to our database, here's its configuration: + +```xml + + + + + + jdbc:h2:mem:metamapping + org.h2.Driver + + 1 + + org.hibernate.dialect.H2Dialect + + false + + create-drop + + + +``` + +Then we can get access to the table just like an object with `Hibernate`, here's some CRUDs: + +```java +@Slf4j +public class UserService { + private static final SessionFactory factory = HibernateUtil.getSessionFactory(); + + /** + * List all users. + * @return list of users + */ + public List listUser() { + LOGGER.info("list all users."); + List users = new ArrayList<>(); + try (var session = factory.openSession()) { + var tx = session.beginTransaction(); + List userIter = session.createQuery("FROM User").list(); + for (var iterator = userIter.iterator(); iterator.hasNext();) { + users.add(iterator.next()); + } + tx.commit(); + } catch (HibernateException e) { + LOGGER.debug("fail to get users", e); + } + return users; + } + + // other CRUDs -> + ... + + public void close() { + HibernateUtil.shutdown(); + } +} +``` + +## Class diagram + +![metamapping](etc/metamapping.png) + +## Applicability + +Use the Metadata Mapping when: + +- you want reduce the amount of work needed to handle database mapping. + +## Known uses + +[Hibernate](https://hibernate.org/), [EclipseLink](https://www.eclipse.org/eclipselink/), [MyBatis](https://blog.mybatis.org/)...... + +## Credits + +- [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31) + diff --git a/metadata-mapping/etc/metamapping.png b/metadata-mapping/etc/metamapping.png new file mode 100644 index 0000000000000000000000000000000000000000..b1e89f8af56f44afafb5d272a526d730797d7b39 GIT binary patch literal 49739 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 &FlB+1VM-~c9!w4PGQkJ;>Atumt><@{G*J4d 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 listUser() ++ int createUser(User) ++ void updateUser(Integer,User) ++ void deleteUser(Integer) ++ User getUser(Integer) ++ void close() +} +class com.iluwatar.metamapping.utils.DatabaseUtil { ++ {static} void createDataSource() +} +class com.iluwatar.metamapping.model.User { +- Integer id +- String username +- String password ++ User(String username, String password) +} +class com.iluwatar.metamapping.utils.HibernateUtil { ++ {static} SessionFactory getSessionFactory() ++ {static} void shutdown() +} +class com.iluwatar.metamapping.App { ++ {static} void main(String[]) ++ {static} List generateSampleUsers() +} + +com.iluwatar.metamapping.service.UserService <.. com.iluwatar.metamapping.App +com.iluwatar.metamapping.model.User <.. com.iluwatar.metamapping.service.UserService +com.iluwatar.metamapping.utils.HibernateUtil <.. com.iluwatar.metamapping.service.UserService +com.iluwatar.metamapping.utils.DatabaseUtil <-- com.iluwatar.metamapping.utils.HibernateUtil +@enduml \ No newline at end of file diff --git a/metadata-mapping/pom.xml b/metadata-mapping/pom.xml new file mode 100644 index 000000000..43c9621df --- /dev/null +++ b/metadata-mapping/pom.xml @@ -0,0 +1,87 @@ + + + + + java-design-patterns + com.iluwatar + 1.26.0-SNAPSHOT + + 4.0.0 + + metadata-mapping + + + org.junit.jupiter + junit-jupiter-engine + test + + + com.h2database + h2 + + + org.hibernate + hibernate-core + + + com.h2database + h2 + + + javax.xml.bind + jaxb-api + + + com.sun.xml.bind + jaxb-impl + + + com.sun.istack + istack-commons-runtime + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + com.iluwatar.metamapping.App + + + + + + + + + \ No newline at end of file diff --git a/metadata-mapping/src/main/java/com/iluwatar/metamapping/App.java b/metadata-mapping/src/main/java/com/iluwatar/metamapping/App.java new file mode 100644 index 000000000..ff0377590 --- /dev/null +++ b/metadata-mapping/src/main/java/com/iluwatar/metamapping/App.java @@ -0,0 +1,72 @@ +package com.iluwatar.metamapping; + +import com.iluwatar.metamapping.model.User; +import com.iluwatar.metamapping.service.UserService; +import com.iluwatar.metamapping.utils.DatabaseUtil; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.hibernate.service.ServiceRegistry; + +/** + * Metadata Mapping specifies the mapping + * between classes and tables so that + * we could treat a table of any database like a Java class. + * + *

With hibernate, we achieve list/create/update/delete/get operations: + * 1)Create the H2 Database in {@link DatabaseUtil}. + * 2)Hibernate resolve hibernate.cfg.xml and generate service like save/list/get/delete. + * For learning metadata mapping pattern, we go deeper into Hibernate here: + * a)read properties from hibernate.cfg.xml and mapping from *.hbm.xml + * b)create session factory to generate session interacting with database + * c)generate session with factory pattern + * d)create query object or use basic api with session, + * hibernate will convert all query to database query according to metadata + * 3)We encapsulate hibernate service in {@link UserService} for our use. + * @see org.hibernate.cfg.Configuration#configure(String) + * @see org.hibernate.cfg.Configuration#buildSessionFactory(ServiceRegistry) + * @see org.hibernate.internal.SessionFactoryImpl#openSession() + */ +@Slf4j +public class App { + /** + * Program entry point. + * + * @param args command line args. + * @throws Exception if any error occurs. + */ + public static void main(String[] args) throws Exception { + // get service + var userService = new UserService(); + // use create service to add users + for (var user: generateSampleUsers()) { + var id = userService.createUser(user); + LOGGER.info("Add user" + user + "at" + id + "."); + } + // use list service to get users + var users = userService.listUser(); + LOGGER.info(String.valueOf(users)); + // use get service to get a user + var user = userService.getUser(1); + LOGGER.info(String.valueOf(user)); + // change password of user 1 + user.setPassword("new123"); + // use update service to update user 1 + userService.updateUser(1, user); + // use delete service to delete user 2 + userService.deleteUser(2); + // close service + userService.close(); + } + + /** + * Generate users. + * + * @return list of users. + */ + public static List 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); + } +} \ No newline at end of file diff --git a/metadata-mapping/src/main/java/com/iluwatar/metamapping/model/User.java b/metadata-mapping/src/main/java/com/iluwatar/metamapping/model/User.java new file mode 100644 index 000000000..0bf2575b1 --- /dev/null +++ b/metadata-mapping/src/main/java/com/iluwatar/metamapping/model/User.java @@ -0,0 +1,29 @@ +package com.iluwatar.metamapping.model; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +/** + * User Entity. + */ +@Setter +@Getter +@ToString +public class User { + private Integer id; + private String username; + private String password; + + public User() {} + + /** + * Get a user. + * @param username user name + * @param password user password + */ + public User(String username, String password) { + this.username = username; + this.password = password; + } +} \ No newline at end of file diff --git a/metadata-mapping/src/main/java/com/iluwatar/metamapping/service/UserService.java b/metadata-mapping/src/main/java/com/iluwatar/metamapping/service/UserService.java new file mode 100644 index 000000000..1f85be0d5 --- /dev/null +++ b/metadata-mapping/src/main/java/com/iluwatar/metamapping/service/UserService.java @@ -0,0 +1,114 @@ +package com.iluwatar.metamapping.service; + +import com.iluwatar.metamapping.model.User; +import com.iluwatar.metamapping.utils.HibernateUtil; +import java.util.ArrayList; +import java.util.List; +import lombok.extern.slf4j.Slf4j; +import org.hibernate.HibernateException; +import org.hibernate.SessionFactory; + +/** + * Service layer for user. + */ +@Slf4j +public class UserService { + private static final SessionFactory factory = HibernateUtil.getSessionFactory(); + + /** + * List all users. + * @return list of users + */ + public List listUser() { + LOGGER.info("list all users."); + List users = new ArrayList<>(); + try (var session = factory.openSession()) { + var tx = session.beginTransaction(); + List userIter = session.createQuery("FROM User").list(); + for (var iterator = userIter.iterator(); iterator.hasNext();) { + users.add(iterator.next()); + } + tx.commit(); + } catch (HibernateException e) { + LOGGER.debug("fail to get users", e); + } + return users; + } + + /** + * Add a user. + * @param user user entity + * @return user id + */ + public int createUser(User user) { + LOGGER.info("create user: " + user.getUsername()); + var id = -1; + try (var session = factory.openSession()) { + var tx = session.beginTransaction(); + id = (Integer) session.save(user); + tx.commit(); + } catch (HibernateException e) { + LOGGER.debug("fail to create user", e); + } + LOGGER.info("create user " + user.getUsername() + " at " + id); + return id; + } + + /** + * Update user. + * @param id user id + * @param user new user entity + */ + public void updateUser(Integer id, User user) { + LOGGER.info("update user at " + id); + try (var session = factory.openSession()) { + var tx = session.beginTransaction(); + user.setId(id); + session.update(user); + tx.commit(); + } catch (HibernateException e) { + LOGGER.debug("fail to update user", e); + } + } + + /** + * Delete user. + * @param id user id + */ + public void deleteUser(Integer id) { + LOGGER.info("delete user at: " + id); + try (var session = factory.openSession()) { + var tx = session.beginTransaction(); + var user = session.get(User.class, id); + session.delete(user); + tx.commit(); + } catch (HibernateException e) { + LOGGER.debug("fail to delete user", e); + } + } + + /** + * Get user. + * @param id user id + * @return deleted user + */ + public User getUser(Integer id) { + LOGGER.info("get user at: " + id); + User user = null; + try (var session = factory.openSession()) { + var tx = session.beginTransaction(); + user = session.get(User.class, id); + tx.commit(); + } catch (HibernateException e) { + LOGGER.debug("fail to get user", e); + } + return user; + } + + /** + * Close hibernate. + */ + public void close() { + HibernateUtil.shutdown(); + } +} \ No newline at end of file diff --git a/metadata-mapping/src/main/java/com/iluwatar/metamapping/utils/DatabaseUtil.java b/metadata-mapping/src/main/java/com/iluwatar/metamapping/utils/DatabaseUtil.java new file mode 100644 index 000000000..b6d0200e8 --- /dev/null +++ b/metadata-mapping/src/main/java/com/iluwatar/metamapping/utils/DatabaseUtil.java @@ -0,0 +1,39 @@ +package com.iluwatar.metamapping.utils; + +import java.sql.SQLException; +import lombok.extern.slf4j.Slf4j; +import org.h2.jdbcx.JdbcDataSource; + +/** + * Create h2 database. + */ +@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`;" + + "CREATE TABLE `user` (\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" + + ");"; + + /** + * Hide constructor. + */ + private DatabaseUtil() {} + + /** + * Create database. + */ + static { + LOGGER.info("create h2 database"); + var source = new JdbcDataSource(); + source.setURL(DB_URL); + try (var statement = source.getConnection().createStatement()) { + statement.execute(CREATE_SCHEMA_SQL); + } catch (SQLException e) { + LOGGER.error("unable to create h2 data source", e); + } + } +} \ No newline at end of file diff --git a/metadata-mapping/src/main/java/com/iluwatar/metamapping/utils/HibernateUtil.java b/metadata-mapping/src/main/java/com/iluwatar/metamapping/utils/HibernateUtil.java new file mode 100644 index 000000000..ba405eb74 --- /dev/null +++ b/metadata-mapping/src/main/java/com/iluwatar/metamapping/utils/HibernateUtil.java @@ -0,0 +1,45 @@ +package com.iluwatar.metamapping.utils; + +import lombok.extern.slf4j.Slf4j; +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; + +/** + * Manage hibernate. + */ +@Slf4j +public class HibernateUtil { + + private static final SessionFactory sessionFactory = buildSessionFactory(); + + /** + * Hide constructor. + */ + private HibernateUtil() {} + + /** + * Build session factory. + * @return session factory + */ + private static SessionFactory buildSessionFactory() { + // Create the SessionFactory from hibernate.cfg.xml + return new Configuration().configure().buildSessionFactory(); + } + + /** + * Get session factory. + * @return session factory + */ + public static SessionFactory getSessionFactory() { + return sessionFactory; + } + + /** + * Close session factory. + */ + public static void shutdown() { + // Close caches and connection pools + getSessionFactory().close(); + } + +} \ No newline at end of file diff --git a/metadata-mapping/src/main/resources/com/iluwatar/metamapping/model/User.hbm.xml b/metadata-mapping/src/main/resources/com/iluwatar/metamapping/model/User.hbm.xml new file mode 100644 index 000000000..cd63c552d --- /dev/null +++ b/metadata-mapping/src/main/resources/com/iluwatar/metamapping/model/User.hbm.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/metadata-mapping/src/main/resources/hibernate.cfg.xml b/metadata-mapping/src/main/resources/hibernate.cfg.xml new file mode 100644 index 000000000..18dc198e8 --- /dev/null +++ b/metadata-mapping/src/main/resources/hibernate.cfg.xml @@ -0,0 +1,20 @@ + + + + + + jdbc:h2:mem:metamapping + org.h2.Driver + + 1 + + org.hibernate.dialect.H2Dialect + + false + + create-drop + + + \ No newline at end of file diff --git a/metadata-mapping/src/test/java/com/iluwatar/metamapping/AppTest.java b/metadata-mapping/src/test/java/com/iluwatar/metamapping/AppTest.java new file mode 100644 index 000000000..127ddad0f --- /dev/null +++ b/metadata-mapping/src/test/java/com/iluwatar/metamapping/AppTest.java @@ -0,0 +1,20 @@ +package com.iluwatar.metamapping; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +/** + * Tests that metadata mapping example runs without errors. + */ +class AppTest { + /** + * Issue: Add at least one assertion to this test case. + * + * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])} + * throws an exception. + */ + @Test + void shouldExecuteMetaMappingWithoutException() { + assertDoesNotThrow(() -> App.main(new String[]{})); + } +} diff --git a/pom.xml b/pom.xml index 5c3634b95..e1573a737 100644 --- a/pom.xml +++ b/pom.xml @@ -75,6 +75,7 @@ 3.0 1.4.8 2.7 + 4.0.1 https://sonarcloud.io iluwatar @@ -227,6 +228,7 @@ lockable-object fanout-fanin domain-model + metadata-mapping @@ -377,6 +379,11 @@ commons-io ${commons-io.version} + + com.sun.istack + istack-commons-runtime + ${istack-commons-runtime.version} + -- GitLab