feature: Metadata Mapping pattern (#1932)

* 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ä <iluwatar@users.noreply.github.com>

* fix review

* fix review

* Update App.java

* Update App.java

* fix review

Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>
This commit is contained in:
DragonDreamer
2022-01-02 02:46:20 +08:00
committed by GitHub
parent f670ae547b
commit 8403fdacdd
13 changed files with 661 additions and 0 deletions
@@ -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.
*
* <p>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<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);
}
}
@@ -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;
}
}
@@ -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<User> listUser() {
LOGGER.info("list all users.");
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("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();
}
}
@@ -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);
}
}
}
@@ -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();
}
}