30 Temmuz 2021 Cuma

Kotlin Null Kontrolü

Giriş
1. Nullable Type
Açıklaması şöyle. C# ile aynı aslında
?: literally means “if the left part is null, call or return right part”.
Örnek
Şöyle yaparız
fun func(obj : MyClass?) {
 obj?.c?.printClass() : print("oops");
}
2. requireNotNull ve checkNotNull kullanılır.
Açıklaması şöyle
requireNotNull can throw an IllegalArgumentException while checkNotNull can throw an IllegalStateException
Farkları şöyle
It is a semantic difference and hence it throws different exceptions. RequireNotNull is used to check input values, typically at the beginning of a method, while checkNotNull is used anywhere to check the current state.
requireNotNull şöyle
[...]
if (value == null) {
    val message = lazyMessage()
    throw IllegalArgumentException(message.toString())
} else {
    return value
}
checkNotNull şöyle
[...]
if (value == null) {
    val message = lazyMessage()
    throw IllegalStateException(message.toString())
} else {
    return value
}

29 Temmuz 2021 Perşembe

IntelliJ Idea - Analyze Menüsü

Giriş
Bu menü Project penceresinde bir dizine sağ tıklayınca çıkıyor veya tepedeki Run/Analyze menüsü ile de erişilebilir.

Unused declaration
Dead code bulmak için kullanılabilir. "Analyze/Run Inspection by Name" seçilir. Ctrl + Alt+ Shift + I kısa yoldur. İsim olarak "Unused declaration" yazılır.

Stack Trace
Stack trace çıktısını formatlı olarak görmek için şöyle yaparız
Analyze -> Stack Trace -> Paste the copied logs -> Hit OK

IntelliJ Idea - Navigation Menüsü

Arayüzden sınıfa gitmek için Ctrl + Alt + Sol Fare düğmesine basılır. Açılan penceredeki liste arayüzden kalıtan sınıflardır.

Back - Go to last/previous cursor position
Bir önceki imleç pozisyonuna gitmek için Ctrl + ALT + Sol Ok kullanılır

Class Structure
Sınıfın yapısını görmek için Ctrl + F12 ile popup açılır.

Jump Directly To The Test File Or Create a New Test
Sınıf için yazılan Test'e Ctrl+Shift+T ile gidilir

Metodun nereden çağrıldığının Görmek
3 tane yöntem var
1. Go to Declaration
Metodun nereden çağrıldığının popup olarak görmek için Ctrl + B kullanılır
2.  Call Hierarchy
Metodun nereden çağrıldığının hiyerarşisini  ayrı bir pencerede Ctrl + Alt + H ile görmek mümkün.
3. Find Usages
Metodun nerede kullanıldığını ayrı bir pencerede Alt+ F7 ile görmek mümkün.

Go to Implementation
Metodumuzu override eden yerlere gitmek için CTRL + ALT + B kullanılır

Matching Brace
Ctrl + Shift + M ile görmek mümkün.

Type Hierarchy
Sınıfın kalıtım hiyerarşisini Ctrl + H ile görmek mümkün.


28 Temmuz 2021 Çarşamba

Kotlin let Anahtar Kelimesi

Açıklaması şöyle
let is treated as a native way to do null checks on the variables.

Please don’t do that even if using `let` feels functionally beautiful but

