这是indexloc提供的服务,不要输入任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions spring-boot-modules/spring-boot-validation/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>

<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.baeldung.customstatefulvalidation;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;

@SpringBootApplication
@ConfigurationPropertiesScan
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.baeldung.customstatefulvalidation.configuration;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("com.baeldung.tenant")
public class TenantChannels {
private String[] channels;

public String[] getChannels() {
return channels;
}

public void setChannels(String[] channels) {
this.channels = channels;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.baeldung.customstatefulvalidation.controllers;

import com.baeldung.customstatefulvalidation.model.PurchaseOrderItem;
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class PurchaseOrderController {

@PostMapping("/api/purchasing/")
public ResponseEntity<String> createPurchaseOrder(@Valid @RequestBody PurchaseOrderItem item) {
// start processing this purchase order and tell the caller we've accepted it

return ResponseEntity.accepted().build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package com.baeldung.customstatefulvalidation.model;

import com.baeldung.customstatefulvalidation.validators.AvailableChannel;
import com.baeldung.customstatefulvalidation.validators.AvailableWarehouseRoute;
import com.baeldung.customstatefulvalidation.validators.ChoosePacksOrIndividuals;
import com.baeldung.customstatefulvalidation.validators.ProductCheckDigit;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;

@ChoosePacksOrIndividuals
@AvailableWarehouseRoute
public class PurchaseOrderItem {

@ProductCheckDigit
@NotNull
@Pattern(regexp = "A-\\d{8}-\\d")
private String productId;

private String sourceWarehouse;
private String destinationCountry;

@AvailableChannel
private String tenantChannel;

private int numberOfIndividuals;
private int numberOfPacks;
private int itemsPerPack;

@org.hibernate.validator.constraints.UUID
private String clientUuid;

public String getProductId() {
return productId;
}

public void setProductId(String productId) {
this.productId = productId;
}

public String getSourceWarehouse() {
return sourceWarehouse;
}

public void setSourceWarehouse(String sourceWarehouse) {
this.sourceWarehouse = sourceWarehouse;
}

public String getDestinationCountry() {
return destinationCountry;
}

public void setDestinationCountry(String destinationCountry) {
this.destinationCountry = destinationCountry;
}

public String getTenantChannel() {
return tenantChannel;
}

public void setTenantChannel(String tenantChannel) {
this.tenantChannel = tenantChannel;
}

public int getNumberOfIndividuals() {
return numberOfIndividuals;
}

public void setNumberOfIndividuals(int numberOfIndividuals) {
this.numberOfIndividuals = numberOfIndividuals;
}

public int getNumberOfPacks() {
return numberOfPacks;
}

public void setNumberOfPacks(int numberOfPacks) {
this.numberOfPacks = numberOfPacks;
}

public int getItemsPerPack() {
return itemsPerPack;
}

public void setItemsPerPack(int itemsPerPack) {
this.itemsPerPack = itemsPerPack;
}

public String getClientUuid() {
return clientUuid;
}

public void setClientUuid(String clientUuid) {
this.clientUuid = clientUuid;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.baeldung.customstatefulvalidation.repository;

import org.springframework.stereotype.Repository;

import java.util.Set;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toSet;

@Repository
public class WarehouseRouteRepository {
private Set<String> availableRoutes = Stream.of(
"Springfield:USA",
"Hartley:USA",
"Gentoo:PL",
"Mercury:GR")
.collect(toSet());

public boolean isWarehouseRouteAvailable(String sourceWarehouse, String destinationCountry) {
return availableRoutes.contains(sourceWarehouse + ":" + destinationCountry);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.baeldung.customstatefulvalidation.validators;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Constraint(validatedBy = AvailableChannelValidator.class)
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface AvailableChannel {
String message() default "must be available tenant channel";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.baeldung.customstatefulvalidation.validators;

import com.baeldung.customstatefulvalidation.configuration.TenantChannels;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Arrays;
import java.util.Set;
import java.util.stream.IntStream;

import static java.util.stream.Collectors.toSet;

public class AvailableChannelValidator implements ConstraintValidator<AvailableChannel, String> {

@Autowired
private TenantChannels tenantChannels;

private Set<String> channels;

@Override
public void initialize(AvailableChannel constraintAnnotation) {
channels = Arrays.stream(tenantChannels.getChannels()).collect(toSet());
}

@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return channels.contains(value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.baeldung.customstatefulvalidation.validators;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Constraint(validatedBy = AvailableWarehouseRouteValidator.class)
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface AvailableWarehouseRoute {
String message() default "chosen warehouse route must be active";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.baeldung.customstatefulvalidation.validators;

import com.baeldung.customstatefulvalidation.model.PurchaseOrderItem;
import com.baeldung.customstatefulvalidation.repository.WarehouseRouteRepository;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.springframework.beans.factory.annotation.Autowired;

public class AvailableWarehouseRouteValidator implements ConstraintValidator<AvailableWarehouseRoute, PurchaseOrderItem> {
@Autowired
private WarehouseRouteRepository warehouseRouteRepository;

@Override
public boolean isValid(PurchaseOrderItem value, ConstraintValidatorContext context) {
return warehouseRouteRepository.isWarehouseRouteAvailable(value.getSourceWarehouse(), value.getDestinationCountry());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.baeldung.customstatefulvalidation.validators;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Constraint(validatedBy = ChoosePacksOrIndividualsValidator.class)
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface ChoosePacksOrIndividuals {
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.baeldung.customstatefulvalidation.validators;

import com.baeldung.customstatefulvalidation.model.PurchaseOrderItem;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

public class ChoosePacksOrIndividualsValidator implements ConstraintValidator<ChoosePacksOrIndividuals, PurchaseOrderItem> {
@Override
public boolean isValid(PurchaseOrderItem value, ConstraintValidatorContext context) {
context.disableDefaultConstraintViolation();

boolean isValid = true;

if ((value.getNumberOfPacks() == 0) == (value.getNumberOfIndividuals() == 0)) {
isValid = false;
context.disableDefaultConstraintViolation();
// either both are zero, or both are turned on
if (value.getNumberOfPacks() == 0) {
context.buildConstraintViolationWithTemplate("must choose a quantity when no packs")
.addPropertyNode("numberOfIndividuals")
.addConstraintViolation();
context.buildConstraintViolationWithTemplate("must choose a quantity when no individuals")
.addPropertyNode("numberOfPacks")
.addConstraintViolation();
} else {
context.buildConstraintViolationWithTemplate("cannot be combined with number of packs")
.addPropertyNode("numberOfIndividuals")
.addConstraintViolation();
context.buildConstraintViolationWithTemplate("cannot be combined with number of individuals")
.addPropertyNode("numberOfPacks")
.addConstraintViolation();
}
}

if (value.getNumberOfPacks() > 0 && value.getItemsPerPack() == 0) {
isValid = false;

context.buildConstraintViolationWithTemplate("cannot be 0 when using packs")
.addPropertyNode("itemsPerPack")
.addConstraintViolation();
}

return isValid;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.baeldung.customstatefulvalidation.validators;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Constraint(validatedBy = ProductCheckDigitValidator.class)
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ProductCheckDigit {
String message() default "must have valid check digit";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.baeldung.customstatefulvalidation.validators;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

import java.util.stream.IntStream;

public class ProductCheckDigitValidator implements ConstraintValidator<ProductCheckDigit, String> {

@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
return false;
}

String[] parts = value.split("-");

return parts.length == 3 && checkDigitMatches(parts[1], parts[2]);
}

private static boolean checkDigitMatches(String productCode, String checkDigit) {
int sumOfDigits = IntStream.range(0, productCode.length())
.map(character -> Character.getNumericValue(productCode.charAt(character)))
.sum();

int checkDigitProvided = Character.getNumericValue(checkDigit.charAt(0));
return checkDigitProvided == sumOfDigits % 10;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,8 @@ spring.jpa.hibernate.ddl-auto=update

# Disable Hibernate validation
spring.jpa.properties.jakarta.persistence.validation.mode=none


com.baeldung.tenant.channels[0]=retail
com.baeldung.tenant.channels[1]=wholesale

Loading