Archive

Posts Tagged ‘java8’

Creating an object stream from a JDBC ResultSet

08/14/2014 6 comments

The introduction of  features Stream API and Lambda in Java 8 enables us to make an elegant conversion from a JDBC ResultSet to a stream of objects just providing a mapping function. Such function could be, of course, a lambda.

Basically, the idea is to generate a Stream using a ResultSet as Supplier:

public class ResultSetSupplier implements Supplier<T>{

		private final ResultSet rs;
		private final Function<ResultSet, T> mappingFunction;

		private ResultSetSupplier(ResultSet rs,
                    Function<ResultSet, T> mappingFunction) {
			this.rs = rs;
			this.mappingFunction = mappingFunction;
		}

		@Override
		public T get() {
			try {
				if (rs.next())
					return mappingFunction.apply(rs);
			} catch (SQLException e) {
				e.printStackTrace();
			}
			return null;
		}
	}

Parameter mappingFunction, which might be a lambda expression, is used to build T instances from a ResultSet. Just like ActiveRecord pattern, every row in such ResultSet maps to an instance of T, where columns are attributes of T.

Let’s consider class City:

public class City{
		String city;
		String country;
		public City(String city, String country) {
			this.city = city;
			this.country = country;
		}
		public String getCountry() {
			return country;
		}
		@Override
		public String toString() {
			return "City [city=" + city + ", country=" + country + ";]";
		}
		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + ((city == null) ? 0 : city.hashCode());
			result = prime * result
					+ ((country == null) ? 0 : country.hashCode());
			return result;
		}
		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			City other = (City) obj;
			if (city == null) {
				if (other.city != null)
					return false;
			} else if (!city.equals(other.city))
				return false;
			if (country == null) {
				if (other.country != null)
					return false;
			} else if (!country.equals(other.country))
				return false;
			return true;
		}
	}

The mapping function for City objects could be a lambda expression like the following:

(ResultSet rs) -> {
                try
                {
                  return new City(rs.getString("city"), rs.getString("country"));
		} catch (Exception e) {
		  return null;
		}}

We have assumed database columns are called city and country, respectively.

Although both PreparedStatement and ResultSet implement AutoCloseable interface, as a  resultSet must be provided to create the object stream, it does make sense to close such resultSet when the stream is closed as well.

A possible approach could be to use a proxy to intercept method invocation on the object stream. Thus, as close() method is invoked on the proxy, it will invoke close() on the provided resultSet. All method invocations will be invoked on the object stream as well, in order to be able to provide all Stream features. That is easy to achieve using a proxy.

Let’s have a look. We will have a proxy factory and a invocation handler:

public class ResultSetStreamInvocationHandler<T> implements InvocationHandler{

  private Stream<T> stream; // proxy will intercept method calls to such stream
  private PreparedStatement st;
  private ResultSet rs;

  public void setup(PreparedStatement st, Function<ResultSet, T> mappingFunction)
  throws SQLException{
    // PreparedStatement must be already setup in order
    // to just call executeQuery()
    this.st = st;
    rs = st.executeQuery();
    stream = Stream.generate(new ResultSetSupplier(rs, mappingFunction));
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args)
  throws Throwable {

    if (method == null)
      throw new RuntimeException("null method null");

    // implement AutoCloseable for PreparedStatement
    // as calling close() more than once has no effects
    if (method.getName().equals("close") && args == null){
    // invoked close(), no arguments
      if (st != null){
        st.close(); // closes ResultSet too
      }
    }

    return method.invoke(stream, args);
  }

private class ResultSetSupplier implements Supplier<T>{

  private final ResultSet rs;
  private final Function<ResultSet, T> mappingFunction;

  private ResultSetSupplier(ResultSet rs, Function<ResultSet, T> mappingFunction) {
    this.rs = rs;
    this.mappingFunction = mappingFunction;
  }

  @Override
  public T get() {
    try {
      if (rs.next())
        return mappingFunction.apply(rs);
    } catch (SQLException e) {
     e.printStackTrace();
    }
    return null;
  }
}

}

Please note how invoke is used to intercept method calls. In case close() is called, close() is called on PreparedStatement as well. For every method called, the corresponding method call is invoked in the stream being proxied.