- You will end up creating another reference called it in the same scope which is less descriptive
- To fix that you will definitely try to provide an explicit parameter for lambda example imageFile?.let{ image →,
- but it creates a load of finding another name for the same variable (naming things are hard)
- Now other devs can refer to image files in the scope with two names imageFile and image, with time you need to maintain that the right variable is being used inside the scope of lambda when there is iteration over the code for bugs and changes.
Örnek - null check
Birinci maddeyi gösteren bir kod şöyle
fun deleteImage(){
   var imageFile : File ? = ...
   imageFile?.let { // ❌ don't write code like this
      if(it.exists()) it.delete()
   }
}
Bu yöntem illaki kullanılacaksa "it" yerine düzgün bir isim verilebilir. Şöyle yaparız
class Foo {
  var imageFile : File ? = ...
  fun deleteImage(){
      // 🧐 you can get away for now
      imageFile?.let { image ->
        if(image.exists()) image.delete()
      }
  }
}

26 Temmuz 2021 Pazartesi

Kotlin data Class

Giriş
Üye alanlar için getter(), setter(), equals(), hashCode() ve copy() metodları üretir. Açıklaması şöyle
1. data class cannot be abstract, inner, open or sealed.
2. At least 1 parameter is required in the primary constructor.
3. data class does not implement Serializable by default.
4. data class can implement interfaces and extend other classes (since v1.1).
Örnek
Şöyle yaparız
enum class RequestType {CREATE, DELETE}
data class RuleChange(val organizationId: String, val userIds: List<String>,
val request: RequestType)
Örnek
Açıklaması şöyle
If we want to exclude any property from being included in the automatically generated functions, we can put the property in the class body :

Only genre and artist properties will be used in the implementation of hashcode(), equal(), copy(), toString() method.
Şöyle yaparız
data class Song(val genre: String, val artist: String){
  var title: String;
}
val songA: Song = Song("Pop", "Jamie")
val songB: Song = Song("Pop", "Jamie")
songA.title = "Take the train"
songB.title = "All blues"
songA.equals(songB) // true
copy
Şöyle yaparız
val song: Song = Song("Jazz", "John")
val anotherSong: Song = song.copy()

print(song == anotherSong) // true
print(song === anotherSong) // false

val nextSong: Song = song.copy(artist = "Judy")

GoF - Builder Örüntüsü

Giriş
Temelde iki çeşit Builder örüntüsü var
1. Blind Builder : Ayrı bir sınıftır
2. Bloch Builder : Kullanılmak istenen sınıfın içine public static bir builder yazılır. 
3. Functional Builder : Sınıfa bir şeyler eklemek için array of functions kullanılır

Blind Builder ve Bloch Builder'ın Ortak Özellikleri
Her iki Builder sınıfı da seçime bağlı özellikler için default değerler sağlamalıdır. Zorunlu parametreler zaten kullanıcı tarafından verilecektir.
class Builder {
  private String builderColor = "white"; // default color
  private String builderWheels = "round"; // default wheels
  private int builderSpeed = 100; // default speed
     
}
Build Edilen Sınıf Zorunlu Parametreleri final Yapmalıdır
Bu alanlar artık değişmeyecektir.
public class Car {

    public static Builder builder() {
        return new Builder();
    }

    // static inner class builder
    public static class Builder {
        ...
    }

    // final (immutable) fields.
    private final String color;
    private final String wheels;
    private final int speed;

    // private constructor, only the builder calls this....
    private Car(String color, String wheels, int speed) {
        this.color = color;
        this.wheels = wheels;
        this.speed = speed;
    }
  
}

1. Blind Builder
Builder yarattığı nesneden ayrıdır. Yaratılan nesne ise içine Builder alan private bir constructor veya setter metodları tanımlar. Örnek
class CarBuilder {
  private String wheels;
  private String color;
  private int speed;

  ...
}
Bu Builder tüm zorunlu ve isteğe bağlı parametreleri içinde biriktirir. Daha sonra build metodu ile yeni bir nesne yaratır. Dolayısıyla build metodu aşağıdakine benzer.
public Car build() {
  // validate your attributes
  Car car = new Car();
  // set all attributes in your car
  return car;
}
Eğer gerekirse build metodu içinde verinin doğruluğu da kontrol edilebilir.
public Car build(){
    // validate your values e.g.
    if (speed < 25) {
      throw new IllegalValueException("Car is too slow.");
    }
    // further validation
    Car car = new Car();
    car.setWheels(wheels);
    car.setColor(color);
    car.setSpeed(speed);
    return car;
}

2. Bloch Builder
Bloch Builder, Effective Java kitabında gösteriliyor. Kullanılmak istenen sınıfın içine public static bir builder yerleştiriliyor. Yaratılan nesne ise içine Builder alan private bir constructor tanımlar.

Yukarıdaki örnekte Builder static ve nested bir sınıf. Kullanırken Pizza sınıfını yarattığı kolayca okunabilir.
Pizza pizza = new Pizza.Builder(12)
                       .cheese(true)
                       .pepperoni(true)
                       .bacon(true)
                       .build();
Örnek - Generics Kullanan Block Builder
Açıklaması şöyle. Bir başka açıklama burada
Type parameters are not inherited from outer class to static nested class. 
Bu yüzden şöyle yapmak gerekir
LanguageMatcher.Builder<MyClass, YourClass> lm =
    new LanguageMatcher.Builder<MyClass, YourClass>();
Örnek
Ben şöyle yaptım
public class RemoteMapSourceParams<T, K, V> {
  private final String mapName;
  private final Predicate<K, V> predicate;
  private final Projection<? super Map.Entry<K, V>, ? extends T> projection;

  private RemoteMapSourceParams(Builder<T, K, V> builder) {
    this.mapName = builder.mapName;
    this.predicate = builder.predicate;
    this.projection = builder.projection;
  }

  // Getters
  public static <T, K, V> Builder<T, K, V> builder(String mapName) {
    return new Builder<>(mapName);
  }

  public static class Builder<T, K, V> {
    private final String mapName;
    private String dataConnectionName;
    private ClientConfig clientConfig;
    private Predicate<K, V> predicate;
    private Projection<? super Map.Entry<K, V>, ? extends T> projection;

    public Builder(String mapName) {
      Objects.requireNonNull(mapName, "mapName can not be null");
      this.mapName = mapName;
    }

    //Setters
    public RemoteMapSourceParams<T, K, V> build() {
      return new RemoteMapSourceParams<>(this);
    }
  }
}
3. Functional Builder
Örnek
Elimizde şöyle bir arayüz olsun
@FunctionalInterface
interface Coffee {
  List<String> ingredients();  
        
  static Coffee withSaltedCaramelFudge(Coffee coffee) {
    return () -> coffee.add("Salted Caramel Fudge");
  }

  default List<String> add(String item) {
    return new ArrayList<>(ingredients()) {{
      add(item);
    }};
  }
    
  static Coffee withSweetenedMilk(Coffee coffee) {
    return () -> coffee.add("Sweetened Milk");
  }

  static Coffee withDarkCookieCrumb(Coffee coffee) {
    return () -> coffee.add("Dark Cookie Crumb");
  }

  static Coffee withVanillaAlmondExtract(Coffee coffee) {
    return () -> coffee.add("Vanilla/Almond Extract");
  }
}
Builder olarak şu kodu kullanırız.
@SafeVarargs
static Coffee getCoffeeWithExtra(Coffee coffee, Function<Coffee, Coffee>... ingredients) {
  var reduced = Stream.of(ingredients)
    .reduce(Function.identity(), Function::andThen);
  return reduced.apply(coffee);
}
Bu kodu kullanmak için şöyle yaparız
record CoffeeCup(List<String> initialIngredient) implements Coffee {
  @Override
  public List<String> ingredients() {
    return initialIngredient;
  }
}

var ingredients = List.of("Tim Horton");
var coffeeCup = new CoffeeCup(ingredients);

var coffee = getCoffeeWithExtra(coffeeCup,
  Coffee::withDarkCookieCrumb,
  Coffee::withSaltedCaramelFudge,
  Coffee::withSweetenedMilk,
  Coffee::withVanillaAlmondExtract);

  System.out.println("Coffee with " + String.join(", ", coffee.ingredients()));
}


