29 Temmuz 2020 Çarşamba

JPA @OneToMany Ilişki - Unidirectional

Giriş
Unidirectional OneToMany ilişki iyi bir şey değil ve mümkünse kaçınılmalı. Unidirectional ilişki, join tablosu ile veya join tablosu olmadan kurulabilir. Açıklaması şöyle.
By default, a unidirectional @OneToMany relationship will use a join table....

1. Join Tablosu Olmadan - Unidirectional İse Bu Tercih Edilir
 Eğer join tablosu olmasın istiyorsak parent tarafında OneToMany +  JoinColumn birlikte kullanılır.
@JoinColumn ile child nesnenin tablosundaki bir alan belirtilir.

Performansı OneToMany - Bidirectional kadar iyi olmasa bile ikinci seçeneğe göre daha iyi.

Örnek
Şöyle yaparız. article_id Vote tablosundadır
class Article extends BaseActivity{
  @OneToMany
  @JoinColumn(name = "article_id")
  private List<Vote> votes = new ArrayList<>();
}
Örnek
Açıklaması şöyle.
Although it seems less likely to be used, we can also have the one-to-many association on one side. In this case, the mappedBy element used above would not work because Book no longer has an association to Author. Instead, we use a @JoinColumn to tell Hibernate which column to use in order to fetch Book instances when given an author id:
Elimizde şöyle bir kod olsun.
@Entity
public class Author {
  @OneToMany
  @JoinColumn(name = "AUTHOR_ID")
  protected List<Book> books = new ArrayList<>();
  ...
}
Author tablosu şöyledir. Buraya kadar her şey normal. Klasik bir primary key kulllanan tablo oluşturuldu
create table Author (
    id bigint not null,
    birthDay date,
    country varchar(30) not null,
    name varchar(255) not null,
    primary key (id)
)
Book tablosu şöyledir. Author tablosuna foreign key olarak bağlandı.
create table Book (
    id bigint not null,
    publishingDate date,
    title varchar(255) not null,
    volumes integer not null,
    PUBLISHER_ID bigint not null,
    AUTHOR_ID bigint,
    primary key (id)
)

alter table Book
    add constraint FKe3rppuv3qa0j4ewpn52qfljn6
    foreign key (AUTHOR_ID)
    references Author
2. Join Tablosu İle - Tercih Edilmez
Eğer join tablosu olsun istiyorsak sadece OneToMany kodunu yazmak yeterli. @JoinTable anotasyonunu kullanmaya gerek yok. Bu durumda join tablosu otomatik olarak oluşturulur. Yani A tablosu, B tablosu ve A_B join tablosu vardır. Ancak bu kullanım şeklinde fazladan bir sürü sql çalıştığı için performans kaybı oluyor. Sebebi ise
- A için bir insert statement,
- B için bir insert statement ve
- A_B için bir insert statemen çalıştırılması
Eğer mevcut bir join tablosunu kullanmak istersek @JoinTable ile birlikte kullanırız.

EntityManager.persist() ve EntityManager.merge() Farkı
EntityManager.persist(parent) şeklinde kullanmak gerekir. Açıklaması şöyle. Ben de EntityManager.merge() ile denediğimde, child entity nesnelere id atanmadıklarını yani persist edilmediklerini gördüm.
Merge should be used for "re-attaching entities that got detached and not for taking arbitrary transient objects"
Örnek
Şöyle yaparız.
@Entity
public class Employee {
  @Id
  @Column(name="EMP_ID")
  private long id;
  ...
  @OneToMany (cascade = CascadeType.ALL)
  @JoinTable(name="EMP_PHONE",
    joinColumns={ @JoinColumn(name="EMP_ID", referencedColumnName="EMP_ID") },
    inverseJoinColumns={ @JoinColumn(name="PHONE_ID", referencedColumnName="ID",
                                     unique=true) }
  )

  private List<Phone> phones;
  ...
}
Empoloyee nesnesini Phone nesneleri ekleyerek kaydedersek, CascadeType.ALL kuıllandığımız için Employee ve Phone nesnelerinin birlikte kaydedildiklerini görürüz.

SQL olarak şöyle bir şey görürüz. Toplam 4 nesne yazmak için daha fazla SQL çalıştırmak gerekti. İşte bu yüzden Unidirectional OneToMany tercih edilmez.
insert into employee ...

insert into phone ...
insert into phone ...
insert into phone ...

insert into emp_phone ...
insert into emp_phone ...
insert into emp_phone ...

28 Temmuz 2020 Salı

JMS Kavramları

JMS Gerçekleştirimler
Bazıları şöyle
Apache Qpid (using AMQP), 
IBM MQ (formerly MQSeries, then WebSphere MQ), 
JBoss HornetQ, 
Oracle AQ, 
RabbitMQ, 
TIBCO EMS, 
TIBCO Cloud Messaging, 
Solace

JMS message brokers and the JMS portability myth
Açıklaması şöyle. Yani JMS kodunu aynı tutarak alttaki broker'ı değiştirmek mümkün olmayabilir.
The JMS specification was developed to provide a common Java library to access different messaging vendor’s brokers. It was intended to act as a wrapper to the messaging vendor’s proprietary APIs in the same way JDBC provided similar functionality for database APIs.

Unfortunately, this simple integration turned out not to be the case. The migration of the JMS code from one vendor’s broker to another is quite complex for several reasons:

- Not all JMS features are mandatory (security, topic/queue labeling, clustering, routing, compression, etc.)
- There is no JMS specification for transport
- No specification to define how persistence is implemented
- No specification to define how fault tolerance or high availability is implemented
- Different interpretations of the JMS specification by different vendors result in potentially other behaviors for the same JMS functions
- No specification for security
- There is no specification for value-added features in the brokers (such as topic to queue bridging, inter-broker routing, access control lists, etc.)

Therefore, simple source code migration and interoperability between JMS vendors is a myth! This sounds crazy, doesn’t it?

Server-side JMS filtering and routing
Açıklaması şöyle
Most JMS message brokers provide some features for server-side event processing. These features are handy for some workloads!

Just be careful that server-side processing usually comes with a cost. For instance:

- JMS Pre-filtering scalability issues: The broker has to handle so many things. This can kill the broker in a hidden fashion
- JMS Selectors (= routing) performance issues: It kills 40–50% of performance

Again, sometimes, the drawbacks are acceptable. Then this is a great functionality.
Single JMS deployment vs. multi-region
Açıklaması şöyle
JMS is a client specification, while multi-data center replication is a broker function. I won’t go deep here and put it simply: JMS message brokers are not built for replication scenarios across regions, continents, or hybrid/multi-cloud environments.
Arayüzler
- Connection
- Session
- Destination : Destination olarak Queue, TemporaryQueue, TemporaryTopic, Topic kullanılır
- Message : Bundan kalıtan BytesMessage, MapMessage, ObjectMessage, StreamMessage, TextMessage sınıfları var
- MessageConsumer
- MessageProducer


1. JMS 1.1
1.1 Topic
Mesaj tüm dinleyicilere gönderilir. Açıklaması şöyle. Birden fazla alan olabilir ve her biri kendi kopyasını alır.
Topics implement an one-to-many channel between a producer and multiple consumers. Unlike an queue, every consumer will receive a message send by the producer.

Pros
Multiple consumers can get a message
Decoupling between producer and consumers (publish-and-subscribe pattern)

Cons
More complicated communication flow
A message cannot be recovered for a single listener
Şeklen şöyle


1.2 Queue
Mesaj iki nokta arasındadır. Bir gönderen bir de alan taraf vardır. Eğer birden fazla alan varsa, sadece bir tanesi mesajı alır. Açıklaması şöyle.
They provide a direct channel between a producer and a consumer. The producer creates messages, while the consumer reads one after another. After a message was read, it’s gone. If multiple consumers are registered for a queue, only one of them will get the message.

Pros
Simple messaging pattern with a transparent communication flow
Messages can be recovered by putting them back on the queue

Cons
Only one consumer can get the message
Implies a coupling between producer and consumer as it’s an one-to-one relation

1.3 TemporaryQueue
Açıklaması şöyle. Bağlantı açık olduğu müddetçe hayatta kalır.
A TemporaryQueue object is a unique Queue object created for the duration of a Connection. It is a system-defined queue that can be consumed only by the Connection that created it.

A TemporaryQueue object can be created at either the Session or QueueSession level. Creating it at the Session level allows to the TemporaryQueue to participate in transactions with objects from the Pub/Sub domain. If it is created at the QueueSession, it will only be able participate in transactions with objects from the PTP domain.
Açıklaması şöyle. Genellikle bir işlemin sonucunu (response/acknowledgement) gibi almak için kullanılır.
It is possible to create a temporary queue or topic that will exist as long as the current connection is open or until you call delete. Only the connection that created the queue or topic can receive messages from it, but other connections may send messages. A common use of temporary queues is to use them for receiving responses to a request message. For this you pass the temporary queue in the JMSReplyTo header field.
1.4 Subscriber
Açıklaması şöyle
A subscriber (also known as a consumer) is an application that creates a subscription to receive publications (or messages) from desired topic(s).

There are two types of subscribers:

Non-Durable subscriber: This type of subscriber application will get publications from a messaging provider as long as the application is running. Once the application ends, the messaging provider removes the subscription.

Durable Subscriber: This is the second type of application which receive publications as long as they are running. When the application ends, the messaging provider will cache publications for the subscriber and deliver them when the application comes back.
1.5 Message Delivery
Açıklaması şöyle. Mesaj dağıtımını etkileyen 3 parametre var. DeliveryMode + Priority + Time To Live. MessageProducer.send() metoduna bu paramtreler girilebilir.
JMS provides three ways of influencing the delivery of messages: delivery mode, priority and time-to-live.

- The delivery mode can be either PERSISTENT or NON_PERSISTENT. A persistent message's delivery is guaranteed. A non-persistent message may not arrive in some cases, for example when the messaging server is being shut down before the delivery. The advantage of non-persistent messages is that they may be faster.
- Messages with higher priority can arrive earlier than messages with lower priority.
- Time-to-live timeouts can prevent the delivery of messages that are older than a configurable amount of time.
Delivery mode, priority and timeout are best-effort services. JMS providers should do their best to implement them, but it is not required that they always work as you may expect. For example, JMS allows the delivery of an expired message.

In order to set a message's delivery options, there are variants of the send method that allow setting them for each message. Alternatively you could also set defaults in the MessageProducer. If you don't do this either, implementation-specific defaults are taken (which can usually be configured somewhere).

2. JMS 2.0
Açıklaması şöyle. 2013 yılında yayınlandı
JMS 2.0, which was released in April 2013, is the first update to the JMS specification since version 1.1 was released in 2002.
2.1 Shared Topic Subscription
Açıklaması şöyle. Bir topic'i bir den fazla tüketicinin (consumer) bağlanbilmesini sağlar. Mesaj sadece tek bir tüketiciye gönderilir, yani çoklanmaz. Böylece aslında mesaj işleme parallelleştiriliyor.
In JMS 1.1, a subscription on a topic was not permitted to have more than one consumer at a time. This meant that the work of processing messages on a topic subscription could not be shared among multiple threads, connections, or Java Virtual Machines (JVMs), thereby limiting the scalability of the application. This restriction has been removed in JMS 2.0 by the introduction of a new kind of topic subscription called a shared subscription.
2.2 Delivery Delay
Açıklaması şöyle.
You can now specify a delivery delay on a message. The JMS provider will not deliver the message until after the specified delivery delay has elapsed.
2.3 Sending Messages Asynchronously
Açıklaması şöyle.
Another new feature of JMS 2.0 is the ability to send a message asynchronously.
3. JMS 3
Jakarta Messaging API olarak isim değiştirdi




