-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from bifrurcated/jpa-hibernate
Jpa hibernate
- Loading branch information
Showing
11 changed files
with
311 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
48 changes: 48 additions & 0 deletions
48
src/main/java/com/bifurcated/wallet/controller/WalletEntityController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package com.bifurcated.wallet.controller; | ||
|
||
import com.bifurcated.wallet.errors.UnsupportedOperationTypeError; | ||
import com.bifurcated.wallet.operation.OperationType; | ||
import com.bifurcated.wallet.service.WalletEntityService; | ||
import com.bifurcated.wallet.service.WalletService; | ||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.web.bind.annotation.*; | ||
|
||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.UUID; | ||
|
||
@RestController | ||
@RequestMapping("/api/v2") | ||
public class WalletEntityController { | ||
|
||
private final WalletEntityService walletService; | ||
private final Map<String, OperationType> operations; | ||
|
||
|
||
@Autowired | ||
public WalletEntityController(WalletEntityService walletEntityService, Map<String, OperationType> operationTypeMap) { | ||
this.walletService = walletEntityService; | ||
this.operations = operationTypeMap; | ||
} | ||
|
||
public record WalletResponse(UUID id, Float amount){} | ||
public record WalletRequest( | ||
@JsonProperty("valletId") UUID walletId, | ||
String operationType, | ||
Float amount | ||
){} | ||
@PostMapping("/wallet") | ||
public WalletResponse wallet(@RequestBody WalletRequest request) { | ||
var operationType = request.operationType().toLowerCase(); | ||
var wallet = walletService.addAmount(request.walletId(), request.amount()); | ||
return new WalletResponse(wallet.getId(), wallet.getAmount()); | ||
} | ||
|
||
public record BalanceResponse(Float amount){} | ||
@GetMapping("/wallets/{WALLET_UUID}") | ||
public BalanceResponse balance(@PathVariable(value = "WALLET_UUID") UUID id) { | ||
Float amount = walletService.amount(id); | ||
return new BalanceResponse(amount); | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
src/main/java/com/bifurcated/wallet/data/UseIdOrGenerate.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package com.bifurcated.wallet.data; | ||
|
||
import org.hibernate.engine.spi.SharedSessionContractImplementor; | ||
import org.hibernate.generator.BeforeExecutionGenerator; | ||
import org.hibernate.generator.EventType; | ||
|
||
import java.util.EnumSet; | ||
import java.util.UUID; | ||
|
||
import static org.hibernate.generator.EventType.INSERT; | ||
|
||
public class UseIdOrGenerate implements BeforeExecutionGenerator { | ||
|
||
@Override | ||
public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue, EventType eventType) { | ||
Object id = session.getEntityPersister(null, owner).getIdentifier(owner, session); | ||
return id != null ? id : UUID.randomUUID(); | ||
} | ||
|
||
|
||
@Override | ||
public EnumSet<EventType> getEventTypes() { | ||
return EnumSet.of(INSERT); | ||
} | ||
|
||
} |
30 changes: 30 additions & 0 deletions
30
src/main/java/com/bifurcated/wallet/data/WalletEntity.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package com.bifurcated.wallet.data; | ||
|
||
import jakarta.persistence.Column; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.Id; | ||
import lombok.Getter; | ||
import lombok.Setter; | ||
import lombok.ToString; | ||
import org.hibernate.annotations.GenericGenerator; | ||
import org.hibernate.annotations.JdbcTypeCode; | ||
import org.hibernate.type.SqlTypes; | ||
|
||
import java.util.UUID; | ||
|
||
@Setter | ||
@Getter | ||
@ToString | ||
@Entity(name = "wallet") | ||
public class WalletEntity { | ||
|
||
@Id | ||
@GeneratedValue(generator="UseIdOrGenerate") | ||
@GenericGenerator(name="UseIdOrGenerate", type = UseIdOrGenerate.class) | ||
@Column(name = "id", nullable = false) | ||
@JdbcTypeCode(SqlTypes.UUID) | ||
private UUID id; | ||
|
||
private Float amount; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
src/main/java/com/bifurcated/wallet/repository/WalletEntityRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.bifurcated.wallet.repository; | ||
|
||
import com.bifurcated.wallet.data.WalletEntity; | ||
import jakarta.annotation.Nonnull; | ||
import jakarta.persistence.LockModeType; | ||
import org.springframework.data.jpa.repository.*; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
import java.util.Optional; | ||
import java.util.UUID; | ||
|
||
public interface WalletEntityRepository extends JpaRepository<WalletEntity, UUID>, JpaSpecificationExecutor<WalletEntity> { | ||
@Transactional | ||
@Modifying | ||
@Query("update wallet w set w.amount = ?1 where w.id = ?2") | ||
int updateAmountById(Float amount, UUID id); | ||
|
||
@Transactional | ||
@Modifying | ||
@Query("update wallet w set w.amount = w.amount + ?2 where w.id = ?1") | ||
void updateAddAmountById(UUID id, Float amount); | ||
} |
43 changes: 43 additions & 0 deletions
43
src/main/java/com/bifurcated/wallet/service/WalletEntityService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package com.bifurcated.wallet.service; | ||
|
||
import com.bifurcated.wallet.data.WalletEntity; | ||
import com.bifurcated.wallet.errors.WalletNotFoundError; | ||
import com.bifurcated.wallet.repository.WalletEntityRepository; | ||
import jakarta.persistence.EntityManager; | ||
import jakarta.persistence.LockModeType; | ||
import jakarta.transaction.Transactional; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.stereotype.Service; | ||
|
||
import java.util.Optional; | ||
import java.util.UUID; | ||
|
||
@Service | ||
public class WalletEntityService { | ||
|
||
private final WalletEntityRepository repository; | ||
private final EntityManager entityManager; | ||
|
||
@Autowired | ||
public WalletEntityService(WalletEntityRepository walletEntityRepository, EntityManager entityManager) { | ||
this.repository = walletEntityRepository; | ||
this.entityManager = entityManager; | ||
} | ||
|
||
@Transactional | ||
public WalletEntity addAmount(UUID id, Float amount) { | ||
var entity = entityManager.find(WalletEntity.class, id, LockModeType.PESSIMISTIC_WRITE); | ||
var wallet = Optional.ofNullable(entity).orElseThrow(WalletNotFoundError::new); | ||
wallet.setAmount(wallet.getAmount() + amount); | ||
return repository.save(wallet); | ||
} | ||
|
||
public WalletEntity addAmountUsingUpdate(UUID id, Float amount) { | ||
repository.updateAddAmountById(id, amount); | ||
return repository.findById(id).orElseThrow(WalletNotFoundError::new); | ||
} | ||
|
||
public Float amount(UUID id) { | ||
return repository.findById(id).orElseThrow(WalletNotFoundError::new).getAmount(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
126 changes: 126 additions & 0 deletions
126
src/test/java/com/bifurcated/wallet/controller/WalletEntityControllerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package com.bifurcated.wallet.controller; | ||
|
||
import com.bifurcated.wallet.data.Wallet; | ||
import com.bifurcated.wallet.data.WalletEntity; | ||
import com.bifurcated.wallet.repository.WalletEntityRepository; | ||
import com.bifurcated.wallet.repository.WalletRepo; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import org.junit.jupiter.api.AfterEach; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; | ||
import org.springframework.boot.test.context.SpringBootTest; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.test.context.ActiveProfiles; | ||
import org.springframework.test.web.servlet.MockMvc; | ||
import org.springframework.test.web.servlet.ResultActions; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.UUID; | ||
import java.util.concurrent.Callable; | ||
import java.util.concurrent.ExecutorService; | ||
import java.util.concurrent.Executors; | ||
import java.util.concurrent.Future; | ||
|
||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; | ||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; | ||
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; | ||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; | ||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; | ||
|
||
|
||
@SpringBootTest | ||
@AutoConfigureMockMvc | ||
@ActiveProfiles("test") | ||
class WalletEntityControllerTest { | ||
private static final Logger logger = LoggerFactory.getLogger(WalletEntityControllerTest.class); | ||
private static final String END_POINT_PATH = "/api/v2"; | ||
@Autowired | ||
private MockMvc mockMvc; | ||
@Autowired | ||
private ObjectMapper objectMapper; | ||
@Autowired | ||
private WalletEntityRepository repository; | ||
|
||
@BeforeEach | ||
public void setup() { | ||
repository.deleteAll(); | ||
WalletEntity wallet1 = new WalletEntity(); | ||
wallet1.setId(UUID.fromString("b3919077-79e6-4570-bfe0-980ef18f3731")); | ||
wallet1.setAmount(2000F); | ||
repository.save(wallet1); | ||
WalletEntity wallet2 = new WalletEntity(); | ||
wallet2.setId(UUID.fromString("bc10fad7-94f1-4047-be4e-311247eed5fb")); | ||
wallet2.setAmount(500F); | ||
repository.save(wallet2); | ||
WalletEntity wallet3 = new WalletEntity(); | ||
wallet3.setId(UUID.fromString("9ebef4de-68e3-43ad-a812-42193919ff02")); | ||
wallet3.setAmount(302000F); | ||
repository.save(wallet3); | ||
} | ||
@AfterEach | ||
public void clear() { | ||
repository.deleteAll(); | ||
} | ||
|
||
@Test | ||
public void testWalletGetAmount() throws Exception { | ||
record BalanceResponse(Float amount){} | ||
BalanceResponse balanceResponse = new BalanceResponse(2000F); | ||
String response = objectMapper.writeValueAsString(balanceResponse); | ||
String id = "b3919077-79e6-4570-bfe0-980ef18f3731"; | ||
this.mockMvc.perform(get(END_POINT_PATH+"/wallets/"+id)) | ||
.andDo(print()) | ||
.andExpect(status().isOk()) | ||
.andExpect(content().json(response)); | ||
} | ||
|
||
@Test | ||
public void testWalletGetAmountNotFound() throws Exception { | ||
String id = "b3919077-79e6-4570-bfe0-980ef18f3700"; | ||
this.mockMvc.perform(get(END_POINT_PATH+"/wallets/"+id)) | ||
.andDo(print()) | ||
.andExpect(status().isNotFound()); | ||
} | ||
|
||
@Test | ||
public void testWalletDepositDDOSAttack() throws Exception { | ||
record WalletRequest(UUID valletId, String operationType, Float amount){} | ||
record BalanceResponse(Float amount){} | ||
UUID id = UUID.fromString("b3919077-79e6-4570-bfe0-980ef18f3731"); | ||
WalletRequest walletRequest = new WalletRequest( | ||
id, "DEPOSIT", 1000F); | ||
|
||
String requestBody = objectMapper.writeValueAsString(walletRequest); | ||
|
||
ExecutorService executorService = Executors.newFixedThreadPool(300); | ||
List<Future<ResultActions>> futures = new ArrayList<>(); | ||
for (int i = 0; i < 300; i++) { | ||
Callable<ResultActions> callable = () -> { | ||
ResultActions perform = this.mockMvc.perform(post(END_POINT_PATH + "/wallet") | ||
.contentType(MediaType.APPLICATION_JSON) | ||
.content(requestBody)); | ||
return perform.andExpect(status().isOk()); | ||
}; | ||
|
||
Future<ResultActions> future = executorService.submit(callable); | ||
futures.add(future); | ||
} | ||
for (Future<ResultActions> future : futures) { | ||
future.get(); | ||
} | ||
|
||
BalanceResponse balanceResponse = new BalanceResponse(302000F); | ||
String response = objectMapper.writeValueAsString(balanceResponse); | ||
this.mockMvc.perform(get(END_POINT_PATH+"/wallets/"+id)) | ||
.andDo(print()) | ||
.andExpect(status().isOk()) | ||
.andExpect(content().json(response)); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters