30 Eylül 2022 Cuma

SpringData Jdbc JdbcTemplate.execute metodu

Giriş
execute metodu parametresiz şekilde veya callback şeklinde parametreler alacak şekilde kullanılabilir

execute metodu
İmzası şöyle
public void execute(final String sql) throws DataAccessException
Örnek
Şöyle yaparız
jdbc.execute("create table if not exist
Player(id int primary key, name varchar(30), team int)"
);
execute metodu - ConnectionCallback
Şöyle yaparız. JDBC Connection nesnesi verir.
jdbcTemplate.execute(new ConnectionCallback<String>() {
  public String doInConnection(Connection con) throws SQLException {
    ...
  }     
}
execute metodu - CallableStatementCreator
Stored Procedure çağırmak için kullanılır. Şöyle yaparız.
jdbcTemplate.execute(
  new CallableStatementCreator() {
    public CallableStatement createCallableStatement(Connection con) throws SQLException{
      CallableStatement cs = con.prepareCall("{call sys.dbms_stats.gather_table_stats
        (ownname=>user, tabname=>'" + cachedMetadataTableName +
        "', estimate_percent=>20, method_opt=>'FOR ALL COLUMNS SIZE 1', degree=>0,
        granularity=>'AUTO', cascade=>TRUE, no_invalidate=>FALSE, force=>FALSE) }");
      return cs;
    }
  },
  new CallableStatementCallback() {
    public Object doInCallableStatement(CallableStatement cs) throws SQLException{
      cs.execute();
      return null; // returned from the jdbcTemplate.execute method
    }
  }
);

execute metodu - PreparedStatementCreator + PreparedStatementCallback
İmzası şöyle
public <T> T execute(PreparedStatementCreator psc, 
                     PreparedStatementCallback<T> action) 
throws DataAccessException
Örnek
Şöyle yaparız
public void updateOrderDateByUserId(String userId, String newOrderDate) 
  throws SQLException {

  final String sql = "UPDATE order_details SET order_date=? WHERE user_id=?";

  PreparedStatementCreator psc = new PreparedStatementCreator() {
    @Override
    public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
      PreparedStatement ps = con.prepareStatement(sql);
      ps.setString(1, newOrderDate);
      ps.setString(2, userId);
      return ps;
    }
  };

  this.jdbcTemplate.execute(psc, new PreparedStatementCallback<String>() {
    @Override
    public String doInPreparedStatement(PreparedStatement ps) 
      throws SQLException, DataAccessException {
      ps.executeUpdate();
      return "SUCCESS";
    }
  });
}





SpringData ElasticSearch ElasticsearchOperations Sınıfı

Giriş
Şu satırı dahil ederiz
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHits; import org.springframework.data.elasticsearch.core.SearchPage; import org.springframework.data.elasticsearch.core.query.Criteria; import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
search metodu
Örnek
Şöyle yaparız. Burada "org.springframework.data.elasticsearch.core.SearchHits" dönülüyor
@Service
@RequiredArgsConstructor
@Slf4j
public class CityService {

@NonNull
final ElasticsearchOperations esTemplate;

public SearchHits<City> searchHits(String name, String country, String subcountry, 
                                     Pageable pageable) {
CriteriaQuery query = buildSearchQuery(name, country, subcountry);
query.setPageable(pageable);
return esTemplate.search(query, City.class);
}

private CriteriaQuery buildSearchQuery(String name, String country, String subcountry) {
var criteria = new Criteria();
if (nonNull(name)) {
criteria.and(new Criteria("name").contains(name));
}
if (nonNull(country)) {
criteria.and(new Criteria("country").expression(country));
}
if (nonNull(subcountry)) {
criteria.and(new Criteria("subcountry").is(subcountry));
}
return new CriteriaQuery(criteria);
}
}
Kullanmak için şöyle yaparız
@RequestMapping(value = CityController.ROOT_PATH, produces = APPLICATION_JSON_VALUE)
@RequiredArgsConstructor
public class CityController {

  static final String ROOT_PATH = "/api/cities";

  @NonNull
  final CityService service;

  @GetMapping("/search_hits")
  public SearchHits<City> searchHits(@PathParam("name") String name, 
    @PathParam("country") String country,
    @PathParam("subcountry") String subcountry, Pageable pageable) {
    return service.searchHits(name, country, subcountry, pageable);
  }
}
Çıktısı şöyle. Ancak bu çıktıda sayfalama bilgisi yok
{
  "totalHits": 3,
  "totalHitsRelation": "EQUAL_TO",
  "maxScore": "NaN",
  "scrollId": null,
  "searchHits": [
    {
      "index": "city",
      "id": "yqoYEIIB55LQo2aMkOKS",
      "score": "NaN",
      "sortValues": [
        "benešov"
      ],
      "content": {
        "id": "yqoYEIIB55LQo2aMkOKS",
        "name": "Benešov",
        "country": "Czech Republic",
        "subcountry": "Central Bohemia",
        "geonameid": 3079508
      },
      "highlightFields": {},
      "innerHits": {},
      "nestedMetaData": null,
      "routing": null,
      "explanation": null,
      "matchedQueries": []
    },
    ...
  ],
  "aggregations": null,
  "suggest": null,
  "empty": false
}
aggregations alanı
Açıklaması şöyle
Since Spring Data Elasticsearch 5.0.1, the SearchHit instance contains aggregations attribute filled (it was null in the previous version). 

