Tuesday, November 24, 2015

Get rid of null checks

As a developer we do our best to catch the null values and fail fast. I was writing too much code lines like below.

if (user == null) {
    throw new IllegalArgumentException("user most not be null.");
} 


After a while codebase can become dirty with this lines copy/pasted everywhere. While I was reading some Spring source code I found that Spring uses `Assert` Util class for that purpose. And we started to use this class as a replacement. Now code become better with this one line.
Assert.notNull(user, "user must not be empty");

Assert class has many useful methods like
Assert.isTrue
Assert.isNull
Assert.notNull
Assert.hasLength
Assert.hasText
Assert.doesNotContain
Assert.notEmpty
...

If throwing IllegalArgumentException is not good for you then you can create your own Util class for that purpose.

Exposing Spring Data Repositories over REST with Spring Boot and Spring Data REST

Spring Boot let us quickly create projects. Spring Boot favors convention over configuration. Most of the time we start with writing domain Entities. To quickly look around Entities or to populate some data we can use Spring Data REST. With Spring Data REST we can easily expose JPA based repositories as RESTful endpoints.

Most of the time you'll need to write your own endpoints for adding validations etc. But for testing the entities or when we need to try something it is good tool.

First we need to create Spring Boot project and add spring-data-rest as maven dependency to pom.xml file

  org.springframework.boot
  spring-boot-starter-parent
  1.3.0.RELEASE
 
 
  
   org.springframework.boot
   spring-boot-starter-data-rest
  
  
   mysql
   mysql-connector-java
   runtime
  
 

Let's create basic Entity
import javax.persistence.*;

@Entity
public class Customer {

    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Id
    private Long id;

    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

Spring Data's JpaRepository interface adds CRUD operations without adding any code. Spring Data REST will use this repository to generate REST endpoints.

@Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
}

Then all we need to do is add @EnableJpaRepositories annotation
@EnableJpaRepositories
@SpringBootApplication
public class SpringDataRestDemo {

    public static void main(String[] args) {
        SpringApplication.run(SpringDataRestDemo.class, args);
    }

}

When you start the application at the root of your url you'll see HATEOS supported endpoints response like below. You can make GET/POST/DELETE/PATCH/PUT requests to this endpoints.
{
  "_links": {
    "customers": {
      "href": "http://localhost:8080/customers{?page,size,sort}",
      "templated": true
    },
    "profile": {
      "href": "http://localhost:8080/profile"
    }
  }
}

Let's check the customers
{
  "_embedded": {
    "customers": [
      {
        "name": "ugur",
        "_links": {
          "self": {
            "href": "http://localhost:8080/customers/1"
          },
          "customer": {
            "href": "http://localhost:8080/customers/1"
          }
        }
      },
      {
        "name": "Luke",
        "_links": {
          "self": {
            "href": "http://localhost:8080/customers/2"
          },
          "customer": {
            "href": "http://localhost:8080/customers/2"
          }
        }
      }
    ]
  },
  "_links": {
    "self": {
      "href": "http://localhost:8080/customers"
    },
    "profile": {
      "href": "http://localhost:8080/profile/customers"
    }
  },
  "page": {
    "size": 20,
    "totalElements": 2,
    "totalPages": 1,
    "number": 0
  }
}

{
  "name": "Luke",
  "_links": {
    "self": {
      "href": "http://localhost:8080/customers/2"
    },
    "customer": {
      "href": "http://localhost:8080/customers/2"
    }
  }
}

Sunday, November 22, 2015

Using Java 8 Time API with JPA

We were using the Joda-Time API for replacement of Java Date API. After improvements on the Java 8 Time API we decided to move on to the Java 8 Time API. But JPA 2.1 does not have built in support for Java 8 time API because Java 8 released after. But we can still use Java 8 API using the attribute converters.

Here the example code for converting LocalDate to java.sql.Date for persisting to db.



With autoApply = true property this converter will be apply to all LocalDates so entities can be clean. If you don't want this feature then you can add @Convert(converter = LocalDateConverter.class) annotation to the fields.

Thursday, January 15, 2015

Spring Data MongoDB ile Spring MongoDB entegrasyonu

Spring Data projesi ile MongoDB'ye erişmek kolay bir hal alıyor.

Kodlara GitHub üzerinden erişebilirsiniz.

spring-boot-starter-data-mongodb'yi dependency olarak ekliyoruz.
pom.xml dosyası:

    org.springframework.boot
    spring-boot-starter-parent
    1.1.10.RELEASE


    
        org.springframework.boot
        spring-boot-starter-data-mongodb
    



    
    UTF-8
    UTF-8
    org.guneriu.springboot.mongo.Application



    
        
            org.springframework.boot
            spring-boot-maven-plugin
        
    


