11 Kasım 2021 Perşembe

SpringData Projections - Open Projections - SpEL Kullanır

Giriş
Open Projections içinde SpEL kullanılır. Bu kullanımda Spring sorgu cümlesini optimize etmez. Açıklaması şöyle.
Spring Data cannot apply query execution optimizations in this case, because the SpEL expression could use any attribute of the aggregate root.
Örnek
Elimizde şöyle bir @Entity kodu olsun
@Data
@Entity
@Table(name = "CUSTOMER_ORDER")
public class OrderEntity {

  @Id
  @Column(name = "ID")
  private Integer id;

  @Column(name = "ORDER_NUMBER")
  private String orderNumber;

  @Column(name = "TOTAL_AMOUNT")
  private String totalAmount;

  @OneToOne
  @JoinColumn(name="CUSTOMER_ID", nullable=false)
  private CustomerEntity customer;
}
Daha sonra projection kodunu tanımlamak için şöyle yaparız
public interface CustomerDetailsDTO {

  Integer getCustomerId();

  @Value("#{target.firstName + ' ' + target.lastName}")
  String getCustomerName();

  String getCity();

  String getCountry();

  @Value("#{@mapperUtility.buildOrderDTO(target.orderNumber, target.totalAmount)}")
  OrderDTO getOrder();
}
Burada SpEL içinde bir başka kod kullanılıyor. O da şöyle
@Component
public class MapperUtility {

  public OrderDTO buildOrderDTO(Long orderNumber, Double totalAmount) {
    OrderDTO order = new OrderDTO();
    order.setOrderNumber(orderNumber);
    order.setTotalAmount("$" + totalAmount);
    return order;
  }
}

public class OrderDTO {
  private Long orderNumber;
  private String totalAmount;

  // Getters and Setter
}
Repository kodumuz şöyledir. Repository belirtilen SQL cümlesini çalıştırır, ancak dönüş tipi @Entity değil de bizim kodladığımı DTO olduğu için projection devreye girer.
@Repository
public interface CustomerRepository extends JpaRepository<CustomerEntity, Integer> {
  
  @Query(name = "customerEntity.getCustomerDetails", nativeQuery = true)
  List<CustomerDetailsDTO> getCustomerDetails();
}
Örnek
Elimizde şöyle bir @Entity kodu olsun. Post nesnesi ve buna ait Tag listesi olsun
class Post {
  @Id
  Long id;
  String title;
 @ManyToMany(fetch = FetchType.LAZY,
                cascade = {
                    CascadeType.PERSIST,
                    CascadeType.MERGE,

                })
  @JoinTable(name = "post_tags",
             joinColumns = { @JoinColumn(name = "post_id") },
             inverseJoinColumns = { @JoinColumn(name = "tag_id") })

  Set<Tag> tags;
  // constructor
  // getters
  // setters
}

class Tag{
  @Id
  Long id
  String name;
  // getters, setter, constructor
}
Daha sonra projection kodunu tanımlamak için şöyle yaparız.
interface PostProjection{
  Long getId();
  String getTitle();
  @Value("#{target.tags.size()}")
  int  getNumberOfTags();
}
SpEL yerine kodla projection yapılabilir. Şöyle yaparız.
import static java.util.stream.Collectors.toSet;

public interface PostProjection {
  String getTitle();

  Set<Tag> getTags();

  default Set<String> getTagsNames() {
    return getTags().stream().map(Tag::getName).collect(toSet());
  }

}

8 Kasım 2021 Pazartesi

Swagger @Tag Anotasyonu

Giriş
Şu satırı dahil ederiz
import io.swagger.v3.oas.annotations.tags.Tag;
Açıklaması şöyle
In springfox implementation this tag represents the equivalent of the tag @Api
name Alanı
Örnek
Şöyle yaparız. Burada @Tags içinde @Tag anotasyonları var
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.tags.Tags;

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

@RestController
@Tags(value = {@Tag(name = "User")})
public class UserController {

  @GetMapping("/users")
  @Tags(value = {@Tag(name = "User")})
  @Operation(
    summary = "Get Users",
    description = "Get Users",
    responses = {@ApiResponse(
      responseCode = "200",
      content = {@Content(mediaType = "application/json")}
     )}
   )
  public List<User> getUsers() {
    ...
  }
  ...
}

Örnek
Şöyle yaparız. Aynı Tag değeri @Operation içinde kullanılabilir.
@RestController
@RequestMapping("/people")
@Tag(name = "People", description = "Endpoints for managing people")
public class PersonController {
  ...
}

