A lesson of @Transactional


Recently, I needed to work with a Cursor for statistics.

<mapper ...>
  <select id="selectCorsor" resultOrdered="true">
  </select>
</mapper>

It didn’t make any difference.

@Mapper
public interface SomeMapper {
    Cursor<Some> selectCursor(...);
}

We need the @Transactional annotation for working with Cursor.

@Service
public class SomeService {

    @Transactional
    public <R> R applyCursor(
            ...,
            final Function<Cursor<Some>, R> function) {
         return function.apply(someMapper.selectCursor(..));
    }

    @Autowired
    private SomeMapper someMapper;
}

Well the method worked as expected.

The problem arose when I added a method using the origin method.

    public void acceptEach(
            ...,
            final Consumer<Some> consumer) {
        applyCursor(
            ...,
            cursor -> {
                cursor.forEach(consumer::accept);
                return null;
            }
        );
    }

This auxiliary method was not annotated with @Transactional and it didn’t work.

And I found @Transactional method calling another method without @Transactional anotation?.

The acceptEach method was also required to be annotated with @Transactional​.

Advertisements

Using Cursors with MyBatis


There are queries that you shouldn’t map as List<T>.

And the Cursor comes to play.

@Service
public class SomeService {

    public Cursor<Some> getCursor(...) {
        // returns the cursor?
    }
}

Well it not a good way to do with it. The return value Cursor<Some> won’t work because the session may already be finished when the method returns.

@Service
public class SomeService {

    @Transactional
    public <R> R applyCursor(
            ...,
            Function<Cursor<Some>, R> function) {
        final Cursor<Some> cursor = ...;
        return function.apply(cursor);
    }
}

Now we can add some other methods to do with it.

    public <R> R applyIterator(
            ...,
            Function<Iterator<Some>, R> function) {
        applyCursor(
                ...,
                cursor -> function.apply(cursor.iterator())
        );
    }

What about a Spliterator?

    public <R> R applySpliterator(
            ...,
            Function<Spliterator<Some>, R> function) {
        applyIterator(
                ...,
                iterator -> {
                    int characteristics
                        = Spliterator.DISTINCT
                          | Spliterator.IMMUTABLE
                          | Spliterator.NONNULL
                          | Spliterator.ORDERED;
                    Spliterator<CsEventAssociate> spliterator
                        = Spliterators.spliteratorUnknownSize(
                                iterator, characteristics);
                    return function.apply(spliterator);
                }
        );
    }

And the Stream?

    public <R> R applyStream(
            ...,
            Function<Stream<Some>, R> function) {
        return applySpliterator(
            ...,
            spliterator -> function.apply(StreamSupport.stream(spliterator, false))
        );
    }

Getting a Substring in MySQL


I just learned how to get substring of specific range using LOCATE() and SUBSTRING().

Let’s say we have email addresses and want to get username part from them.

Step 1 Locate the index of ‘@’ character

LOCATE('@', email)

Step 2 Get substring using it.

SUBSTRING(email, 1, LOCATE('@', email) - 1)

Note that the starting index is 1 and the the len part must be 1 minus the location.

Wait.

There is a function exactly does this. SUBSTRING_INDEX().

SUBSTRING_INDEX(email, '@', 1)

Injecting Property Values into EJBs from Database


references

Property.java

@Entity
@NamedQueries({
@NamedQuery(name = "Property.find",
            query = "SELECT p FROM Property AS p WHERE p.name = :name")
})
public class Property {

    @Basic(optional = false)
    @Column(name = "NAME", nullable = false, unique = true)
    @NotNull
    private String name;

    @Basic(Optional = false)
    @Column(name = "VALUE_", nullable = false)
    @NotNull
    private String value = "";
}

find

public static Property findNamed(final EntityManager entityManager,
                                 final String name) {
    final TypedQuery typedQuery
        = entityManager.createNamedQuery("Property.find", Property.class);
    typedQuery.setParameter("name", name);
    try {
        return typedQuery.getSingleResult();
    } catch (final NoResultException nre) {
        return null;
    }
}

public static Property findCriteria(final EntityManager entityManager,
                                    final String name) {
    final CriteriaBuilder criteriaBuilder
        = entityManager.getCriteriaBuilder();
    final CriteriaQuery criteriaQuery
        = criteriaBuilder.createQuery(Property.class);
    final Root<Property> property = criteriaQuery.from(Property.class);
    criteriaQuery.select(property);
    criteriaQuery.where(
        criteriaBuilder.equal(property.get(Property_.name), name));
    final TypedQuery typedQuery
        = entityManager.createQuery(criteriaQuery);
    try {
        return typedQuery.getSingleResult();
    } catch (final NoResultException nre) {
        return null;
    }
}

public static Property find(final EntityManager entityManager,
                            final String name) {
    if (current().nextBoolean()) {
        return findNamed(entityManager, name);
    }
    return findCriteria(entityManager, name);
}

PropertyValue.java

@Qualifier
@Retention(RUNTIME)
@Target({FIELD, METHOD, PARAMETER, TYPE})
public @interface PropertyValue {

    @Nonbinding
    String name() default "";

    @Nonbinding
    boolean optional() default true;
}

PropertyFactory.java

@Dependent
public class PropertyValueFactory {

@Produces
@PropertyValue
public String producePropertyValue(final InjectionPoint injectionPoint) {
    final PropertyValue annotation
        = injectionPoint.getAnnotated().getAnnotation(PropertyValue.class);
    final String name = annotation.name();
    final boolean optional = annotation.optional();
    final Property property = Property.find(entityManger, name);
    if (property == null && !optional) {
        throw new InjectionException("property not found; name= " + name);
    }
    return ofNullable(property).map(Property::getValue).orElse(null);
}

public void diposePropertyValue(
    @Disposes @PropertyValue final String value) {
    // empty


@PersistenceContext
private EntityManager entityManger;
}

StorageObjectServiceFtp.java

 @Stateless
 public class StorageObjectServiceFtp extends StorageObjectService {

    @Inject
    @PropertyValue(name = "ftp.hostname", optional = false)
    private String hostname;

    @Inject
    @PropertyValue(name = "ftp.username", optional = false)
    private String username;

    @Inject
    @PropertyValue(name = "ftp.password", optional = false)
    private String password;

    @Inject
    @PropertyValue(name = "ftp.rootpath", optional = true)
    private String rootpath;
}