MongoDB üzerinde CRUD işlemlerini yapabilmek için Spring-Data-MongoDB bize MongoRepository sınıfını sunuyor. Bu sınıf ile birlikte
"count, delete, delete, delete, deleteAll, exists, findAll, findOne, save" operasyonları hazır halde geliyor. Bununla birlikte bu metodlara geçirebileceğimiz "Pageable" ve "Sort" parametreleri ile paging ve sorting işlemlerini de yapabiliyoruz.

Customer sınıfı:
import org.springframework.data.annotation.Id;

public class Customer {

    @Id
    private String id;
    private String firstName;
    private String lastName;

    public Customer(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    @Override
    public String toString() {
        return String.format("Customer[id=%s, firstName='%s', lastName='%s']",
                id, firstName, lastName);
    }
}

CustomerRepository sınıfı.
import java.util.List;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;

public interface CustomerRepository extends MongoRepository<Customer string=""> {

    public List<Customer> findByFirstName(String firstName);
    public List<Customer> findByLastName(String lastName);

    @Query(value = "{}", count = true)
    public int customerCount();

    @Query(value = "{$or:[{'firstName': '?0'}, {'lastName': '?0'}]}", count = true)
    public int customersCountByFirstNameOrLastName(String name);
}

Buradaki findByFirstName ve findByLastName için bir kod bloğu yazmadım. Spring property expression ile sınıf üzerindeki alanlar ile metod ismini eşleştirip bizim için sorguları oluşturuyor. Aşağıdaki örnek kullanımlar Spring-Data-Mongo referans dökümanından. Bazen kompleks sorgular yazmanız gerektiğinde native sorgular yazıp çalıştırabilirsiniz. Bunun için yukarıdaki örnekte görüldüğü gibi @Query annotasyonu içerisine sorguyu yazıyoruz '?0' parametre yerine geçen placeholder.

// Enabling ignoring case for an individual property
    List<Person> findByLastnameIgnoreCase(String lastname);
    // Enabling ignoring case for all suitable properties
    List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);

    // Enabling static ORDER BY for a query
    List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
    List<Person> findByLastnameOrderByFirstnameDesc(String lastname);

MongoDB üzerinde işlem yapabilmek için Spring'in sunduğu diğer sınıf ise MongoTemplate. Bu örnek için MongoRepository yeterli geliyor ancak kullanımını göstermek için MongoTemplate yapılandırması aşağıdaki gibi.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;

import com.mongodb.Mongo;
import com.mongodb.MongoClient;

@Configuration
public class MongoConfig {

    @Bean
    public Mongo mongo() throws Exception {
        return new MongoClient();
    }

    @Bean
    public MongoOperations mongoTemplate() throws Exception {
        return new MongoTemplate(mongo(), "test");
    }

}

Uygulamayı çalıştıracağımız main metodumuzu yazdığımız Application sınıfı aşağıdaki gibi.

import java.util.Arrays;
import java.util.List;

import org.guneriu.springboot.mongo.config.MongoConfig;
import org.guneriu.springboot.mongo.customer.Customer;
import org.guneriu.springboot.mongo.customer.CustomerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Import;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;

@EnableAutoConfiguration
@Import(MongoConfig.class)
public class Application implements CommandLineRunner {

    @Autowired
    private CustomerRepository customerRepository;

    @Autowired
    private MongoOperations mongoOperations;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(String... args) throws Exception {

     Customer customer1 = new Customer("James", "Gosling");
     Customer customer2 = new Customer("James", "Joyce");
     Customer customer3 = new Customer("Joshua", "Bloch");

     customerRepository.save(Arrays.asList(customer1, customer2, customer3));

     List<Customer> customers = customerRepository.findAll();

     System.out.println("listing all customers");

     for (Customer customer : customers) {
        System.out.println(customer);
     }

     List<Customer> customersByFirstName = customerRepository.findByFirstName("James");

     System.out.println("customers with firstName James");

     for (Customer customer : customersByFirstName) {
        System.out.println(customer);
     }

     List<Customer> customersByFirstNameAndLastName =
            mongoOperations.find(
                    new Query(new Criteria().andOperator(
                            Criteria.where("firstName").is("Joshua"),
                            Criteria.where("lastName").is("Bloch"))),
                            Customer.class);

     System.out.println("customers with firstName Joshua and lastName Bloch");

     for (Customer customer : customersByFirstNameAndLastName) {
         System.out.println(customer);
     }

     System.out.println("all costumer counts: " + customerRepository.customerCount());
     System.out.println("customer count firstName or lastName is James :"
             + customerRepository.customersCountByFirstNameOrLastName("James"));
    }
}

Uygulamayı terminal'den spring-boot maven eklentisini kullanarak yada "Run As Java Application" diyerek çalıştırabiliriz.
Uygulamayı çalıştırdığımızda aldığımız çıktı aşağıdaki gibi.

ugur@ugur-PC:~/dev/workspace/spring-boot-mongo$ mvn spring-boot:run
listing all customers
Customer[id=54b7062544aefa551023948f, firstName='James', lastName='Gosling']
Customer[id=54b7062544aefa5510239490, firstName='James', lastName='Joyce']
Customer[id=54b7062544aefa5510239491, firstName='Joshua', lastName='Bloch']
customers with firstName James
Customer[id=54b7062544aefa551023948f, firstName='James', lastName='Gosling']
Customer[id=54b7062544aefa5510239490, firstName='James', lastName='Joyce']
customers with firstName Joshua and lastName Bloch
Customer[id=54b7062544aefa5510239491, firstName='Joshua', lastName='Bloch']
all costumer counts: 3
customer count firstName or lastName is James :2

Sunday, January 4, 2015

Constructor içerisinde polymorphic method davranışı

Uzun zamandır okumayı planladığım Thinking in Java (4th Edition) kitabına sonunda başlayabildim. Öncelikle kitap hakkında birkaç söz etmeden geçmek istemiyorum.

Keşke Java'ya bu kitap ile giriş yapmış olsaydım. Bruce Eckel Java anlatmak yerine önce Object Oriented Programming (OOP) mantığını anlatarak işe başlıyor ve kitap boyunca da bu tavrından vazgeçmeyip her zaman OOP odağında anlatmaya devam ediyor.
Ancak Software Principles çalışırken duyabileceğim "favour composition over inheritance" öğütünü inheritance'dan bahsederken anlatması çok etkileyici.
Okumanızı tavsiye ederim.

Polymorphism bölümünde constructor içerisinde override edilmiş metod çağrımına dair anlattıklarını toparlamaya çalışacağım.

Java'da static ve final (private metodlar da dolaylı olarak final'dır) metod çağrımları hariç diğer tüm çağrımlar dynamic binding (late binding, runtime binding) ile çözümlenirler. Yani compile-time'da compiler inheritance hiyararşisinde hangi alt sınıfın metodunun çağrılacağını bilmez buna runtime'da karar verir.

Peki base class constructor içerisinde polymorphic bir metodu çağırırsak hangi metod çağrılır?
Cevap alt sınıf metodu.

Bir örnek ile görelim. Sonra bu durumun yaratabileceği problemlere bakalım.

class Shape {
 
 Shape() {
  System.out.println("shape before draw method");
  draw();
  System.out.println("shape after draw method");
 }

 void draw() {
  System.out.println("shape -> draw");
 }
}

public class Circle extends Shape {
 private int radius = 0;
 
 Circle(int radius) {
  this.radius = radius;
  System.out.println("circle before draw method");
  draw();
  System.out.println("circle after draw method");
 }

 @Override
 void draw() {
  System.out.println("circle -> draw. radius: " + radius);
 }
 
 public static void main(String[] args) {
  Shape shape = new Circle(5);
 }
}

Kod çıktısı aşağıdaki gibi.

shape before draw method
circle -> draw. radius: 0
shape after draw method
circle before draw method
circle -> draw. radius: 5
circle after draw method

Shape sınıfı içerisinde draw metodu çağrımında override edilmiş alt sınıf metodu çağrıldı (2. satır). Ancak henüz Circle objesi stable durumda değil. Yine highlight edilmiş satırda görüleceği üzere Circle sınıfına radius değeri olarak 5 göndermemize rağmen henüz Shape constructor çağrımından dönmediği için Circle constructor tamamlanmadı dolayısı ile sınıfımız şu anda hatalı bir statüde duruyor.

Sonuç olarak kazara yapılacak bir hata sonucu böylesi bir bug'ı bulmak biraz zor olabilir.

Aynı örnekte radius primitive olduğu için default 0 değerini aldı. Ancak Integer tipinde bir nesne olarak tanımlayıp örneğin toString() metodunu çağırsaydık Shape sınıfı'nın constructor içerisinden gelen draw çağrımı sonucu NullPointerException alıyor olacaktık.

Bu durumdan kaçınmak için constructor'larda minimum işlem ile objeyi geçerli bir statüye getirip yapabiliyorsak sınıftaki diğer metod çağrımlarından kaçınmalıyız. Constructor'larda güvenle çağırabileceğimiz metodlar üst sınıfta final olarak tanımlananlardır. Çünkü bunların override edilme ihtimalleri olmadığından burada bahsettiğim sorun ortaya çıkmayacaktır.