2 Kasım 2021 Salı

SpringKafka Producer TopicBuilder Sınıfı

Giriş
Şu satırı dahil ederiz
import org.springframework.kafka.config.TopicBuilder;
Örnek
Şöyle yaparız
import org.apache.kafka.clients.admin.NewTopic;
import org.springframework.kafka.config.TopicBuilder;

@Configuration
public class TopicProducer {

  static final String NAME_INCREMENT = "name_increment";
  static final String NAME_DECREMENT = "name_decrement";

  @Bean
  public NewTopic increment() {
    return TopicBuilder.name(NAME_INCREMENT)
      .partitions(10)
      .replicas(1)
      .build();
  }

  @Bean
  public NewTopic decrement() {
    return TopicBuilder.name(NAME_DECREMENT)
      .partitions(10)
      .replicas(1)
      .build();
  }
}
Kullanmak için şöyle yaparız
@Service
public class NameService {

  @Autowired
  private final KafkaTemplate<String, Name> template;

  public void decrement(String name) {
    template.send(TopicProducer.NAME_DECREMENT, new Name(name));
  }

  public void increment(String name) {
    template.send(TopicProducer.NAME_INCREMENT, new Name(name));
  }
}
Kullanmak için şöyle yaparız
@Component
public class NameConsumer {

  @KafkaListener(id = "increment", topics = TopicProducer.NAME_INCREMENT)
  public void increment(Name name) {
    ...
   }

  @KafkaListener(id = "decrement", topics = TopicProducer.NAME_DECREMENT)
  public void decrement(Name name) {
    ...
  }
}
 

 

1 Kasım 2021 Pazartesi

SpringGraphQL

Gradle
İki proje var. 
1. Şöyle yaparız
implementation("org.springframework.boot:spring-boot-starter-graphql")
2. Şöyle yaparız. Kullanmayın
implementation("com.graphql-java-kickstart:graphql-spring-boot-starter:14.0.0")
SpringGraphQL anotasyonlara dayalı. Açıklaması şöyle
So instead of implementing GraphQLQueryResolver/GraphQLMutationResolver, we use @QueryMapping and @MutationMapping alongside with @Argument to resolve the method arguments. Also there is @SchemaMapping (@QueryMapping/@MutationMapping’s parent) which allows a method to act as the DataFetcher for a field from the schema mapping.  
@MutationMapping Anotasyonu
Örnek
Şöyle yaparız
@RestController
public class StudentController {

  private Map<String, Student> map = new HashMap<>();
  private AtomicInteger id = new AtomicInteger();

  @QueryMapping
  public Student getStudentById(@Argument String id) {
    return map.get(id);
  }

  @MutationMapping
  public Student addStudent(@Argument String name) {
    int id = this.id.incrementAndGet();
    Student student = new Student(String.valueOf(id), name);
    map.put(String.valueOf(id), student);
    return student;
  }

  @QueryMapping
  public List<Student> getStudents(){
    return new ArrayList<>(map.values());
  }
}
@QueryMapping Anotasyonu
Şu satırı dahil ederiz
import org.springframework.graphql.data.method.annotation.QueryMapping;
Örnek
Şöyle yaparız
@Controller
public class GreetingController {
  @QueryMapping //1
  public String hello() { //2
    return "Hello, world!";
  }
}
Açıklaması şöyle
1. Binds this method to a query, i.e. a field under the Query type
2. Determine the query from the method name if not declared on the annotation
DataFetcherExceptionResolver Arayüzü
Exception handling için gerekir. Açıklaması şöyle
DataFetcherExceptionResolver is an asynchronous contract. For most implementations, it would be sufficient to extend DataFetcherExceptionResolverAdapter and override one of its resolveToSingleError or resolveToMultipleErrors methods that resolve exceptions synchronously.

QuerydslPredicateExecutor Arayüzü
Şu satırı dahil ederiz
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
Örnek
Şöyle yaparız
public interface AccountRepository extends Repository<Account,Long>,
  QuerydslPredicateExecutor<Account> {
}
Kullanmak için şöyle yaparız
//For single result queries
DataFetcher<Account> dataFetcher = 
  QuerydslDataFetcher.builder(repository).single();

//For multi-result queries
DataFetcher<Iterable<Account>> dataFetcher = 
  QuerydslDataFetcher.builder(repository).many();