Tagged: jpa
how to exclude generated jpa metamodels from being javadoc-ed
<plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-javadoc-plugin</artifactId> <configuration> <sourceFileExcludes> <exclude>**/**_.java</exclude> </sourceFileExcludes> </configuration> </plugin> </plugins>
When are generated primary key values available?
JSR 338: JavaTM Persistence 2.1 says, in 3.5.3, that
Generated primary key values are available in the
PostPersist
method.
It’s a pity that even those ids with GenerationType.TABLE
are not available in the PrePersist
method and it’s a bigger pity that I didn’t rely on the spec.
public class MyEntity { @PrePersist private void onPrePersist() { derived = Stirng.format("%1$016x", id); } @GeneratedValue(generator = "some", strategy = GenerationType.TABLE) @TableGenerator( ... name = "some", ... ) @NotNull private Long id; @Basic(optional = false) @Column(name = "DERIVED", nullable = false, updatable = false) @NotNull private String derived; }
I just found that above code doesn’t work with WildFly while works with Payara.
I have to change my database and the code.
public class MyEntity { @PrePersist private void onPrePersist() { //derived = Stirng.format("%1$016x", id); } @PostPersist private void onPostPersist() { derived = Stirng.format("%1$016x", id); } @GeneratedValue(generator = "some", strategy = GenerationType.TABLE) @TableGenerator( ... name = "some", ... ) @NotNull private Long id; @Basic // optional @Column(name = "DERIVED") // nullable, updatable //@NotNull private String derived; }
finding an entity by its unique attribute value.
public static <T extends BaseEntity, Y> Optional<T> findByUniqueAttribute( final EntityManager entityManager, final Class<T> entityClass, final Supplier<SingularAttribute<? super T, Y>> attributeSupplier, final Supplier<? extends Y> valueSupplier) { final CriteriaBuilder builder = entityManager.getCriteriaBuilder(); final CriteriaQuery<T> criteria = builder.createQuery(entityClass); final Root<T> root = criteria.from(entityClass); criteria.select(root); criteria.where(builder.equal(root.get( attributeSupplier.get()), valueSupplier.get())); try { return Optional.of( entityManager.createQuery(criteria).getSingleResult()); } catch (final NoResultException nre) { return Optional.empty(); } }
public static <T extends BaseEntity, Y> Optional<T> findByUniqueAttribute2( @NotNull final EntityManager entityManager, @NotNull final Class<T> entityClass, @NotNull final Function<ManagedType<T>, SingularAttribute<? super T, Y>> attributeSupplier, @NotNull final Supplier<? extends Y> valueSupplier) { final CriteriaBuilder builder = entityManager.getCriteriaBuilder(); final CriteriaQuery<T> criteria = builder.createQuery(entityClass); final Root<T> root = criteria.from(entityClass); criteria.select(root); criteria.where(builder.equal( root.get(attributeSupplier.apply( entityManager.getMetamodel().entity(entityClass))), valueSupplier.get())); try { return Optional.of( entityManager.createQuery(criteria).getSingleResult()); } catch (final NoResultException nre) { return Optional.empty(); } }
an idea of general service for the persistence context
@Stateless public class PersistenceContextService { @PersistenceContext private EntityManager entityManager; public void accept(final Consumer<EntityManager> consumer) { consumer.accept(entityManager); } public void accept(final BiConsumer<EntityManager, U> consumer final U u) { consumer.accept(entityManager, u); } public <R> R apply(final Function<EnityManager, R> function) { return function.apply(entityManager); } public <U, R> R appply(final BiFunction<EntityManager, U, R> function final U u) { return function.apply(entityManager, u); } }
And now we can do this.
@Path("/people") public class PeopleResource { @Inject private PersistenceContextService service; @POST @Consumes({MediaType.APPLICATION_JSON}) public Response create(final Person person) { service.accept(em -> em.persist(person)); return Response.created(URI.create(person.getId()).build(); } @GET @Path("/{id: \\d}") @Produces({MediaType.APPLICATION_JSON}) public Person read(final long id) { return service.apply(em -> em.find(Person.class, id)); } }
A bug of EclipseLink with Lambda expressions
references
error
GlassFish which ships EclipseLink suddenly started refusing my war file.
...Message] uses a non-entity [class ...Attachment] as target entity in the relationship attribute [field attachments].
new hotness
The problem is that Attachment
class contains a lambda expression.
@XmlAttribute public Long getMessageId() { return ofNullable(message).map(Message::getId()).orElse(null); }
old and busted
@XmlAttribute public Long getMessageId() { return message != null ? message.getId() : null; }
jpa-metamodels-with-maven
Sourcecode
$ git clone git@github.com:jinahya/jpa-metamodels-with-maven-example.git $ cd jpa-metamodels-with-maven $ git checkout develop $ mvn help:all-profiles
Hibernate
With direct dependencies
<dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-jpamodelgen</artifactId> <scope>compile</scope> <optional>true</optional> </dependency> </dependencies>
Using maven-processor-plugin
<build> <plugins> <plugin> <groupId>org.bsc.maven</groupId> <artifactId>maven-processor-plugin</artifactId> <executions> <execution> <id>process</id> <goals> <goal>process</goal> </goals> <phase>generate-sources</phase> <configuration> <processors> <processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor> </processors> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-jpamodelgen</artifactId> <version>${hibernate.version}</version> </dependency> </dependencies> </plugin> </plugins> </build>
Apache OpenJPA
With direct dependencies
It seems that Apache OpenJPA must be triggered with a specific compiler argument.
<dependencies> <dependency> <groupId>org.apache.openjpa</groupId> <artifactId>openjpa</artifactId> <scope>compile</scope> <optional>true</optional> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <compilerArgs> <arg>-Aopenjpa.metamodel=true</arg> </compilerArgs> </configuration> </plugin> </plugins> </build>
Using maven-processor-plugin
Again, Apache OpenJPA requires some additional configuration.
<build> <plugins> <plugin> <groupId>org.bsc.maven</groupId> <artifactId>maven-processor-plugin</artifactId> <executions> <execution> <id>process</id> <goals> <goal>process</goal> </goals> <phase>generate-sources</phase> <configuration> <processors> <processor>org.apache.openjpa.persistence.meta.AnnotationProcessor6</processor> </processors> <optionMap> <openjpa.metamodel>true</openjpa.metamodel> </optionMap> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>org.apache.openjpa</groupId> <artifactId>openjpa</artifactId> <version>${openjpa.version}</version> </dependency> </dependencies> </plugin> </plugins> </build>
EclipseLink
For EclipseLink, the location of persistence.xml must be specified.
Using direct dependencies
<dependencies> <dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId> <scope>compile</scope> <optional>true</optional> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <compilerArgs> <arg>-Aeclipselink.persistencexml=src/main/resources/META-INF/persistence.xml</arg> </compilerArgs> </configuration> </plugin> </plugins> </build>
Using maven-processor-plugin
<build> <plugins> <plugin> <groupId>org.bsc.maven</groupId> <artifactId>maven-processor-plugin</artifactId> <executions> <execution> <id>process</id> <goals> <goal>process</goal> </goals> <phase>generate-sources</phase> <configuration> <processors> <processor>org.eclipse.persistence.internal.jpa.modelgen.CanonicalModelProcessor</processor> </processors> <compilerArguments>-Aeclipselink.persistencexml=src/main/resources/META-INF/persistence.xml</compilerArguments> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId> <version>${eclipselink.version}</version> </dependency> </dependencies> </plugin> </plugins> </build>
DataNucleus
Using direct dependencies
When working with DataNucleus, an additional dependency needs to be specified.
<dependencies> <dependency> <groupId>org.datanucleus</groupId> <artifactId>datanucleus-jpa-query</artifactId> <scope>compile</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.datanucleus</groupId> <artifactId>datanucleus-core</artifactId> <version>${datanucleus.version}</version> <scope>compile</scope> <optional>true</optional> </dependency> </dependencies>
Using maven-processor-plugin
<build> <plugins> <plugin> <groupId>org.bsc.maven</groupId> <artifactId>maven-processor-plugin</artifactId> <executions> <execution> <id>process</id> <goals> <goal>process</goal> </goals> <phase>generate-sources</phase> <configuration> <processors> <processor>org.datanucleus.jpa.query.JPACriteriaProcessor</processor> </processors> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>org.datanucleus</groupId> <artifactId>datanucleus-jpa-query</artifactId> <version>${datanucleus.version}</version> </dependency> <dependency> <groupId>org.datanucleus</groupId> <artifactId>datanucleus-core</artifactId> <version>${datanucleus.version}</version> </dependency> </dependencies> </plugin> </plugins> </build>
general methods and utilities for password encryption
References
Sponsors
Spider Strategies
Morton Salt | “When It Rains It Pours®”
MappedMorton.java
(full source)
There is actually only one class that I’m going to introduce in this blog entry. Much (or Almost) of the codes are came from the referenced blog entry.
public abstract class MappedMorton implements Serializable { protected static final int DENSITY_MIN = 1; protected static final int DENSITY_MAX = 26; protected static final int MAPPED_DENSITY = 16; protected static final int SODIUM_SIZE_MIN = 8; // = 64 / 8 protected static final int SODIUM_SIZE_MAX = 64; // = 512 / 8 protected static final int MAPPED_SODIUM_LENGTH = 32; // = 256 / 8 protected static byte[] pbkdf2(final char[] password, final byte[] salt, final int iterationCount, final int keyLength) { try { final SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); final KeySpec keySpec = new PBEKeySpec( password, salt, iterationCount, keyLength); try { final SecretKey secretKey = secretKeyFactory.generateSecret(keySpec); return secretKey.getEncoded(); } catch (InvalidKeySpecException ikse) { throw new RuntimeException(ikse); } } catch (NoSuchAlgorithmException nsae) { throw new RuntimeException(nsae); } } protected static char[] cassword(final byte[] bassword) { final char[] cassword = new char[bassword.length]; for (int i = 0; i < cassword.length; i++) { cassword[i] = (char) (bassword[i] & 0xFF); } return cassword; } protected static byte[] sodium(final int length) { final byte[] sodium = new byte[length]; try { SecureRandom.getInstance("SHA1PRNG").nextBytes(sodium); } catch (NoSuchAlgorithmException nsae) { throw new RuntimeException(nsae); } return sodium; } protected MappedMorton(final int density, final byte[] sodium) { super(); this.density = density; this.sodium = Arrays.copyOf(sodium, sodium.length); } public MappedMorton() { this(MAPPED_DENSITY, sodium(MAPPED_SODIUM_LENGTH)); } public byte[] salty(final byte[] bland) { final char[] password = cassword(bland); final int degree = 0x01 << density; final int iterationCount = (new BigInteger(bland).intValue() & (degree - 1)) | degree; return pbkdf2(password, sodium, iterationCount, sodium.length * 8); } @Basic(optional = false) @Column(name = "DENSITY", nullable = false, updatable = false) @Min(DENSITY_MIN) @Max(DENSITY_MAX) private int density; @Basic(optional = false) @Column(name = "SODIUM", nullable = false, updatable = false) @NotNull @Size(min = SODIUM_SIZE_MIN, max = SODIUM_SIZE_MAX) private byte[] sodium; }
Note that the iterationCount
s are not fixed and varies by each challenged password.
Here comes a table of testing results.
password (bad) | salty (64 hex) | iteration count | elapsed (ms) |
---|---|---|---|
password | C7BA…697E | 79,158 | 232 |
123456 | C0B6…9BFA | 79,672 | 236 |
12345678 | 4185…958D | 78,387 | 228 |
abc123 | 6360…72F8 | 95,353 | 276 |
qwerty | 8A3B…4501 | 91,513 | 264 |
monkey | 5AE1…929E | 92,526 | 271 |
letmein | FAA8…8812 | 94,062 | 272 |
dragon | 5F2A…A1B6 | 78,129 | 226 |
111111 | 8E25…BDE6 | 93,292 | 267 |
baseball | FAB8…0EA4 | 106,129 | 304 |
Lets take it into a real example.
Morton.java
(full source)
With this extended entity, we increased the default density by 1 which will produce the iterationCount between 2^17(inclusive) and 2^18(exclusive) and extended the the salt size to the maximum (512 bits).
@Entity @Table(name = "MORTON") public class Morton extends MappedMorton { protected static final int DENSITY = MAPPED_DENSITY + 1; protected static final int SODIUM_LENGTH = MAPPED_SODIUM_LENGTH << 1; protected Morton() { super(DENSITY, sodium(SODIUM_LENGTH)); } @Id @GeneratedValue(generator = "MORTON_ID_GENERATOR", strategy = GenerationType.TABLE) @TableGenerator(initialValue = Pkv.INITIAL_VALUE, name = "MORTON_ID_GENERATOR", pkColumnName = Pkv.PK_COLUMN_NAME, pkColumnValue = "MORTON_ID", table = Pkv.TABLE, valueColumnName = Pkv.VALUE_COLUMN_NAME) @NotNull private Long id; }
Shadow
(full source)
This entity is for storing usernames and encrypted passwords.
@Entity @Table(name = "SHADOW") public class Shadow implements Serializable { public static Shadow newInstance(final String username, final byte[] password) { final Shadow instance = new Shadow(); instance.username = username; instance.passsalt = new Morton(); instance.passcode = instance.passsalt.salty(password); return instance; } public boolean nassword(final Shadow reference, final byte[] password, final byte[] nassword) { if (!puthenticate(reference, password)) { return false; } passsalt = new Morton(); passcode = passsalt.salty(nassword); return true; } public boolean puthenticate(final Shadow reference, final byte[] password) { return Arrays.equals(passsalt.salty(password), passcode); } @Id @GeneratedValue(generator = "SHADOW_ID_GENERATOR", strategy = GenerationType.TABLE) @TableGenerator(initialValue = Pkv.INITIAL_VALUE, name = "SHADOW_ID_GENERATOR", pkColumnName = Pkv.PK_COLUMN_NAME, pkColumnValue = "SHADOW_ID", table = Pkv.TABLE, valueColumnName = Pkv.VALUE_COLUMN_NAME) @NotNull @XmlTransient private Long id; @Basic(optional = false) @Column(name = "USERNAME", nullable = false, unique = true, updatable = false) @NotNull @Size(min = USERNAME_SIZE_MIN, max = USERNAME_SIZE_MAX) private String username; @JoinColumn(name = "PASSSALT_ID", nullable = false) @OneToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}, optional = false, orphanRemoval = true) @NotNull private Morton passsalt; @Basic(optional = false) @Column(length = Morton.SODIUM_LENGTH, name = "PASSCODE", nullable = false) @NotNull @Size(min = Morton.SODIUM_LENGTH, max = Morton.SODIUM_LENGTH) private byte[] passcode; }
Self-Referencing One-to-Many Bidirectional Relationship
@Entity public class Person { /** * Adopts given <code>child</code>. * * @param child the child to be adopted. */ public void adopt(final Person child) { if (child == null) { throw new NullPointerException("null child"); } if (child.parent != null) { // how cruel child.parent.getChildren().remove(child); child.parent = null; } child.parent = this; getChildren().add(child); } /** * Returns the children of this person. * * @return the children of this person */ public List<Person> getChildren() { if (children == null) { children = new ArrayList<Person>(); } return children; } /** * id. */ @Id @XmlAttribute @XmlID @XmlJavaTypeAdapter(XmlIDAdapter.class) // MOXy doesn't need this private Long id; @Basic @Column private String name; @Basic @Column private int age; /** * parent. */ @JoinColumn @ManyToOne @XmlAttribute(name = "parentId") @XmlIDREF private Person parent; /** * children. */ @JoinColumn @OneToMany(mappedBy = "parent") @XmlAttribute(name = "childIds") @XmlIDREF @XmlList private List<Person> children; }
public class XmlIDAdapter extends XmlAdapter<String, Long> { @Override public String marshal(final Long bound) throws Exception { if (bound == null) { return null; } return DatatypeConverter.printLong(bound); } @Override public Long unmarshal(final String value) throws Exception { if (value == null) { return null; } return DatatypeConverter.parseLong(value); } }