And the factory:

 

public class ResultSetStream<T>{

	@SuppressWarnings("unchecked")
	public Stream<T> getStream(PreparedStatement st,
            Function<ResultSet, T> mappingFunction) throws SQLException{
		final ResultSetStreamInvocationHandler<T> handler =
                    new ResultSetStreamInvocationHandler<T>();
		handler.setup(st, mappingFunction);
		Stream<T> proxy = (Stream<T>) Proxy.newProxyInstance(getClass().getClassLoader(),
                new Class<?>[] {Stream.class},
                handler);
		return proxy;
	}
}

To put it all together, let’s write a simple test to show usage. Mockito will be used to mock both PreparedStatement and ResultSet to avoid running tests against a real database.


public class ResultSetStreamTest {

	private class City{
		String city;
		String country;
		public City(String city, String country) {
			this.city = city;
			this.country = country;
		}
		public String getCountry() {
			return country;
		}
		@Override
		public String toString() {
			return "City [city=" + city + ", country=" + country + "]";
		}
		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + getOuterType().hashCode();
			result = prime * result + ((city == null) ? 0 : city.hashCode());
			result = prime * result
					+ ((country == null) ? 0 : country.hashCode());
			return result;
		}
		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			City other = (City) obj;
			if (!getOuterType().equals(other.getOuterType()))
				return false;
			if (city == null) {
				if (other.city != null)
					return false;
			} else if (!city.equals(other.city))
				return false;
			if (country == null) {
				if (other.country != null)
					return false;
			} else if (!country.equals(other.country))
				return false;
			return true;
		}
		private ResultSetStreamTest getOuterType() {
			return ResultSetStreamTest.this;
		}
	}

	private String[][] data = new String[][]{
			{"Karachi", "Pakistan"},
			{"Istanbul", "Turkey"},
			{"Hong Kong", "China"},
			{"Saint Petersburg", "Russia"},
			{"Sydney", "Australia"},
			{"Berlin", "Germany"},
			{"Madrid", "Spain"}
		};

	private int timesCalled;
	private PreparedStatement mockPST;
	private ResultSet mockRS;

	@Before
	public void setup() throws SQLException{
		timesCalled = -1;
		mockRS = mock(ResultSet.class);
		mockPST = mock(PreparedStatement.class);

		when(mockRS.next()).thenAnswer(new Answer<Boolean>() {

			@Override
			public Boolean answer(InvocationOnMock invocation) throws Throwable {
				if (timesCalled++ &gt;= data.length)
					return false;
				return true;
			}
		});

		when(mockRS.getString(eq("city"))).thenAnswer(new Answer<String>() {

			@Override
			public String answer(InvocationOnMock invocation) throws Throwable {
				return data[timesCalled][0];
			}
		});
		when(mockRS.getString(eq("country"))).thenAnswer(new Answer<String>() {

			@Override
			public String answer(InvocationOnMock invocation) throws Throwable {
				return data[timesCalled][1];
			}
		});

		when(mockPST.executeQuery()).thenReturn(mockRS);
	}

	@Test
	public void simpleTest() throws SQLException{

		try (Stream<City> testStream = new ResultSetStream<City>().getStream(mockPST,
				(ResultSet rs) -> {try {
					return new City(rs.getString("city"), rs.getString("country"));
				} catch (Exception e) {
					return null;
				}})){

			Iterator<City> cities = testStream.filter(
					city -> !city.getCountry().equalsIgnoreCase("China"))
					.limit(3).iterator();

			assertTrue(cities.hasNext());
			assertEquals(new City("Karachi", "Pakistan"), cities.next());

			assertTrue(cities.hasNext());
			assertEquals(new City("Istanbul", "Turkey"), cities.next());

			assertTrue(cities.hasNext());
			assertEquals(new City("Saint Petersburg", "Russia"), cities.next());

			assertFalse(cities.hasNext());
		}

	}

}

Download full source code on Github.

Advertisements

Java yield-like using Stream API

08/11/2014 1 comment

Several programming languages, such as Ruby or Python to name a few, provides the yield command. Yield provides an effective way, in terms of memory consumption, to create series of values, by generating such values on demand. More information on Python Yield.