Aynı şeyi Spring ile sayfalama bilgisini de dönerek yapabiliriz. Şöyle yaparız
import static org.springframework.data.elasticsearch.core.SearchHitSupport.searchPageFor;

public SearchPage<City> searchPage(String name, String country, String subcountry, 
                                   Pageable pageable) {
  return searchPageFor(searchHits(name, country, subcountry, pageable), pageable);
}
Bu durumda çıktı şöyle. Ancak bu sefer de content ve searchHits altında gerçek veri iki defa tekrar ediyor
{
  "content": [
    {
      "index": "city",
      "id": "yqoYEIIB55LQo2aMkOKS",
      "score": "NaN",
      "sortValues": [
        "benešov"
      ],
      "content": {
        "id": "yqoYEIIB55LQo2aMkOKS",
        "name": "Benešov",
        "country": "Czech Republic",
        "subcountry": "Central Bohemia",
        "geonameid": 3079508
      },
      "highlightFields": {},
      "innerHits": {},
      "nestedMetaData": null,
      "routing": null,
      "explanation": null,
      "matchedQueries": []
    },
    ...
  ],
  "pageable": {
    "sort": {
      "empty": false,
      "sorted": true,
      "unsorted": false
    },
    "offset": 0,
    "pageNumber": 0,
    "pageSize": 5,
    "unpaged": false,
    "paged": true
  },
  "searchHits": {
    "totalHits": 3,
    "totalHitsRelation": "EQUAL_TO",
    "maxScore": "NaN",
    "scrollId": null,
    "searchHits": [
      {
        "index": "city",
        "id": "yqoYEIIB55LQo2aMkOKS",
        "score": "NaN",
        "sortValues": [
          "benešov"
        ],
        "content": {
          "id": "yqoYEIIB55LQo2aMkOKS",
          "name": "Benešov",
          "country": "Czech Republic",
          "subcountry": "Central Bohemia",
          "geonameid": 3079508
        },
        "highlightFields": {},
        "innerHits": {},
        "nestedMetaData": null,
        "routing": null,
        "explanation": null,
        "matchedQueries": []
      },
      ...
    ],
    "aggregations": null,
    "suggest": null,
    "empty": false
  },
  "totalPages": 1,
  "totalElements": 3,
  "size": 5,
  "number": 0,
  "sort": {
    "empty": false,
    "sorted": true,
    "unsorted": false
  },
  "first": true,
  "last": true,
  "numberOfElements": 3,
  "empty": false
}
Bundan da kurtulmak için şöyle yaparız
import static org.springframework.data.elasticsearch.core.SearchHitSupport.unwrapSearchHits;
@SuppressWarnings("unchecked")
public Page<City> search(String name, String country, String subcountry, Pageable pageable) {
  return (Page<City>) unwrapSearchHits(searchPage(name, country, subcountry, pageable));
}
Bu durumda çıktı şöyle. Her şey content altında
{
  "content": [
    {
      "id": "yqoYEIIB55LQo2aMkOKS",
      "name": "Benešov",
      "country": "Czech Republic",
      "subcountry": "Central Bohemia",
      "geonameid": 3079508
    },
    ...
  ],
  "pageable": {
    "sort": {
      "empty": false,
      "sorted": true,
      "unsorted": false
    },
    "offset": 0,
    "pageNumber": 0,
    "pageSize": 5,
    "unpaged": false,
    "paged": true
  },
  "last": true,
  "totalPages": 1,
  "totalElements": 3,
  "size": 5,
  "number": 0,
  "sort": {
    "empty": false,
    "sorted": true,
    "unsorted": false
  },
  "first": true,
  "numberOfElements": 3,
  "empty": false
}



28 Eylül 2022 Çarşamba

SpringBoot spring.datasource Dbcp2 Ayarları

Örnek
Şöyle yaparız
spring:
datasource:
  url : jdbc:mariadb://{DB Path}?autoReconnect=true
  hikari:
    # on Connection Time
    max-lifetime: 40000
    read-only: false
    connection-timeout: 9000
    # Max DB Connection Pool Size : 8
    maximum-pool-size: 8
    # Test Sql Connection
    connection-init-sql: SELECT 1
    # Default Connection Size : 4
    minimum-idle: 4
  dbcp2:
    # send something when Start Web
    test-on-borrow: true
    # something
    validation-query: SELECT 1
    # Test Sql
    test-while-idle: true


27 Eylül 2022 Salı

SpringContext SmartInitializingSingleton Arayüzü - Extension Point

Giriş
Şeklen şöyle
Açıklaması şöyle
... which is a callback interface called after all singleton objects managed by the spring container are initialized. The trigger timing is after postProcessAfterInitialization.
Örnek
Şöyle yaparız
public class TestSmartInitializingSingleton implements SmartInitializingSingleton {
  @Override
  public void afterSingletonsInstantiated() {
    ...
  }
}







SpringContext ApplicationContextInitializer Arayüzü - Extension Point

Giriş
Şeklen şöyle. Henüz daha SpringContext yüklenmeden çağrılır

Açıklaması şöyle
You can see that the spring container has not been initialized at this time. If your extension takes effect, 
there are two ways:

(1) In the startup class, use the springApplication.addInitializers(new TestApplicationContextInitializer()) statement to add
(2) Configuration file configuration context.initializer.classes=com.example.demo.TestApplicationContextInitialize

Örnek
Şöyle yaparız
public class TestApplicationContextInitializer implements ApplicationContextInitializer {
  @Override
  public void initialize(ConfigurableApplicationContext applicationContext) {
    ...
  }
}



26 Eylül 2022 Pazartesi

SpringBoot spring.jpa Hibernate'e Özel Ayarlar - Second Level Cache

Ayarlar
use_second_level_cache=true 
 2. kademe cache kullanılacağı belirtilir. 2. kademe cache açılıp kapatılabilen bir şey. Açıklaması şöyle
Set this to true to enable the second-level cache.
hibernate.cache.region.factory_class
 hangi cache gerçekleştirimi kullanılacağı belirtilir. Cache gerçekleştirimi olarak Hazelcast, Redis, EhCache vs kullanılabilir. Eğer bu alanı tanımlamazsak sanırım Spring kendisi otomatik olarak bulmaya çalışıyor Açıklaması şöyle
This property specifies the caching provider to be used for the second-level and query cache. For example, to use EHCache, set it to org.hibernate.cache.ehcache.EhCacheRegionFactory.
hibernate.cache.provider_configuration_file_resource_path
Açıklaması şöyle
This property specifies the path to the configuration file for the caching provider. For example, for EHCache, it can be set to ehcache.xml.

Entity İçin CacheConcurrency
Açıklaması şöyle
Hibernate supports three cache modes for Second-level Caching: read-only, read-write, and transactional. The read-only cache mode is used for data that is not expected to change frequently. The read-write cache mode is used for data that is frequently updated. The transactional cache mode is used for data that is updated within a transaction.

The cache concurrency strategy determines how multiple threads access the cache. Hibernate supports multiple cache concurrency strategies such as READ_ONLY, NONSTRICT_READ_WRITE, READ_WRITE, and TRANSACTIONAL.
CacheConcurrencyStrategy.READ_ONLY
Açıklaması şöyle
- This strategy is suitable for entities that are read frequently but rarely modified. It assumes that the data is mostly static and does not change frequently.
- No automatic synchronization with the database when entities are updated or deleted.
CacheConcurrencyStrategy.READ_WRITE
Açıklaması şöyle
This strategy is used for entities that are frequently both read and modified. It ensures that cached data is kept up-to-date with changes in the database.

- Automatic synchronization with the database when entities are updated or deleted.
- This strategy guarantees strong consistency which it achieves by using ‘soft’ locks: When a cached entity is updated, a soft lock is stored in the cache for that entity as well, which is released after the transaction is committed. All concurrent transactions that access soft-locked entries will fetch the corresponding data directly from database.
-  Suitable for entities that are updated occasionally but read frequently.

In JTA Environment: If your application uses JTA for managing transactions (common in enterprise-level applications), you need to specify the property hibernate.transaction.manager_lookup_class. This property should specify a strategy for obtaining the JTA TransactionManager, which is crucial for managing distributed transactions in JTA environments.
CacheConcurrencyStrategy.NONSTRICT_READ_WRITE
Açıklaması şöyle
- This strategy allows for better performance than READ_WRITE while sacrificing some cache consistency.
- Suitable for entities that are updated less frequently than they are read and where slight data staleness is acceptable.
- Automatic synchronization with the database when entities are updated or deleted but with less strict consistency guarantees compared to READ_WRITE.
- Provides improved write performance compared to READ_WRITE because it doesn't acquire locks during read operations.
Örnek
Şöyle yaparız
@Data @Entity @Table(name = "users") @Cacheable @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; private String email; }
Örnek - One-Many & Many-Many Relations
Açıklaması şöyle
When Hibernate caches a collection, it doesn’t cache the entire collection of entities but rather caches the IDs of the entities contained in the collection.
Şöyle yaparız
@Entity @Cacheable public class Category { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @OneToMany(mappedBy = "category") @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) private List<Product> products; // Getters and setters } @Entity @Cacheable public class Product { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @ManyToOne private Category category; // Getters and setters }
Açıklaması şöyle
- Hibernate creates a cache region named com.example.model.Category.products. This region is dedicated to storing cached collections of products associated with categories.
- Let’s say we have a category with ID 1 and it has products with IDs 101, 102, and 103. Hibernate stores this data in the cache using a key-value pair.
- The key-value pair might look like "Category:1:products — [101, 102, 103]"

EhCache
- EhCache için "org.hibernate.cache.ehcache.EhCacheRegionFactory" yazılır. Şu satırı dahil ederiz
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-ehcache</artifactId>
  <version>5.6.11.Final</version>
</dependency>
Örnek
Şöyle yaparız.
# caching
spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.use_query_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=
  org.hibernate.cache.ehcache.EhCacheRegionFactory
Örnek
Şöyle yaparız
# Enable @Cacheable annotation spring.jpa.properties.javax.persistence.sharedCache.mode = ENABLE_SELECTIVE # Enable second-level cache. spring.jpa.properties.hibernate.cache.use_second_level_cache = true # Specifies the class to handle the second-level cache. spring.jpa.properties.hibernate.cache.region.factory_class = org.hibernate.cache.ehcache.EhCacheRegionFactory # Check whether the cache is applied spring.jpa.properties.hibernate.generate_statistics = true
Örnek
Şöyle yaparız.
jpa:
    hibernate:
      ddl-auto: update
      show_sql: true
    properties:
      hibernate:
        hbm2ddl:
          auto: update
        cache:
          use_second_level_cache: true
          region.factory_class: org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory
          use_query_cache: true
          use_collection_cache: true
Hazelcast
Şu satırı dahil ederiz
<dependency>
  <groupId>com.hazelcast</groupId>
  <artifactId>hazelcast-hibernate53</artifactId>
  <version>2.2.1</version>
</dependency>
Bu bağımlılık ile HazelcastCacheRegionFactory sınıfı geliyor. Hibernate ile bağlantıyı sağlayan sınıf bu. Kalıtım şöyle
org.hibernate.cache.spi.RegionFactory
  org.hibernate.cache.RegionFactory
    AbstractHazelcastCacheRegionFactory
      HazelcastCacheRegionFactory
  
Örnek
Şöyle yaparız. Burada Hibernate Second Level Cache aynı zamanda SpringCache içinde kullanılıyor. Bu yüzden instance_name belirtiliyor ve shutdown_on_session_factory_close alanı false yapılıyor
spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.use_query_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=
  com.hazelcast.hibernate.HazelcastCacheRegionFactory

spring.jpa.properties.hibernate.cache.hazelcast.instance_name=users-app
spring.jpa.properties.hibernate.cache.hazelcast.shutdown_on_session_factory_close=false
Açıklaması şöyle
Here, along with a general second-level cache, we also enable Query cache. As we are using an embedded Hazelcast that will be used in a distributed environment, the factory class is set into com.hazelcast.hibernate.HazelcastCacheRegionFactory. A detailed description of properties and settings for Hibernate caching and Hazelcast implementation for the second-level cache can be found in documentation correspondingly Hibernate Caching and Hibernate Second Level Cache.
Hazelcast ayarlarında şöyle yaparız
hazelcast:
  instance-name: users-app
Burada entity şöyle tanımlanır @Cache ve CacheConcurrencyStrategy sınıfları Hibernate kütüphanesinden geliyor.
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

import javax.persistence.Cacheable;
import javax.persistence.Entity;

@Cacheable
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Entity
public class User {
  // skipped
}
Örnek
Şöyle yaparız
# Second-Level Cache Configuration
spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.use_query_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=
  com.hazelcast.hibernate.HazelcastCacheRegionFactory
spring.jpa.properties.hibernate.cache.hazelcast.use_native_client=true
spring.jpa.properties.hibernate.cache.use_minimal_puts=true
spring.jpa.properties.hibernate.generate_statistics=true
logging.level.org.hibernate.type=trace
spring.cache.type=hazelcast

Redis
Örnek
Şu satırı dahil ederiz. Bu örnekte hibernate.cache.region.factory_class belirtilmiyor. 
<dependency>
  <groupId>org.redisson</groupId>
  <artifactId>redisson-hibernate-53</artifactId>
  <version>3.19.0</version>
</dependency>