24 Temmuz 2021 Cumartesi

QueryDSL Kütüphanesi

Giriş
"Type Safe" SQL cümlesi yazmak için kullanılır. Açıklaması şöyle
QueryDSL is an external library that uses annotation processing to generate additional classes – called Q—types. These are copies of the JPA entities but introduce an API that allows us to create database queries.
Maven
Şu satırı dahil ederiz
<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-jpa</artifactId>
</dependency>
<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-apt</artifactId>
</dependency>
QueryDSL kod ürettiği için şu satırı dahil ederiz. Böylece her bir @Entity sınıfı için QFoo şeklinde kod üretilir.
<plugin>
<groupId>com.mysema.maven</groupId> <artifactId>apt-maven-plugin</artifactId> <version>1.1.3</version> <executions> <execution> <goals> <goal>process</goal> </goals> <configuration> <outputDirectory>target/generated-sources/java</outputDirectory> <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor> </configuration> </execution> </executions> </plugin>
JPAQuery Sınıfı
select metodu
Örnek
Şöyle yaparız
entityManager.getTransaction().begin();   

List<User> users = new JPAQuery<User>(entityManager)  
        .select(QUser.user)  
        .from(QUser.user)  
        .where(QUser.user.email.endsWith("gmail.com")  
                .and(QUser.user.firstName.eq("Alice")))  
        .orderBy(QUser.user.lastName.asc())  
        .fetch();  

entityManager.getTransaction().commit();  
entityManager.close();  
JPAQueryFactory Sınıfı
constructor
Şöyle yaparız
@Configuration
class QueryDslConfiguration {

  @PersistenceContext
  private EntityManager em;