Let’s consider a class or method requiring a huge amount of secure random integers. The classical approach would be to create an array or collection of such integers. Yield provides two major advantages over such approach:

  • yield does not require to know the length of the series in advance.
  • yield does not require to store all values in memory.

Fortunately, yield features can be used in Java 8 thanks to Stream API:

 

 

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Date;
import java.util.function.Supplier;
import java.util.stream.Stream;

public class Yield {

	private static final Integer RANDOM_INTS = 10;

	public static void main(String[] args) {

		try (Stream randomInt = generateRandomIntStream()){
			Object[] randomInts = randomInt.limit(RANDOM_INTS)
                                .sorted().toArray();
			for (int i = 0; i < randomInts.length;i++)
				System.out.println(randomInts[i]);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
	}

	private static Stream generateRandomIntStream()
           throws NoSuchAlgorithmException{
		return Stream.generate(new Supplier() {

			final SecureRandom random = SecureRandom
                                .getInstance("SHA1PRNG");
			boolean init = false;
			int numGenerated = 0;

			@Override
			public Integer get() {
				if (!init){
					random.setSeed(new Date().getTime());
					init = true;
					System.out.println("Seeding");
				}
				final int nextInt = random.nextInt();
				System.out.println("Generated random "
                                         + numGenerated++
                                         + ": " + nextInt);
				return nextInt;
			}

		});
	}

}

Following is the output after provided code snippet is executed:

Seeding
Generated random 0: -896358073
Generated random 1: -1268521873
Generated random 2: 9627917
Generated random 3: -2106415441
Generated random 4: 935583477
Generated random 5: -1132421439
Generated random 6: -1324474601
Generated random 7: -1768257192
Generated random 8: -566921081
Generated random 9: 425501046
-2106415441
-1768257192
-1324474601
-1268521873
-1132421439
-896358073
-566921081
9627917
425501046
935583477

It is easy to see that Supplier is only instantiated one. Of course, we can take advantage of all Stream API features such as limit() and sorted().

The line randomInt.limit(RANDOM_INTS).sorted().toArray() triggers the generation of RANDOM_INTS values which are then sorted and stored as an array.

Casi decidida la sintaxis de las closures para Java 8

09/15/2011 1 comment

Después de mucho discutir sobre si el lenguaje java debería o no tener closures, al final se decidió que si. Luego tocaba la, no menos complicada, labor de decidir la sintaxis. Algunas de las propuestas fueron:

#{Foo foo -> foo.bar()}

int(int a, int b) { return a – b; }

….

Pero al final parece que ha ganado la sintaxis utilizada en C# y scala:

lambda = ArgList Arrow Body
   ArgList = Identifier
             | "(" Identifier [ "," Identifier ]* ")"
             | "(" Type Identifier [ "," Type Identifier ]* ")"
   Body = Expression
             | "{" [ Statement ";" ]+ "}"

Algunos ejemplos serían:

  1. x => x + 1
  2. (x) => x + 1
  3. (int x) => x + 1
  4. (int x, int y) => x + y
  5. (x, y) => x + y
  6. (x, y) => { System.out.printf(“%d + %d = %d%n”, x, y, x+y); }
  7. () => { System.out.println(“I am a Runnable”); }

A mi, eso de que puedas definir la closure sin indicar de que clase son las variables, en un lenguaje fuertemente tipado como es Java, no lo veo claro (ejemplos 2, 5 y 6)

Un compañero de JavaHispano nos muestra un ejemplo más completo de como van a funcionar estar closures:

// sin closures
List data = new Vector();
Collections.sort( data, 
                  new Comparator()
                      { public int compare(Object a, Object b)
                               { return ((String) a).length()- ((String) b).length();  }
                      }
                );

// con closures
List data = new Vector();
Collections.sort(data, (a, b) => { ((String) a).length()- ((String) b).length(); } );

Para poder empezar a usar las closures vamos a tener que esperar hasta octubre del 2012, si Oracle no hace ninguna pirula, ya que es para cuando tienen pensado sacar Java 8.

Dejo un par de enlaces sobre la sintaxis de la closures en JavaHispano y la fuente original correos de openJDK.

Categories: Tecnologías Tags: , , , ,
%d bloggers like this: