Compare commits
6 Commits
all-contri
...
valueObjec
Author | SHA1 | Date | |
---|---|---|---|
d2fab302ce | |||
2679f7aa6f | |||
8403fdacdd | |||
f670ae547b | |||
df73d80365 | |||
4588e09939 |
@ -1758,6 +1758,33 @@
|
|||||||
"contributions": [
|
"contributions": [
|
||||||
"code"
|
"code"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "yuhangbin",
|
||||||
|
"name": "CharlieYu",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/17566866?v=4",
|
||||||
|
"profile": "https://github.com/yuhangbin",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "Leisterbecker",
|
||||||
|
"name": "Leisterbecker",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/20650323?v=4",
|
||||||
|
"profile": "https://github.com/Leisterbecker",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "castleKing1997",
|
||||||
|
"name": "DragonDreamer",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/35420129?v=4",
|
||||||
|
"profile": "http://rosaecrucis.cn",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"contributorsPerLine": 7,
|
"contributorsPerLine": 7,
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
[](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns)
|
[](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns)
|
||||||
[](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||||
[](#contributors-)
|
[](#contributors-)
|
||||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
@ -323,6 +323,9 @@ This project is licensed under the terms of the MIT license.
|
|||||||
<td align="center"><a href="http://no website"><img src="https://avatars.githubusercontent.com/u/47126749?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kevin</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3AKevinyl3" title="Reviewed Pull Requests">👀</a></td>
|
<td align="center"><a href="http://no website"><img src="https://avatars.githubusercontent.com/u/47126749?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kevin</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3AKevinyl3" title="Reviewed Pull Requests">👀</a></td>
|
||||||
<td align="center"><a href="https://github.com/Shrirang97"><img src="https://avatars.githubusercontent.com/u/28738668?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Shrirang</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3AShrirang97" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/iluwatar/java-design-patterns/commits?author=Shrirang97" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/Shrirang97"><img src="https://avatars.githubusercontent.com/u/28738668?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Shrirang</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3AShrirang97" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/iluwatar/java-design-patterns/commits?author=Shrirang97" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/interactwithankush"><img src="https://avatars.githubusercontent.com/u/18613127?v=4?s=100" width="100px;" alt=""/><br /><sub><b>interactwithankush</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=interactwithankush" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/interactwithankush"><img src="https://avatars.githubusercontent.com/u/18613127?v=4?s=100" width="100px;" alt=""/><br /><sub><b>interactwithankush</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=interactwithankush" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/yuhangbin"><img src="https://avatars.githubusercontent.com/u/17566866?v=4?s=100" width="100px;" alt=""/><br /><sub><b>CharlieYu</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=yuhangbin" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/Leisterbecker"><img src="https://avatars.githubusercontent.com/u/20650323?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Leisterbecker</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Leisterbecker" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="http://rosaecrucis.cn"><img src="https://avatars.githubusercontent.com/u/35420129?v=4?s=100" width="100px;" alt=""/><br /><sub><b>DragonDreamer</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=castleKing1997" title="Code">💻</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
@ -49,13 +49,28 @@ public class App {
|
|||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
||||||
var kingJoffrey = new KingJoffrey();
|
var kingJoffrey = new KingJoffrey();
|
||||||
var kingsHand = new KingsHand(kingJoffrey);
|
|
||||||
|
var kingsHand = new KingsHand();
|
||||||
|
kingsHand.registerObserver(kingJoffrey, Event.TRAITOR_DETECTED);
|
||||||
|
kingsHand.registerObserver(kingJoffrey, Event.STARK_SIGHTED);
|
||||||
|
kingsHand.registerObserver(kingJoffrey, Event.WARSHIPS_APPROACHING);
|
||||||
|
kingsHand.registerObserver(kingJoffrey, Event.WHITE_WALKERS_SIGHTED);
|
||||||
|
|
||||||
|
var varys = new LordVarys();
|
||||||
|
varys.registerObserver(kingsHand, Event.TRAITOR_DETECTED);
|
||||||
|
varys.registerObserver(kingsHand, Event.WHITE_WALKERS_SIGHTED);
|
||||||
|
|
||||||
|
var scout = new Scout();
|
||||||
|
scout.registerObserver(kingsHand, Event.WARSHIPS_APPROACHING);
|
||||||
|
scout.registerObserver(varys, Event.WHITE_WALKERS_SIGHTED);
|
||||||
|
|
||||||
|
var baelish = new LordBaelish(kingsHand, Event.STARK_SIGHTED);
|
||||||
|
|
||||||
var emitters = List.of(
|
var emitters = List.of(
|
||||||
kingsHand,
|
kingsHand,
|
||||||
new LordBaelish(kingsHand),
|
baelish,
|
||||||
new LordVarys(kingsHand),
|
varys,
|
||||||
new Scout(kingsHand)
|
scout
|
||||||
);
|
);
|
||||||
|
|
||||||
Arrays.stream(Weekday.values())
|
Arrays.stream(Weekday.values())
|
||||||
|
@ -31,6 +31,7 @@ import lombok.RequiredArgsConstructor;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public enum Event {
|
public enum Event {
|
||||||
|
|
||||||
|
WHITE_WALKERS_SIGHTED("White walkers sighted"),
|
||||||
STARK_SIGHTED("Stark sighted"),
|
STARK_SIGHTED("Stark sighted"),
|
||||||
WARSHIPS_APPROACHING("Warships approaching"),
|
WARSHIPS_APPROACHING("Warships approaching"),
|
||||||
TRAITOR_DETECTED("Traitor detected");
|
TRAITOR_DETECTED("Traitor detected");
|
||||||
|
@ -23,31 +23,48 @@
|
|||||||
|
|
||||||
package com.iluwatar.event.aggregator;
|
package com.iluwatar.event.aggregator;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EventEmitter is the base class for event producers that can be observed.
|
* EventEmitter is the base class for event producers that can be observed.
|
||||||
*/
|
*/
|
||||||
public abstract class EventEmitter {
|
public abstract class EventEmitter {
|
||||||
|
|
||||||
private final List<EventObserver> observers;
|
private final Map<Event, List<EventObserver>> observerLists;
|
||||||
|
|
||||||
public EventEmitter() {
|
public EventEmitter() {
|
||||||
observers = new LinkedList<>();
|
observerLists = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public EventEmitter(EventObserver obs) {
|
public EventEmitter(EventObserver obs, Event e) {
|
||||||
this();
|
this();
|
||||||
registerObserver(obs);
|
registerObserver(obs, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void registerObserver(EventObserver obs) {
|
/**
|
||||||
observers.add(obs);
|
* Registers observer for specific event in the related list.
|
||||||
|
*
|
||||||
|
* @param obs the observer that observers this emitter
|
||||||
|
* @param e the specific event for that observation occurs
|
||||||
|
* */
|
||||||
|
public final void registerObserver(EventObserver obs, Event e) {
|
||||||
|
if (!observerLists.containsKey(e)) {
|
||||||
|
observerLists.put(e, new LinkedList<>());
|
||||||
|
}
|
||||||
|
if (!observerLists.get(e).contains(obs)) {
|
||||||
|
observerLists.get(e).add(obs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void notifyObservers(Event e) {
|
protected void notifyObservers(Event e) {
|
||||||
observers.forEach(obs -> obs.onEvent(e));
|
if (observerLists.containsKey(e)) {
|
||||||
|
observerLists
|
||||||
|
.get(e)
|
||||||
|
.forEach(observer -> observer.onEvent(e));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void timePasses(Weekday day);
|
public abstract void timePasses(Weekday day);
|
||||||
|
@ -31,8 +31,8 @@ public class KingsHand extends EventEmitter implements EventObserver {
|
|||||||
public KingsHand() {
|
public KingsHand() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public KingsHand(EventObserver obs) {
|
public KingsHand(EventObserver obs, Event e) {
|
||||||
super(obs);
|
super(obs, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -42,6 +42,5 @@ public class KingsHand extends EventEmitter implements EventObserver {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void timePasses(Weekday day) {
|
public void timePasses(Weekday day) {
|
||||||
// NOP
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,8 +31,8 @@ public class LordBaelish extends EventEmitter {
|
|||||||
public LordBaelish() {
|
public LordBaelish() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public LordBaelish(EventObserver obs) {
|
public LordBaelish(EventObserver obs, Event e) {
|
||||||
super(obs);
|
super(obs, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -23,16 +23,19 @@
|
|||||||
|
|
||||||
package com.iluwatar.event.aggregator;
|
package com.iluwatar.event.aggregator;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LordVarys produces events.
|
* LordVarys produces events.
|
||||||
*/
|
*/
|
||||||
public class LordVarys extends EventEmitter {
|
@Slf4j
|
||||||
|
public class LordVarys extends EventEmitter implements EventObserver {
|
||||||
|
|
||||||
public LordVarys() {
|
public LordVarys() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public LordVarys(EventObserver obs) {
|
public LordVarys(EventObserver obs, Event e) {
|
||||||
super(obs);
|
super(obs, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -41,4 +44,10 @@ public class LordVarys extends EventEmitter {
|
|||||||
notifyObservers(Event.TRAITOR_DETECTED);
|
notifyObservers(Event.TRAITOR_DETECTED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEvent(Event e) {
|
||||||
|
notifyObservers(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,8 +31,8 @@ public class Scout extends EventEmitter {
|
|||||||
public Scout() {
|
public Scout() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Scout(EventObserver obs) {
|
public Scout(EventObserver obs, Event e) {
|
||||||
super(obs);
|
super(obs, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -40,5 +40,8 @@ public class Scout extends EventEmitter {
|
|||||||
if (day == Weekday.TUESDAY) {
|
if (day == Weekday.TUESDAY) {
|
||||||
notifyObservers(Event.WARSHIPS_APPROACHING);
|
notifyObservers(Event.WARSHIPS_APPROACHING);
|
||||||
}
|
}
|
||||||
|
if (day == Weekday.WEDNESDAY) {
|
||||||
|
notifyObservers(Event.WHITE_WALKERS_SIGHTED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|||||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -46,7 +47,7 @@ abstract class EventEmitterTest<E extends EventEmitter> {
|
|||||||
/**
|
/**
|
||||||
* Factory used to create a new instance of the test object with a default observer
|
* Factory used to create a new instance of the test object with a default observer
|
||||||
*/
|
*/
|
||||||
private final Function<EventObserver, E> factoryWithDefaultObserver;
|
private final BiFunction<EventObserver, Event, E> factoryWithDefaultObserver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory used to create a new instance of the test object without passing a default observer
|
* Factory used to create a new instance of the test object without passing a default observer
|
||||||
@ -67,7 +68,7 @@ abstract class EventEmitterTest<E extends EventEmitter> {
|
|||||||
* Create a new event emitter test, using the given test object factories, special day and event
|
* Create a new event emitter test, using the given test object factories, special day and event
|
||||||
*/
|
*/
|
||||||
EventEmitterTest(final Weekday specialDay, final Event event,
|
EventEmitterTest(final Weekday specialDay, final Event event,
|
||||||
final Function<EventObserver, E> factoryWithDefaultObserver,
|
final BiFunction<EventObserver, Event, E> factoryWithDefaultObserver,
|
||||||
final Supplier<E> factoryWithoutDefaultObserver) {
|
final Supplier<E> factoryWithoutDefaultObserver) {
|
||||||
|
|
||||||
this.specialDay = specialDay;
|
this.specialDay = specialDay;
|
||||||
@ -129,8 +130,8 @@ abstract class EventEmitterTest<E extends EventEmitter> {
|
|||||||
final var observer2 = mock(EventObserver.class);
|
final var observer2 = mock(EventObserver.class);
|
||||||
|
|
||||||
final var emitter = this.factoryWithoutDefaultObserver.get();
|
final var emitter = this.factoryWithoutDefaultObserver.get();
|
||||||
emitter.registerObserver(observer1);
|
emitter.registerObserver(observer1, event);
|
||||||
emitter.registerObserver(observer2);
|
emitter.registerObserver(observer2, event);
|
||||||
|
|
||||||
testAllDays(specialDay, event, emitter, observer1, observer2);
|
testAllDays(specialDay, event, emitter, observer1, observer2);
|
||||||
}
|
}
|
||||||
@ -146,9 +147,9 @@ abstract class EventEmitterTest<E extends EventEmitter> {
|
|||||||
final var observer1 = mock(EventObserver.class);
|
final var observer1 = mock(EventObserver.class);
|
||||||
final var observer2 = mock(EventObserver.class);
|
final var observer2 = mock(EventObserver.class);
|
||||||
|
|
||||||
final var emitter = this.factoryWithDefaultObserver.apply(defaultObserver);
|
final var emitter = this.factoryWithDefaultObserver.apply(defaultObserver, event);
|
||||||
emitter.registerObserver(observer1);
|
emitter.registerObserver(observer1, event);
|
||||||
emitter.registerObserver(observer2);
|
emitter.registerObserver(observer2, event);
|
||||||
|
|
||||||
testAllDays(specialDay, event, emitter, defaultObserver, observer1, observer2);
|
testAllDays(specialDay, event, emitter, defaultObserver, observer1, observer2);
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,11 @@ class KingsHandTest extends EventEmitterTest<KingsHand> {
|
|||||||
@Test
|
@Test
|
||||||
void testPassThrough() throws Exception {
|
void testPassThrough() throws Exception {
|
||||||
final var observer = mock(EventObserver.class);
|
final var observer = mock(EventObserver.class);
|
||||||
final var kingsHand = new KingsHand(observer);
|
final var kingsHand = new KingsHand();
|
||||||
|
kingsHand.registerObserver(observer, Event.STARK_SIGHTED);
|
||||||
|
kingsHand.registerObserver(observer, Event.WARSHIPS_APPROACHING);
|
||||||
|
kingsHand.registerObserver(observer, Event.TRAITOR_DETECTED);
|
||||||
|
kingsHand.registerObserver(observer, Event.WHITE_WALKERS_SIGHTED);
|
||||||
|
|
||||||
// The kings hand should not pass any events before he received one
|
// The kings hand should not pass any events before he received one
|
||||||
verifyZeroInteractions(observer);
|
verifyZeroInteractions(observer);
|
||||||
|
@ -34,7 +34,9 @@ class ScoutTest extends EventEmitterTest<Scout> {
|
|||||||
* Create a new test instance, using the correct object factory
|
* Create a new test instance, using the correct object factory
|
||||||
*/
|
*/
|
||||||
public ScoutTest() {
|
public ScoutTest() {
|
||||||
super(Weekday.TUESDAY, Event.WARSHIPS_APPROACHING, Scout::new, Scout::new);
|
|
||||||
|
super(Weekday.TUESDAY, Event.WARSHIPS_APPROACHING, Scout::new, Scout::new);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
182
metadata-mapping/README.md
Normal file
182
metadata-mapping/README.md
Normal file
@ -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
|
||||||
|
<?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">
|
||||||
|
<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>
|
||||||
|
```
|
||||||
|
|
||||||
|
We use `Hibernate` to resolve the mapping and connect to our database, here's its configuration:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE hibernate-configuration PUBLIC
|
||||||
|
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
|
||||||
|
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
|
||||||
|
<hibernate-configuration>
|
||||||
|
<session-factory>
|
||||||
|
<!-- JDBC Database connection settings -->
|
||||||
|
<property name="connection.url">jdbc:h2:mem:metamapping</property>
|
||||||
|
<property name="connection.driver_class">org.h2.Driver</property>
|
||||||
|
<!-- JDBC connection pool settings ... using built-in test pool -->
|
||||||
|
<property name="connection.pool_size">1</property>
|
||||||
|
<!-- Select our SQL dialect -->
|
||||||
|
<property name="dialect">org.hibernate.dialect.H2Dialect</property>
|
||||||
|
<!-- Echo the SQL to stdout -->
|
||||||
|
<property name="show_sql">false</property>
|
||||||
|
<!-- Drop and re-create the database schema on startup -->
|
||||||
|
<property name="hbm2ddl.auto">create-drop</property>
|
||||||
|
<mapping resource="com/iluwatar/metamapping/model/User.hbm.xml" />
|
||||||
|
</session-factory>
|
||||||
|
</hibernate-configuration>
|
||||||
|
```
|
||||||
|
|
||||||
|
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<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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// other CRUDs ->
|
||||||
|
...
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
HibernateUtil.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Class diagram
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 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)
|
||||||
|
|
BIN
metadata-mapping/etc/metamapping.png
Normal file
BIN
metadata-mapping/etc/metamapping.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 49 KiB |
32
metadata-mapping/etc/metamapping.puml
Normal file
32
metadata-mapping/etc/metamapping.puml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
@startuml
|
||||||
|
interface com.iluwatar.metamapping.service.UserService {
|
||||||
|
+ List<User> 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<User> 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
|
87
metadata-mapping/pom.xml
Normal file
87
metadata-mapping/pom.xml
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
The MIT License
|
||||||
|
Copyright © 2014-2021 Ilkka Seppälä
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>java-design-patterns</artifactId>
|
||||||
|
<groupId>com.iluwatar</groupId>
|
||||||
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>metadata-mapping</artifactId>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.h2database</groupId>
|
||||||
|
<artifactId>h2</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.hibernate</groupId>
|
||||||
|
<artifactId>hibernate-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.h2database</groupId>
|
||||||
|
<artifactId>h2</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.xml.bind</groupId>
|
||||||
|
<artifactId>jaxb-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.sun.xml.bind</groupId>
|
||||||
|
<artifactId>jaxb-impl</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.sun.istack</groupId>
|
||||||
|
<artifactId>istack-commons-runtime</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<configuration>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<mainClass>com.iluwatar.metamapping.App</mainClass>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
<?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">
|
||||||
|
<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>
|
20
metadata-mapping/src/main/resources/hibernate.cfg.xml
Normal file
20
metadata-mapping/src/main/resources/hibernate.cfg.xml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE hibernate-configuration PUBLIC
|
||||||
|
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
|
||||||
|
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
|
||||||
|
<hibernate-configuration>
|
||||||
|
<session-factory>
|
||||||
|
<!-- JDBC Database connection settings -->
|
||||||
|
<property name="connection.url">jdbc:h2:mem:metamapping</property>
|
||||||
|
<property name="connection.driver_class">org.h2.Driver</property>
|
||||||
|
<!-- JDBC connection pool settings ... using built-in test pool -->
|
||||||
|
<property name="connection.pool_size">1</property>
|
||||||
|
<!-- Select our SQL dialect -->
|
||||||
|
<property name="dialect">org.hibernate.dialect.H2Dialect</property>
|
||||||
|
<!-- Echo the SQL to stdout -->
|
||||||
|
<property name="show_sql">false</property>
|
||||||
|
<!-- Drop and re-create the database schema on startup -->
|
||||||
|
<property name="hbm2ddl.auto">create-drop</property>
|
||||||
|
<mapping resource="com/iluwatar/metamapping/model/User.hbm.xml" />
|
||||||
|
</session-factory>
|
||||||
|
</hibernate-configuration>
|
@ -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[]{}));
|
||||||
|
}
|
||||||
|
}
|
7
pom.xml
7
pom.xml
@ -75,6 +75,7 @@
|
|||||||
<license-maven-plugin.version>3.0</license-maven-plugin.version>
|
<license-maven-plugin.version>3.0</license-maven-plugin.version>
|
||||||
<urm-maven-plugin.version>1.4.8</urm-maven-plugin.version>
|
<urm-maven-plugin.version>1.4.8</urm-maven-plugin.version>
|
||||||
<commons-io.version>2.7</commons-io.version>
|
<commons-io.version>2.7</commons-io.version>
|
||||||
|
<istack-commons-runtime.version>4.0.1</istack-commons-runtime.version>
|
||||||
<!-- SonarCloud -->
|
<!-- SonarCloud -->
|
||||||
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
|
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
|
||||||
<sonar.organization>iluwatar</sonar.organization>
|
<sonar.organization>iluwatar</sonar.organization>
|
||||||
@ -227,6 +228,7 @@
|
|||||||
<module>lockable-object</module>
|
<module>lockable-object</module>
|
||||||
<module>fanout-fanin</module>
|
<module>fanout-fanin</module>
|
||||||
<module>domain-model</module>
|
<module>domain-model</module>
|
||||||
|
<module>metadata-mapping</module>
|
||||||
</modules>
|
</modules>
|
||||||
<repositories>
|
<repositories>
|
||||||
<repository>
|
<repository>
|
||||||
@ -377,6 +379,11 @@
|
|||||||
<artifactId>commons-io</artifactId>
|
<artifactId>commons-io</artifactId>
|
||||||
<version>${commons-io.version}</version>
|
<version>${commons-io.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.sun.istack</groupId>
|
||||||
|
<artifactId>istack-commons-runtime</artifactId>
|
||||||
|
<version>${istack-commons-runtime.version}</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -10,19 +10,80 @@ tags:
|
|||||||
---
|
---
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
|
|
||||||
Provide objects which follow value semantics rather than reference semantics.
|
Provide objects which follow value semantics rather than reference semantics.
|
||||||
This means value objects' equality are not based on identity. Two value objects are
|
This means value objects' equality is not based on identity. Two value objects are
|
||||||
equal when they have the same value, not necessarily being the same object.
|
equal when they have the same value, not necessarily being the same object.
|
||||||
|
|
||||||
|
## Explanation
|
||||||
|
|
||||||
|
Real-world example
|
||||||
|
|
||||||
|
> There is a class for hero statistics in a role-playing game. The statistics contain attributes
|
||||||
|
> such as strength, intelligence, and luck. The statistics of different heroes should be equal
|
||||||
|
> when all the attributes are equal.
|
||||||
|
|
||||||
|
In plain words
|
||||||
|
|
||||||
|
> Value objects are equal when their attributes have the same value
|
||||||
|
|
||||||
|
Wikipedia says
|
||||||
|
|
||||||
|
> In computer science, a value object is a small object that represents a simple entity whose
|
||||||
|
> equality is not based on identity: i.e. two value objects are equal when they have the same
|
||||||
|
> value, not necessarily being the same object.
|
||||||
|
|
||||||
|
**Programmatic Example**
|
||||||
|
|
||||||
|
Here is the `HeroStat` class that is the value object. Notice the use of
|
||||||
|
[Lombok's `@Value`](https://projectlombok.org/features/Value) annotation.
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Value(staticConstructor = "valueOf")
|
||||||
|
class HeroStat {
|
||||||
|
|
||||||
|
int strength;
|
||||||
|
int intelligence;
|
||||||
|
int luck;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The example creates three different `HeroStat`s and compares their equality.
|
||||||
|
|
||||||
|
```java
|
||||||
|
var statA = HeroStat.valueOf(10, 5, 0);
|
||||||
|
var statB = HeroStat.valueOf(10, 5, 0);
|
||||||
|
var statC = HeroStat.valueOf(5, 1, 8);
|
||||||
|
|
||||||
|
LOGGER.info(statA.toString());
|
||||||
|
LOGGER.info(statB.toString());
|
||||||
|
LOGGER.info(statC.toString());
|
||||||
|
|
||||||
|
LOGGER.info("Is statA and statB equal : {}", statA.equals(statB));
|
||||||
|
LOGGER.info("Is statA and statC equal : {}", statA.equals(statC));
|
||||||
|
```
|
||||||
|
|
||||||
|
Here's the console output.
|
||||||
|
|
||||||
|
```
|
||||||
|
20:11:12.199 [main] INFO com.iluwatar.value.object.App - HeroStat(strength=10, intelligence=5, luck=0)
|
||||||
|
20:11:12.202 [main] INFO com.iluwatar.value.object.App - HeroStat(strength=10, intelligence=5, luck=0)
|
||||||
|
20:11:12.202 [main] INFO com.iluwatar.value.object.App - HeroStat(strength=5, intelligence=1, luck=8)
|
||||||
|
20:11:12.202 [main] INFO com.iluwatar.value.object.App - Is statA and statB equal : true
|
||||||
|
20:11:12.203 [main] INFO com.iluwatar.value.object.App - Is statA and statC equal : false
|
||||||
|
```
|
||||||
|
|
||||||
## Class diagram
|
## Class diagram
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Applicability
|
## Applicability
|
||||||
|
|
||||||
Use the Value Object when
|
Use the Value Object when
|
||||||
|
|
||||||
* You need to measure the objects' equality based on the objects' value
|
* The object's equality needs to be based on the object's value
|
||||||
|
|
||||||
## Real world examples
|
## Known uses
|
||||||
|
|
||||||
* [java.util.Optional](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html)
|
* [java.util.Optional](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html)
|
||||||
* [java.time.LocalDate](https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html)
|
* [java.time.LocalDate](https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html)
|
||||||
@ -31,6 +92,7 @@ Use the Value Object when
|
|||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
* [Patterns of Enterprise Application Architecture](http://www.martinfowler.com/books/eaa.html)
|
* [Patterns of Enterprise Application Architecture](http://www.martinfowler.com/books/eaa.html)
|
||||||
|
* [ValueObject](https://martinfowler.com/bliki/ValueObject.html)
|
||||||
* [VALJOs - Value Java Objects : Stephen Colebourne's blog](http://blog.joda.org/2014/03/valjos-value-java-objects.html)
|
* [VALJOs - Value Java Objects : Stephen Colebourne's blog](http://blog.joda.org/2014/03/valjos-value-java-objects.html)
|
||||||
* [Value Object : Wikipedia](https://en.wikipedia.org/wiki/Value_object)
|
* [Value Object : Wikipedia](https://en.wikipedia.org/wiki/Value_object)
|
||||||
* [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=f27d2644fbe5026ea448791a8ad09c94)
|
* [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=f27d2644fbe5026ea448791a8ad09c94)
|
||||||
|
@ -43,7 +43,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This practice creates three HeroStats(Value object) and checks equality between those.
|
* This example creates three HeroStats (value objects) and checks equality between those.
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
var statA = HeroStat.valueOf(10, 5, 0);
|
var statA = HeroStat.valueOf(10, 5, 0);
|
||||||
@ -51,6 +51,8 @@ public class App {
|
|||||||
var statC = HeroStat.valueOf(5, 1, 8);
|
var statC = HeroStat.valueOf(5, 1, 8);
|
||||||
|
|
||||||
LOGGER.info(statA.toString());
|
LOGGER.info(statA.toString());
|
||||||
|
LOGGER.info(statB.toString());
|
||||||
|
LOGGER.info(statC.toString());
|
||||||
|
|
||||||
LOGGER.info("Is statA and statB equal : {}", statA.equals(statB));
|
LOGGER.info("Is statA and statB equal : {}", statA.equals(statB));
|
||||||
LOGGER.info("Is statA and statC equal : {}", statA.equals(statC));
|
LOGGER.info("Is statA and statC equal : {}", statA.equals(statC));
|
||||||
|
@ -23,10 +23,7 @@
|
|||||||
|
|
||||||
package com.iluwatar.value.object;
|
package com.iluwatar.value.object;
|
||||||
|
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.Value;
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.ToString;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HeroStat is a value object.
|
* HeroStat is a value object.
|
||||||
@ -35,23 +32,10 @@ import lombok.ToString;
|
|||||||
* http://docs.oracle.com/javase/8/docs/api/java/lang/doc-files/ValueBased.html
|
* http://docs.oracle.com/javase/8/docs/api/java/lang/doc-files/ValueBased.html
|
||||||
* </a>
|
* </a>
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Value(staticConstructor = "valueOf")
|
||||||
@ToString
|
class HeroStat {
|
||||||
@EqualsAndHashCode
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class HeroStat {
|
|
||||||
|
|
||||||
// Stats for a hero
|
|
||||||
|
|
||||||
private final int strength;
|
|
||||||
private final int intelligence;
|
|
||||||
private final int luck;
|
|
||||||
|
|
||||||
// Static factory method to create new instances.
|
|
||||||
public static HeroStat valueOf(int strength, int intelligence, int luck) {
|
|
||||||
return new HeroStat(strength, intelligence, luck);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The clone() method should not be public. Just don't override it.
|
|
||||||
|
|
||||||
|
int strength;
|
||||||
|
int intelligence;
|
||||||
|
int luck;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user