27 Temmuz 2020 Pazartesi

JPA @PersistenceContex Anotasyonu

Giriş
Şu satırı dahil ederiz.
import javax.persistence.PersistenceContext;
Java EE dünyasında container tarafından yönetilen bir EntityManager nesnesidir.

Spring ile kullanıyorsak bir tane proxy inject edilir. Açıklaması şöyle. Her transaction için farklı bir EntityManager daha doğrusu Hibernate Session nesnesi kullanılır.
Proxied entity manager for @PersistenceContext
Thanks to this transaction-aware proxies, EntityManager injected as @PersistenceContext can be used as a thread-safe object. It means that each requests will be handled by its own EntityManager. Why EntityManager is used through a proxy ? By definition (JSR-317 Final Release, section "7.3 Obtaining an Entity Manager Factory"), EntityManager is not thread safe. But according to the Spring's documentation, EntityManager injected through @PersistenceContext is "thread-safe proxy for the actual transactional EntityManager".
Farklı olmasının açıklaması şöyle
You shouldn't use @Autowired. @PersistenceContext takes care to create a unique EntityManager for every thread. In a production application you can have multiple clients calling your application in the same time. For each call, the application creates a thread. Each thread should use its own EntityManager. Imagine what would happen if they share the same EntityManager: different users would access the same entities.
Bu proxy kodla transaction yaratılmasına izin vermez. Eğer denersek şöyle bir exception alırız
java.lang.IllegalStateException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead
Bu PersistenceContext.EXTENDED halini kullanırsak da kodla transaction yaratılmasına izin vermez. Eğer denersek şöyle bir exception alırız
java.lang.IllegalStateException: Cannot obtain local EntityTransaction from a transaction-synchronized EntityManager
Örnek
Spring ile şöyle yaparız.
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.transaction.Transactional;

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Repository;


@Transactional
@Repository
public class PasswordJpaDaoImpl implements PasswordJpaDaoCustom {

  @PersistenceContext
  private EntityManager em;
  ...
}
Örnek
Spring ile şöyle yaparız.
public interface CustomerRepositoryCustom {

  List<Customer> getByCompany(Long id, int page, int size);
}

@Transactional(readOnly = true)
public class CustomerRepositoryImpl implements CustomerRepositoryCustom {

  @PersistenceContext
  private EntityManager entityManager;

  public List<Customer> getByCompany(Long id, int page, int size) {
    ... 
  }

}
unitName Alanı
persistence.xml dosyasındaki isim ile aynı olmalıdır.
Örnek
Elimizde şöyle bir XML olsun.
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
    http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
  version="2.0">
  <persistence-unit name="manager1" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>

    <properties>
      <property name="javax.persistence.jdbc.driver"
        value="com.mysql.jdbc.Driver"/>
      <property name="javax.persistence.jdbc.user" value="root"/>
      <property name="javax.persistence.jdbc.password" value="mypassword"/>
      <property name="javax.persistence.jdbc.url"
         value="jdbc:mysql://localhost/ptbrowserdb"/>
      <property name="hibernate.dialect"
        value="org.hibernate.dialect.MySQLDialect"/>
  </properties>
  </persistence-unit>
</persistence>
Şöyle yaparız.
@PersistenceContext(unitName = "manager1")
private EntityManager entityManager;
type  Alanı
Açıklaması şöyle. Her transaction için aynı EntityManager daha doğrusu Hibernate Session nesnesi kullanılır.
Notice that you can inject an no thread-safe persistence context by using type="PersistenceContextType.EXTENDED" attribute in @PersistenceContext annotation.