21 Kasım 2023 Salı

RxJava Observable.switchMap metodu - Yani flatMapLatest

Giriş
Açıklaması şöyle. Bu metodu eski ismi flatMapLatest, en son sonuç nesnesini dışarı verir
The switchMap operator must not be confused with concatMap, as it looks very similar at the first glance. However, it works by cancelling the previous inner subscriber whenever the outer publisher emits an item.

RxJava Observable.flatMapSequential metodu - Sonuç Sırasını Korur

Giriş
Açıklaması şöyle
This operator eagerly subscribes to its inner publishers like flatMap, but queues up elements from later inner publishers to match the actual natural ordering and thereby prevents interleaving like concatMap.
Örnek
Şöyle yaparız. Burada flatMapSequential ile çalıştırma paralel olsa bile çıktı sırası korunuyor
@Test void test_flatMap() { Flux.just(1, 2, 3) .flatMap(this::doSomethingAsync) //.flatMapSequential(this::doSomethingAsync) //.concatMap(this::doSomethingAsync) .doOnNext(n -> log.info("Done {}", n)) .blockLast(); } private Mono<Integer> doSomethingAsync(Integer number) { //add some delay for the second item... return number == 2 ? Mono.just(number).doOnNext(n -> log.info("Executing {}", n)) .delayElement(Duration.ofSeconds(1)) : Mono.just(number).doOnNext(n -> log.info("Executing {}", n)); }
Çıktı şöyle
// flatMap does not preserve original ordering, and has subscribed to all three elements // eagerly. Also, notice that element 3 has proceeded before element 2. 2022-04-22 19:38:49,164 INFO main - Executing 1 2022-04-22 19:38:49,168 INFO main - Done 1 2022-04-22 19:38:49,198 INFO main - Executing 2 2022-04-22 19:38:49,200 INFO main - Executing 3 2022-04-22 19:38:49,200 INFO main - Done 3 2022-04-22 19:38:50,200 INFO parallel-1 - Done 2 // flatMapSequential has subscribed to all three elements eagerly like flatMap // but preserves the order by queuing elements received out of order. 2022-04-22 19:53:40,229 INFO main - Executing 1 2022-04-22 19:53:40,232 INFO main - Done 1 2022-04-22 19:53:40,261 INFO main - Executing 2 2022-04-22 19:53:40,263 INFO main - Executing 3 2022-04-22 19:53:41,263 INFO parallel-1 - Done 2 2022-04-22 19:53:41,264 INFO parallel-1 - Done 3 //concatMap naturally preserves the same order as the source elements. 2022-04-22 19:59:31,817 INFO main - Executing 1 2022-04-22 19:59:31,820 INFO main - Done 1 2022-04-22 19:59:31,853 INFO main - Executing 2 2022-04-22 19:59:32,857 INFO parallel-1 - Done 2 2022-04-22 19:59:32,857 INFO parallel-1 - Executing 3 2022-04-22 19:59:32,857 INFO parallel-1 - Done 3










RxJava Observable.concatMap metodu

Giriş
Açıklaması şöyle. Yani concatMap dışarıdaki Observable ile gelen girdinin sırasını değiştirmez. Ayrıca bir nesnenin işi bitmeden bir sonrakine geçmez.
The concatMap operator is actually quite similar to the flatMap, except that the operator waits for its inner publishers to complete before subscribing to the next one.
Örnek
Şöyle yaparız. Burada sıra korunuyor
@Test
public void flatMapVsConcatMap() throws Exception {
  System.out.println("******** Using flatMap() *********");
  Observable.range(1, 15)
    .flatMap(item -> Observable.just(item).delay(1, TimeUnit.MILLISECONDS))
    .subscribe(x -> System.out.print(x + " "));

  Thread.sleep(100);

  System.out.println("\n******** Using concatMap() *********");
  Observable.range(1, 15)
    .concatMap(item -> Observable.just(item).delay(1, TimeUnit.MILLISECONDS))
    .subscribe(x -> System.out.print(x + " "));

  Thread.sleep(100);
}
Çıktı şöyle
******** Using flatMap() ********* 1 2 3 4 5 6 7 9 8 11 13 15 10 12 14 ******** Using concatMap() ********* 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Örnek
Şöyle yaparız. Burada sıra korunuyor
@Test void test_flatMap() { Flux.just(1, 2, 3) .flatMap(this::doSomethingAsync) //.flatMapSequential(this::doSomethingAsync) //.concatMap(this::doSomethingAsync) .doOnNext(n -> log.info("Done {}", n)) .blockLast(); } private Mono<Integer> doSomethingAsync(Integer number) { //add some delay for the second item... return number == 2 ? Mono.just(number).doOnNext(n -> log.info("Executing {}", n)) .delayElement(Duration.ofSeconds(1)) : Mono.just(number).doOnNext(n -> log.info("Executing {}", n)); }
Çıktı şöyle
// flatMap does not preserve original ordering, and has subscribed to all three elements // eagerly. Also, notice that element 3 has proceeded before element 2. 2022-04-22 19:38:49,164 INFO main - Executing 1 2022-04-22 19:38:49,168 INFO main - Done 1 2022-04-22 19:38:49,198 INFO main - Executing 2 2022-04-22 19:38:49,200 INFO main - Executing 3 2022-04-22 19:38:49,200 INFO main - Done 3 2022-04-22 19:38:50,200 INFO parallel-1 - Done 2 // flatMapSequential has subscribed to all three elements eagerly like flatMap // but preserves the order by queuing elements received out of order. 2022-04-22 19:53:40,229 INFO main - Executing 1 2022-04-22 19:53:40,232 INFO main - Done 1 2022-04-22 19:53:40,261 INFO main - Executing 2 2022-04-22 19:53:40,263 INFO main - Executing 3 2022-04-22 19:53:41,263 INFO parallel-1 - Done 2 2022-04-22 19:53:41,264 INFO parallel-1 - Done 3 //concatMap naturally preserves the same order as the source elements. 2022-04-22 19:59:31,817 INFO main - Executing 1 2022-04-22 19:59:31,820 INFO main - Done 1 2022-04-22 19:59:31,853 INFO main - Executing 2 2022-04-22 19:59:32,857 INFO parallel-1 - Done 2 2022-04-22 19:59:32,857 INFO parallel-1 - Executing 3 2022-04-22 19:59:32,857 INFO parallel-1 - Done 3









16 Kasım 2023 Perşembe

Log4j2 Ayar Dosyası

Giriş
Log4J2 için kullanılan ayar dosyası belirli bir önceliğe göre aranıyor. Öncelik sıralaması burada tanımlı. Açıklaması şöyle
1. Log4j will inspect the "log4j2.configurationFile" system property and, if set, will attempt to load the configuration using the ConfigurationFactory that matches the file extension. Note that this is not restricted to a location on the local file system and may contain a URL.
2. If no system property is set the properties ConfigurationFactory will look for log4j2-test.properties in the classpath.
3. If no such file is found the YAML ConfigurationFactory will look for log4j2-test.yaml or log4j2-test.yml in the classpath.
4. If no such file is found the JSON ConfigurationFactory will look for log4j2-test.json or log4j2-test.jsn in the classpath.
5. If no such file is found the XML ConfigurationFactory will look for log4j2-test.xml in the classpath.
6. If a test file cannot be located the properties ConfigurationFactory will look for log4j2.properties on the classpath.
7. If a properties file cannot be located the YAML ConfigurationFactory will look for log4j2.yaml or log4j2.yml on the classpath.
8 . If a YAML file cannot be located the JSON ConfigurationFactory will look for log4j2.json or log4j2.jsn on the classpath.
9. If a JSON file cannot be located the XML ConfigurationFactory will try to locate log4j2.xml on the classpath.
10. If no configuration file could be located the DefaultConfiguration will be used. This will cause logging output to go to the console.
1. DefaultConfiguration
Açıklaması şöyle
Log4j 2 provides a default configuration that will be used if no custom configuration file is provided. This default configuration is designed to work in most situations, but it may not meet all of your specific logging needs.
2. XML veya YAML Dosyası
Açıklaması şöyle
If you choose to configure Log4j 2 using a log4j2.xml or log4j2.yml file, you can place the file in your application's classpath (in our case main/java/resources), in the root of your application's jar file, or in a directory specified by the log4j.configurationFile system property.

3. Kendi Dosyam
log4j2.configurationFile ile belirtilir.

Örnek - properties dosyası
Şöyle yaparız
java ... -Dlog4j2.configurationFile=mylog.properties
Kodla şöyle yaparız
public static void main(String[] args) {
  System.setProperty("log4j2.configurationFile", "file:///.../mylog.properties");
  
}

JDBC XAConnection Arayüzü

Giriş
Şu satırı dahil ederiz 
import javax.sql.XAConnection;
PooledConnection arayüzünden kalıtır. Açıklaması şöyle.
XAConnection extends PooledConnection because it serves a similar purpose: they are both a handle to the physical connection that is held in a connection pool (PooledConnection) or in the transaction manager (XAConnection). Transaction managers usually have a connection pool themselves (which may be exposed as a javax.sql.DataSource to the application code).
Bu arayüzü gerçekleştiren bazı sınıflar şöyle
com.microsoft.sqlserver.jdbc.SQLServerXAConnection;
com.mysql.cj.jdbc.SuspendableXAConnection
org.h2.jdbcx.JdbcXAConnection;
org.postgresql.xa.PGXAConnection;

13 Kasım 2023 Pazartesi

JPA JPQL LEFT JOIN FETCH ve MultipleBagFetchException

Giriş
LEFT JOIN FETCH özellikle parent nesnede iki tane ilişki varsa problem olabiliyor. 

1. Set Döndürmek
Hibernate tarafından beklenen şey Set döndürmek. Set kullanırsak bile 
1. Full Cartesian Product problemi ortaya çıkar. 
2. Sayfalama çalışmıyor

Örnek-  Sayfalama Hatası
Elimizde şöyle bir kod olsun
@Entity
@Data
@NoArgsConstructor
@Table(name = "teams")
public class Team {

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

  private String name;

  @OneToMany(mappedBy = "team", 
    cascade = {CascadeType.PERSIST, CascadeType.MERGE}, orphanRemoval = true)
  private Set<Member> members;

  @OneToMany(mappedBy = "team", 
    cascade = {CascadeType.PERSIST, CascadeType.MERGE}, orphanRemoval = true)
  private Set<Milestone> milestones;

  // constructors, helper functions, etc ..
}

public interface TeamRepository extends JpaRepository<Team, Long> {

  @EntityGraph(attributePaths = {
            "members", "milestones"
  })
  Page<Team> findAll(Pageable pageable);
}
Açıklaması şöyle
When lookin at the response, it might lead you to believe that pagination is automatically handled when using the entity graph by passing in a Pageable to your query. However, it will give you a warning indicating that pagination is being performed in memory.
Çıktı şöyle
WARN 88609 --- [nio-8080-exec-3] org.hibernate.orm.query : HHH90003004: firstResult/maxResults specified with collection fetch; applying in memory
2. List Döndürmek
Bazen kodda ilişkiyi temsil etmek için Set yerine List kullanılıyor. Bu durumda 
org.hibernate.loader.MultipleBagFetchException diye bir exception alırız.
Açıklaması şöyle
The reason why a MultipleBagFetchException is thrown by Hibernate is that duplicates can occur, and the unordered List, which is called a bag in Hibernate terminology, is not supposed to remove duplicates.
Çözüm olarak 2 tane JPQL kullanılır. Açıklaması şöyle
This can be solved if you use Set instead of List. If you really want to stick with List, then you would have to divide into two queries eagerly loading each child and merge in the application code. Therefore, it may require you to change some code if you need to load multiple child entities and the decision will depend on the feature acceptance criteria.

Örnek
Elimizde Post sınıfı için şöyle bir kod olsun. Burada PostComment ve Tag ilişkileri için List kullanılıyor.
@OneToMany(
    mappedBy = "post",
    cascade = CascadeType.ALL,
    orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
 
@ManyToMany(
    cascade = {
        CascadeType.PERSIST,
        CascadeType.MERGE
    }
)
@JoinTable(
    name = "post_tag",
    joinColumns = @JoinColumn(name = "post_id"),
    inverseJoinColumns = @JoinColumn(name = "tag_id")
)
private List<Tag> tags = new ArrayList<>();
Post nesnelerini çekmek için şöyle bir kod çalıştıralım. MultipleBagFetchException alırız
List<Post> posts = entityManager.createQuery("""
    select p
    from Post p
    left join fetch p.comments
    left join fetch p.tags
    where p.id between :minId and :maxId
    """, Post.class)
.setParameter("minId", 1L)
.setParameter("maxId", 50L)
.getResultList();
Çözüm olarak 2 tane JPQL kullanılır. Şöyle yaparız
List<Post> posts = entityManager.createQuery("""
    select distinct p
    from Post p
    left join fetch p.comments
    where p.id between :minId and :maxId""", Post.class)
.setParameter("minId", 1L)
.setParameter("maxId", 50L)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();
 
posts = entityManager.createQuery("""
    select distinct p
    from Post p
    left join fetch p.tags t
    where p in :posts""", Post.class)
.setParameter("posts", posts)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();
Örnek
Elimizde şöyle bir kod olsun
public class Lesson {
  @Id
  private Long id;
    // ...other properties
  @OneToMany(mappedBy = "lesson", cascade = CascadeType.ALL)
  private List<Student> students;
  @OneToMany(mappedBy = "lesson", , cascade = CascadeType.ALL)
  private List<Guest> guests;
    // ...constructors, getters and setters
}
Yine 2 tane JPQL kullanılır. Şöyle yaparız
@Repository
public class LessonCriteriaRepositoryImpl implements LessonCriteriaRepository {

  @PersistenceContext
  private EntityManager entityManager;

  public List<Lesson> findAll() {
    //build first query for fetching students
    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    CriteriaQuery<Lesson> criteriaQuery = builder.createQuery(Lesson.class);
    Root<Lesson> lesson = criteriaQuery.from(Lesson.class);
    lesson.fetch("students", JoinType.LEFT);
    criteriaQuery.select(lesson).distinct(true);
    TypedQuery<Lesson> query1 = entityManager.createQuery(criteriaQuery);
    List<Lesson> lessons = query1.getResultList();

    //build second query for fetching guests
    builder = entityManager.getCriteriaBuilder();
    criteriaQuery = builder.createQuery(Lesson.class);
    lesson = criteriaQuery.from(Lesson.class);
    lesson.fetch("guests", JoinType.LEFT);
    criteriaQuery.select(lesson).distinct(true).where(lesson.in(lessons));
    TypedQuery<Lesson> query2 = entityManager.createQuery(criteriaQuery);
    return query2.getResultList();
  }
}



8 Kasım 2023 Çarşamba

AtomicReference İle Stack Gerçekleştirimi

Örnek
Şöyle yaparız. Burada bir Stack gerçekleştirimi var ve head ve next değerleri atanıyor
public class MyConcurrentStack<T> {

  private AtomicReference<Node> head = new AtomicReference<Node>();

  public void push(T t) {
    if (t == null) {
      return;
    }
    Node<T> n = new Node<T>(t);
    Node<T> current;

    do {
      current = head.get();
      n.setNext(current);
    } while (!head.compareAndSet(current, n));
  }

  public T pop() {
    Node<T> currentHead = null;
    Node<T> futureHead = null;
    do {
      currentHead = head.get();
      if (currentHead == null) {
        return null;
      }
      futureHead = currentHead.next;
    } while (!head.compareAndSet(currentHead, futureHead));

    return currentHead.data;
  }
  
}
Örnek
Elimizde şöyle bir kod olsun
public class StackNode<T> {
  T data;
  StackNode next;

  public StackNode(T t) {
    data = t;
    next = null;
  }
}
Şöyle yaparız
public class AtomicStack<T> {
  private final AtomicReference<StackNode<T>> head = new AtomicReference<>();
private final AtomicInteger count = new AtomicInteger(0);
public int getCount() { return count.get(); } public T pop() { StackNode<T> currentStackHead; StackNode<T> newStackHead; while(true) { currentStackHead = head.get(); if(currentStackHead == null){ break; } newStackHead = currentStackHead.next; if(head.compareAndSet(currentStackHead, newStackHead)) { break; } else { LockSupport.parkNanos(1); } } count.decrementAndGet(); return currentStackHead != null ? currentStackHead.data: null; } public void push(T obj) { StackNode<T> newStackNode = new StackNode<>(obj); while (true) { StackNode<T> currentHead = head.get(); newStackNode.next = currentHead; if (head.compareAndSet(currentHead, newStackNode)) { break; } else { LockSupport.parkNanos(1); } } count.incrementAndGet(); } }


1 Kasım 2023 Çarşamba

JPA @OneToOne Unidirectional İlişki

Giriş
@JoinColumn anotasyonuna sahip nesne child nesne olarak düşünülebilir

Örnek
Şöyle yaparız. Address tablosundaki id sütünü primary key, Employee tablosundaki addrs_id sütünü da Address tablosuna foreign key olarak tanımlanır.
@Entity
@Data
public class Address {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;
  ...
}

@Entity
@Data
public class Employee {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;
  ...
  @OneToOne(cascade = CascadeType.ALL)
  @JoinColumn(name = "addrs_id", referencedColumnName = "id")
  private Address address;    
}
Örnek
Şöyle tanımlarız.
@Entity
@Table(name = Membership_Profile)
class MembershipProfile {
   Long moduleMembershipId;
   String displayName;
   // Other properties if needed
}
ve
@Entity
class PokerProfile {
   @OneToOne(fetch=FetchType.LAZY)
   @JoinColumn(name = "membershipId")
   MembershipProfile membershipProfile;
   ...
}
PokerProfile nesnesi JoinColumn ile hangi membership profile nesnesini ile ilişkili olduğunu bilir.

Örnek
ForeignKeyvermek için şöyle yaparız.
@Entity
public class MediaAnalysis {

  @OneToOne
  @JoinColumn(
    name = "MED_ID",
    nullable = false,
    foreignKey = @ForeignKey(name="MEA_MED_FK")
  )
  private Media media;
  ...

}