The second demo marks an evolution of our model. Now there are 10 tables.
This demo presents the track JPA2 of minuteproject.
Now we want to know:
- Who translate what via translation request.
- What a user can speak and can translate.
In the current diagram there are multiple many-to-many relationships
- request_key
- application_x_key
- language_x_translator
- language_x_speaker
And 2 (language_x_translator and language_x_speaker) link twice user to language...
This demo will illustrate:
- Enrichment facilities and customisation
- Generation for JPA2
- Integration technics of resulting artefacts
Model source
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';
DROP SCHEMA IF EXISTS `tranxy` ;
CREATE SCHEMA IF NOT EXISTS `tranxy` DEFAULT CHARACTER SET latin1;
USE `tranxy` ;
DROP TABLE IF EXISTS `tranxy`.`traduction` ;
DROP TABLE IF EXISTS `tranxy`.`translation_key` ;
DROP TABLE IF EXISTS `tranxy`.`language` ;
-- -----------------------------------------------------
-- Table `tranxy`.`translation_key`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `tranxy`.`translation_key` ;
CREATE TABLE IF NOT EXISTS `tranxy`.`translation_key` (
`id` BIGINT NOT NULL AUTO_INCREMENT ,
`key_name` VARCHAR(200) NULL ,
`description` VARCHAR(400) NULL ,
PRIMARY KEY (`id`) )
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `tranxy`.`language`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `tranxy`.`language` ;
CREATE TABLE IF NOT EXISTS `tranxy`.`language` (
`idlanguage` INT NOT NULL AUTO_INCREMENT ,
`code` VARCHAR(45) NOT NULL ,
`locale` VARCHAR(10) NOT NULL ,
`description` VARCHAR(45) NOT NULL ,
PRIMARY KEY (`idlanguage`) ,
UNIQUE INDEX `code_UNIQUE` (`code` ASC) )
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `tranxy`.`user`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `tranxy`.`user` ;
CREATE TABLE IF NOT EXISTS `tranxy`.`user` (
`iduser` BIGINT NOT NULL AUTO_INCREMENT ,
`first_name` VARCHAR(45) NOT NULL ,
`last_name` VARCHAR(45) NOT NULL ,
`email` VARCHAR(200) NULL ,
PRIMARY KEY (`iduser`) )
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `tranxy`.`translation`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `tranxy`.`translation` ;
CREATE TABLE IF NOT EXISTS `tranxy`.`translation` (
`id` BIGINT NOT NULL AUTO_INCREMENT ,
`translation` VARCHAR(800) NULL ,
`language_id` INT NOT NULL ,
`Key_id` BIGINT NOT NULL ,
`is_final` TINYINT NOT NULL ,
`date_finalization` DATE NULL ,
`translator_id` BIGINT NOT NULL ,
PRIMARY KEY (`id`) ,
INDEX `fk_traduction_language` (`language_id` ASC) ,
INDEX `fk_traduction_Key1` (`Key_id` ASC) ,
INDEX `fk_traduction_user1` (`translator_id` ASC) ,
CONSTRAINT `fk_traduction_language`
FOREIGN KEY (`language_id` )
REFERENCES `tranxy`.`language` (`idlanguage` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_traduction_Key1`
FOREIGN KEY (`Key_id` )
REFERENCES `tranxy`.`translation_key` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_traduction_user1`
FOREIGN KEY (`translator_id` )
REFERENCES `tranxy`.`user` (`iduser` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `tranxy`.`application`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `tranxy`.`application` ;
CREATE TABLE IF NOT EXISTS `tranxy`.`application` (
`idapplication` BIGINT NOT NULL AUTO_INCREMENT ,
`name` VARCHAR(100) NULL ,
`description` VARCHAR(200) NULL ,
`type` VARCHAR(45) NOT NULL ,
PRIMARY KEY (`idapplication`) ,
UNIQUE INDEX `name_UNIQUE` (`name` ASC) )
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `tranxy`.`application_x_key`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `tranxy`.`application_x_key` ;
CREATE TABLE IF NOT EXISTS `tranxy`.`application_x_key` (
`Key_id` BIGINT NOT NULL ,
`application_id` BIGINT NOT NULL ,
INDEX `fk_application_x_key_Key1` (`Key_id` ASC) ,
INDEX `fk_application_x_key_application1` (`application_id` ASC) ,
PRIMARY KEY (`Key_id`, `application_id`) ,
CONSTRAINT `fk_application_x_key_Key1`
FOREIGN KEY (`Key_id` )
REFERENCES `tranxy`.`translation_key` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_application_x_key_application1`
FOREIGN KEY (`application_id` )
REFERENCES `tranxy`.`application` (`idapplication` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `tranxy`.`translation_request`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `tranxy`.`translation_request` ;
CREATE TABLE IF NOT EXISTS `tranxy`.`translation_request` (
`idtranslation_request` BIGINT NOT NULL AUTO_INCREMENT ,
`name` VARCHAR(45) NULL ,
`request_date` DATE NOT NULL ,
`user_id` BIGINT NOT NULL ,
`Key_id` BIGINT NOT NULL ,
PRIMARY KEY (`idtranslation_request`) ,
INDEX `fk_translation_request_user1` (`user_id` ASC) ,
INDEX `fk_translation_request_Key1` (`Key_id` ASC) ,
CONSTRAINT `fk_translation_request_user1`
FOREIGN KEY (`user_id` )
REFERENCES `tranxy`.`user` (`iduser` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_translation_request_Key1`
FOREIGN KEY (`Key_id` )
REFERENCES `tranxy`.`translation_key` (`id` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `tranxy`.`request_key`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `tranxy`.`request_key` ;
CREATE TABLE IF NOT EXISTS `tranxy`.`request_key` (
`translation_request_id` BIGINT NOT NULL ,
`language_id` INT NOT NULL ,
INDEX `fk_request_key_translation_request1` (`translation_request_id` ASC) ,
PRIMARY KEY (`translation_request_id`, `language_id`) ,
INDEX `fk_request_key_language1` (`language_id` ASC) ,
CONSTRAINT `fk_request_key_translation_request1`
FOREIGN KEY (`translation_request_id` )
REFERENCES `tranxy`.`translation_request` (`idtranslation_request` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_request_key_language1`
FOREIGN KEY (`language_id` )
REFERENCES `tranxy`.`language` (`idlanguage` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `tranxy`.`language_x_translator`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `tranxy`.`language_x_translator` ;
CREATE TABLE IF NOT EXISTS `tranxy`.`language_x_translator` (
`language_id` INT NOT NULL ,
`user_id` BIGINT NOT NULL ,
PRIMARY KEY (`language_id`, `user_id`) ,
INDEX `fk_request_key_language1` (`language_id` ASC) ,
INDEX `fk_language_x_translator_user1` (`user_id` ASC) ,
CONSTRAINT `fk_request_key_language10`
FOREIGN KEY (`language_id` )
REFERENCES `tranxy`.`language` (`idlanguage` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_language_x_translator_user1`
FOREIGN KEY (`user_id` )
REFERENCES `tranxy`.`user` (`iduser` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `tranxy`.`language_x_speaker`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `tranxy`.`language_x_speaker` ;
CREATE TABLE IF NOT EXISTS `tranxy`.`language_x_speaker` (
`language_id` INT NOT NULL ,
`user_id` BIGINT NOT NULL ,
PRIMARY KEY (`language_id`, `user_id`) ,
INDEX `fk_request_key_language1` (`language_id` ASC) ,
INDEX `fk_language_x_translator_user1` (`user_id` ASC) ,
CONSTRAINT `fk_request_key_language100`
FOREIGN KEY (`language_id` )
REFERENCES `tranxy`.`language` (`idlanguage` )
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT `fk_language_x_translator_user10`
FOREIGN KEY (`user_id` )
REFERENCES `tranxy`.`user` (`iduser` )
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
Enrichment facilities and customisation
Minuteproject configuration allows to define naming conventions working globally and allow specific enrichment whose granularity is limited to a table or a field.
New configuration
TRANXY-JPA2-2.xml
<!DOCTYPE root>
<generator-config xmlns="http://minuteproject.sf.net/xsd/mp-config"
xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:noNamespaceSchemaLocation="../config/mp-config.xsd">
<configuration>
<conventions>
<target-convention type="enable-updatable-code-feature" />
</conventions>
<model name="tranxy" version="1.0" package-root="net.sf.mp.demo">
<data-model>
<driver name="mysql" version="5.1.16" groupId="mysql"
artifactId="mysql-connector-java"></driver>
<dataSource>
<driverClassName>org.gjt.mm.mysql.Driver</driverClassName>
<url>jdbc:mysql://127.0.0.1:3306/tranxy</url>
<username>root</username>
<password>mysql</password>
</dataSource>
<primaryKeyPolicy oneGlobal="false" >
<primaryKeyPolicyPattern name="autoincrementPattern"></primaryKeyPolicyPattern>
</primaryKeyPolicy>
</data-model>
<business-model>
<business-package default="tranxy">
<condition type="package" startsWith="trans" result="translation"></condition>
</business-package>
<enrichment>
<conventions>
<!-- manipulate the structure and entities BEFORE manipulating the
entities -->
<column-naming-convention type="apply-fix-primary-key-column-name-when-no-ambiguity"
default-value="ID"/>
<column-naming-convention type="apply-strip-column-name-suffix"
pattern-to-strip="ID" />
<reference-naming-convention
type="apply-referenced-alias-when-no-ambiguity" is-to-plurialize="true" />
<reference-naming-convention type="apply-many-to-many-aliasing" is-to-plurialize="true"/>
</conventions>
<entity name="language_x_translator">
<field name="language_id" linkReferenceAlias="translating_language" linkToTargetEntity="LANGUAGE"/>
<field name="user_id" linkReferenceAlias="translator" linkToTargetEntity="USER"/>
</entity>
<entity name="LANGUAGE_X_SPEAKER">
<field name="LANGUAGE_ID" linkToTargetEntity="LANGUAGE"
linkToTargetField="IDLANGUAGE" linkReferenceAlias="spoken_language" />
<field name="user_id" linkReferenceAlias="speaker" linkToTargetEntity="USER"/>
</entity>
<entity name="APPLICATION" alias="registered application">
<field name="TYPE" alias="obedience">
<property tag="checkconstraint" alias="application_type">
<property name="OPENSOURCE"/>
<property name="COPYRIGHT" />
</property>
</field>
</entity>
<entity name="LANGUAGE" content-type="reference-data"/>
</enrichment>
</business-model>
</model>
<targets>
<target refname="JPA2" fileName="mp-template-config-JPA2.xml"
outputdir-root="../../dev/latvianjug/output/JPA2" templatedir-root="../../template/framework/jpa">
<property name="add-querydsl" value="2.1.2"></property>
<property name="add-jpa2-implementation" value="hibernate"></property>
<property name="add-cache-implementation" value="ehcache"></property>
</target>
<target refname="CACHE-LIB" fileName="mp-template-config-CACHE-LIB.xml"
templatedir-root="../../template/framework/cache">
</target>
<target refname="LIB" fileName="mp-template-config-bsla-LIB-features.xml"
templatedir-root="../../template/framework/bsla">
</target>
</targets>
</configuration>
</generator-config>
Global conventions
Make database convention java-friendly.Fix primary key variable
<column-naming-convention type="apply-fix-primary-key-column-name-when-no-ambiguity"
default-value="ID"/>
In Language class the variable + getter/setter are related to 'id' althought mapped to 'idlanguage'.
@Id @Column(name="idlanguage" )
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
Give readable collection name
When there is no ambiguity (only one foreign key between 2 entities), there is the possibility to have the collection variable made of the name of the linked table plurialized.<reference-naming-convention
type="apply-referenced-alias-when-no-ambiguity" is-to-plurialize="true" />
In TranslationKey
private Set <translation> translations = new HashSet<translation>();
Simplify many-to-many relationship
Simplify many-to-many relationship variable when there is an ambiguity. In fact language_x_translator and language_x_speaker both user and language. So it is by default not possible to add 'users' variable to language since there should be 2 'users' variable. And vice-versa for user entity where 'languages' collection variable would be duplicate. This means that by default the variable is ambiguous and its name is a combination of link table foreign key and the other-end table, which gives quite a complexe name to read. The convention below in combination to some enrichment offers the possibility to get simple names declaratively.<reference-naming-convention type="apply-many-to-many-aliasing" is-to-plurialize="true"/>Combined with many-to-many tables enrichment
<entity name="language_x_translator">
<field name="language_id" linkReferenceAlias="translating_language" linkToTargetEntity="LANGUAGE"/>
<field name="user_id" linkReferenceAlias="translator" linkToTargetEntity="USER"/>
</entity>
<entity name="LANGUAGE_X_SPEAKER">
<field name="LANGUAGE_ID" linkToTargetEntity="LANGUAGE"
linkToTargetField="IDLANGUAGE" linkReferenceAlias="spoken_language" />
<field name="user_id" linkReferenceAlias="speaker" linkToTargetEntity="USER"/>
</entity>
Gives for User entity
@ManyToMany
@JoinTable(name="LANGUAGE_X_SPEAKER",
joinColumns=@JoinColumn(name="user_id"),
inverseJoinColumns=@JoinColumn(name="language_id")
)
private Set <language> spokenLanguages = new HashSet <Language> ();
@ManyToMany
@JoinTable(name="LANGUAGE_X_TRANSLATOR",
joinColumns=@JoinColumn(name="user_id"),
inverseJoinColumns=@JoinColumn(name="language_id")
)
private Set <language> translatingLanguages = new HashSet <Language> ();
Gives for Language entity
@ManyToMany
@JoinTable(name="LANGUAGE_X_SPEAKER",
joinColumns=@JoinColumn(name="language_id"),
inverseJoinColumns=@JoinColumn(name="user_id")
)
private Set <user> speakers = new HashSet <User> ();
@ManyToMany
@JoinTable(name="LANGUAGE_X_TRANSLATOR",
joinColumns=@JoinColumn(name="language_id"),
inverseJoinColumns=@JoinColumn(name="user_id")
)
private Set <user> translators = new HashSet <User> ();
Local enrichment
Fine grain tuning: Granularity at the level of the entity or attribute.Modify the name of the entity with alias
<entity name="APPLICATION" alias="registered application" >Gives
@Entity (name="RegisteredApplication") @Table (name="application") public class RegisteredApplication ...
Modify the name of the field with alias
<field name="TYPE" alias="obedience" >Gives
@Column(name="type")
private ApplicationType obedience;
Add enumeration
<field name="TYPE" alias="obedience" >
<property tag="checkconstraint" alias="application_type">
<property name="OPENSOURCE"/>
<property name="COPYRIGHT" />
</property>
</field>
Gives
@Enumerated (EnumType.STRING)
@Column(name="type")
private ApplicationType obedience;
And a Enum artefact
public enum ApplicationType {
OPENSOURCE("OPENSOURCE"),
COPYRIGHT("COPYRIGHT");
private final String value;
...
Providing content type of an entity
<entity name="LANGUAGE" content-type="reference-data"/>Gives an entry in ehcache.xml configuration
<cache
name="net.sf.mp.demo.tranxy.domain.tranxy.Language"
maxElementsInMemory="5000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="false"
/>
Generation
Same as for demo one:
Put TRANXY-JPA2-2.xml in /mywork/config
Run
>model-generation.cmd TRANXY-JPA2-2.xml
>model-generation.cmd TRANXY-JPA2-2.xml
But what happened to my altered code?
It is kept. Your validation annotation are not erased!
Be ready for continuous-refactoring of your backend!
Build and test
The model has changed and 2 new fields are mandatory to create a Language. The unit test is modified.package mytest;
import javax.persistence.*;
import javax.validation.*;
import javax.validation.constraints.*;
import static junit.framework.Assert.*;
import org.junit.*;
import net.sf.mp.demo.tranxy.domain.tranxy.*;
public class TranxyTest {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("tranxy");
EntityManager em = emf.createEntityManager();
@Test
public void testLanguage() {
EntityTransaction tx = em.getTransaction();
tx.begin();
Language language = new Language();
language.setCode("FR");
//demo2
language.setDescription("France");
language.setLocale("fr");
em.persist(language);
tx.commit();
}
@Test
public void testTooSmallLanguage() {
try {
EntityTransaction tx = em.getTransaction();
tx.begin();
Language language = new Language();
language.setCode("F");
em.persist(language);
fail("Expected ConstraintViolationException wasn't thrown.");
tx.commit();
}
catch (ConstraintViolationException e) {
assertEquals(1, e.getConstraintViolations().size());
ConstraintViolation violation =
e.getConstraintViolations().iterator().next();
assertEquals("code", violation.getPropertyPath().toString());
assertEquals(
Size.class,
violation.getConstraintDescriptor().getAnnotation().annotationType());
}
}
@Before
public void clean () {
EntityTransaction tx = em.getTransaction();
tx.begin();
Query q = em.createQuery("delete Language");
int i = q.executeUpdate();
tx.commit();
}
}
To build run>mvn clean package
Integration technics
Two are standard- integration by extension
- integration by overriding
- Integration by alteration/mutation
- 3 types of alteration
- artifact level (exclude for next generation)
- snippet level
- added part
- updatable part

No comments:
Post a Comment