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>
Advertisements

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

We’ve made software for the web since day one.

Morton Salt | “When It Rains It Pours®”

“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 iterationCounts 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;
}