  @Bean
  public JPAQueryFactory jpaQueryFactory() {
    return new JPAQueryFactory(em);
  }
}
fetch metodu
Şöyle yaparız
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;


@Repository
@RequiredArgsConstructor
public class DefaultTeamQueryRepository {

  private final JPAQueryFactory queryFactory;

  public List<MemberResponse> findMembersByTeamId(long teamId) {
    QMember memberTable = new QMember("member");

    return queryFactory
      .select(new QMemberResponse(memberTable.id,memberTable.name))
      .from(memberTable)
      .where(memberTable.team.id.eq(teamId))
      .fetch();
  }
}
fetchFirst metdou
Şöyle yaparız
public abstract class BaseRepositoryImpl<T, ID> extends SimpleJpaRepository<T, ID>
implements BaseRepository<T, ID> {
  protected final QAuthor author = QAuthor.author;
  protected final QBook book = QBook.book;

  private final EntityManager em;
  protected final JPAQueryFactory queryFactory;

  public BaseRepositoryImpl(Class<T> domainClass, EntityManager em) {
    super(domainClass, em);
    this.em = em;
    this.queryFactory = new JPAQueryFactory(em);
  }
  ...
}

@Repository
public class AuthorRepositoryImpl extends BaseRepositoryImpl<Author, Long>
implements AuthorRepository {
  public AuthorRepositoryImpl(EntityManager em) {
    super(Author.class, em);
  }

  @Override
  public Optional<Author> findByEmailIgnoreCase(String email) {
    return Optional.ofNullable(
      queryFactory
        .select(author)
        .from(author)
        .where(author.email.equalsIgnoreCase(email))
        .fetchFirst()
    );
  }
}
fetchJoin metodu
N+1 Select Problem çözümü için kullanılır. JPQL karşılığı olarak "JOIN FETCH" kullanılır.
Örnek
Şöyle yaparız
import com.querydsl.jpa.impl.JPAQueryFactory;

public Shipment getById(final UUID id) {
  final QShipment shipment = QShipment.shipment; // generated
  final QItem item = QItem.item;                 // generated
  return (Shipment) new JPAQueryFactory(entityManager)
    .from(shipment) 
    .innerJoin(shipment.items, item)
.fetchJoin() //Items is lazy, the fetchJoin will load it
    .where(shipment.id.eq(id.toString())) 
    .fetchOne();
}
Örnek
Şöyle yaparız
@Override
public List<Author> findAllWithBooks() { return queryFactory .select(author) .distinct() .from(author) .innerJoin(author.books, book) .fetchJoin() .fetch(); }
offset metodu
Şöyle yaparız
public Page<MemberResponse> members(Pageable pageable) {
  QMember memberTable = new QMember("member");

  List<MemberResponse> members = queryFactory
    .select(new QMemberResponse(memberTable.id,memberTable.name))
    .from(memberTable)
    .offset(pageable.getOffset())
    .limit(pageable.getPageSize())
    .fetch();

    return PageableExecutionUtils.getPage(members, pageable,
      () -> countQuery().fetchOne());
  }

  private JPAQuery<Long> countQuery() {
    QMember memberTable = new QMember("member");
    return queryFactory
      .select(memberTable.count())
      .from(memberTable);
  }
}
Açıklaması şöyle
We can use the same Pageable from Spring Data. Note that PageableExecutionUtils.getPage() is used instead of new PageImpl(...). This is an optimization technique to reduce the number of count calls.

select metodu
Örnek
Elimizde şöyle bir kod olsun
@Data
@AllArgsConstructor
public class AuthorStatistic {
  private String authorName;
  private Long bookSize;
}
Şöyle yaparız
@Override
public List<AuthorStatistic> findAuthorStatistic() {
  return queryFactory
    .from(author)
    .innerJoin(author.books, book)
    .groupBy(author.fullName)
    .select(Projections.constructor(AuthorStatistic.class,
                        author.fullName,
                        book.count())
    )
    .fetch();
}
Örnek
Elimizde şöyle bir kod olsun
import com.querydsl.core.annotations.QueryProjection;

public record MemberResponse(long id, String name) {

    @QueryProjection
    public MemberResponse {
    }
}
Şöyle yaparız
public List<MemberResponse> findMembersByTeamId(long teamId) {
  QMember memberTable = new QMember("member");

  return queryFactory
    .select(new QMemberResponse(memberTable.id,memberTable.name)
    .from(memberTable)
    .where(memberTable.team.id.eq(teamId))
    .fetch();
}