스프링

 

 

 

소스 :

https://github.com/braverokmc79/hateoas-rest-api-account/tree/main

 

 

 

 

 

 

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.0.6</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>net.macaronics.account</groupId>
	<artifactId>AccoutAPI</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>AccoutAPI</name>
	<description>Develop REST APis with HATOAS</description>
	<properties>
		<java.version>17</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-hateoas</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		
		
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

 

 

 

 

 

1. application.properties

spring.jpa.hibernate.ddl-auto=create
#spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true


#logging.level.org.hibernate.SQL=debug
logging.level.org.hibernate.type.descriptor.sql=trace

 

 

 

2.Account

import org.springframework.hateoas.RepresentationModel;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Table(name="accounts")
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
public class Account extends RepresentationModel<Account>{
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;
	
	
	@Column(length =20, nullable=false, unique=true)
	private String accountNumber;
	
	private float balance;

	
	public Account(String accountNumber, float balance) {
		this.accountNumber = accountNumber;
		this.balance = balance;
	}
	
}


 

 

 

3.AccountNotFoundException

package com.example.demo;

public class AccountNotFoundException extends Exception {
}

 

 

4.AccountRepository

package com.example.demo;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;

public interface AccountRepository extends JpaRepository<Account, Integer> {

	@Query("UPDATE Account a SET a.balance= a.balance + :amount   WHERE a.id =:id")
	@Modifying
	public void updateBalance(float amount, Integer id);
	
}

 

 

 

5.AccountService

package com.example.demo;

import java.util.List;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class AccountService {

	private AccountRepository repo;
	
	public AccountService(AccountRepository repo) {
		this.repo=repo;
	}

	public List<Account> listAll(){
		return repo.findAll();
	}
	
	
	public Account get(Integer id) {
		return repo.findById(id).get();
	}
	
	
	public Account save(Account account) {
		return repo.save(account);
	}
	
	
	/** 입금 */
	public Account deposit(float amount, Integer id) {
		repo.updateBalance(-amount, id);
		return repo.findById(id).get();
	}
	
	
	/** 출금 */
	public Account withdraw(float amount, Integer id) {
		repo.updateBalance(-amount, id);
		return repo.findById(id).get();
	}
	
	public void delete(Integer id) throws AccountNotFoundException {
		if(!repo.existsById(id)) {
			throw new AccountNotFoundException();
		}
		repo.deleteById(id);
	}
}

 

 

 

6.Amount

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Amount {

	private float amout;

}

 

 

7.DatabaseLoader

package com.example.demo;

import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class DatabaseLoader {

	private AccountRepository repo;
	
	public DatabaseLoader(AccountRepository repo) {
		this.repo=repo;
	}
	
	@Bean
	public CommandLineRunner initDatabase() {
		return args ->{
			 Account account1=new Account("1234567891", 100);
			 Account account2=new Account("1002003009", 50);
			 Account account3=new Account("1223565893", 1000);
			 
			 //repo.saveAll(List.of(account1, account2, account3));
			 repo.save(account1);
			 repo.save(account2);
			 repo.save(account3);
			 System.out.println("Sample database initialized");
		};
	}
	
	
}

 

 

 

 

 

 

 

8.AccountModelAssember

package com.example.demo;

import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;

import org.springframework.context.annotation.Configuration;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.IanaLinkRelations;
import org.springframework.hateoas.server.RepresentationModelAssembler;


@Configuration
public class AccountModelAssember implements RepresentationModelAssembler<Account, EntityModel<Account>> {

	@Override
	public EntityModel<Account> toModel(Account account) {
		EntityModel<Account> accountModel=EntityModel.of(account);
		
		accountModel.add(linkTo(methodOn(AccountApi.class).getOne(account.getId())).withSelfRel());
		accountModel.add(linkTo(methodOn(AccountApi.class).deposit(account.getId(), null)).withRel("deposits"));
		accountModel.add(linkTo(methodOn(AccountApi.class).withdraw(account.getId(), null)).withRel("withdrawals"));
		accountModel.add(linkTo(methodOn(AccountApi.class).listAll()).withRel(IanaLinkRelations.COLLECTION));

		return accountModel;
	}

}

 

 

 

 

9.AccountApi

package com.example.demo;

import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;

import java.util.List;
import java.util.NoSuchElementException;

import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


//@RequestMapping(value = "/api/events", produces = MediaTypes.HAL_JSON_VALUE)
@RestController
@RequestMapping(value="/api/accounts", produces=MediaTypes.HAL_JSON_VALUE)
//@RequestMapping(value="/api/accounts")
public class AccountApi {

	
	private AccountService service;
	private AccountModelAssember assember;
	
	
	public AccountApi(AccountService service, AccountModelAssember assember) {
		this.service=service;
		this.assember=assember;
	}
	
	
	@GetMapping
	public ResponseEntity<?> listAll(){
		List<Account> listAccounts =service.listAll();	
		if(listAccounts.isEmpty()) {
			return ResponseEntity.noContent().build();
		}
		/**	
		List<EntityModel<Account>> accountModel =listAccounts.stream().map(assember::toModel).collect(Collectors.toList());
		CollectionModel<EntityModel<Account>> collectionModel=CollectionModel.of(accountModel); //컬렉션에  링크 추가
		collectionModel.add(linkTo(methodOn(AccountApi.class).listAll()).withSelfRel());
		
		
		assember 에 내장된 toCollectionModel 을 사용 ==>
		*/
		CollectionModel<EntityModel<Account>> collectionModel=assember.toCollectionModel(listAccounts);
		
		return ResponseEntity.status(HttpStatus.OK).body(collectionModel);
	}
	
	//return new ResponseEntity<>(collectionModel, HttpStatus.OK);
	

	/**
	 * ====>출력

{
    "_embedded": {
        "accountList": [
            {
                "id": 1,
                "accountNumber": "1234567891",
                "balance": 100.0,
                "_links": {
                    "self": {
                        "href": "http://localhost:8080/api/accounts/1"
                    },
                    "collection": {
                        "href": "http://localhost:8080/api/accounts"
                    }
                }
            },
            {
                "id": 2,
                "accountNumber": "1002003009",
                "balance": 50.0,
                "_links": {
                    "self": {
                        "href": "http://localhost:8080/api/accounts/2"
                    },
                    "collection": {
                        "href": "http://localhost:8080/api/accounts"
                    }
                }
            },
            {
                "id": 3,
                "accountNumber": "1223565893",
                "balance": 1000.0,
                "_links": {
                    "self": {
                        "href": "http://localhost:8080/api/accounts/3"
                    },
                    "collection": {
                        "href": "http://localhost:8080/api/accounts"
                    }
                }
            }
        ]
    },
    "_links": {
        "self": {
            "href": "http://localhost:8080/api/accounts"
        }
    }
}


	 */
	
	
	@GetMapping("/{id}")
	public ResponseEntity<?> getOne(@PathVariable("id") Integer id) {
		try {
			 Account account=service.get(id);
			 EntityModel<Account> accountModel=assember.toModel(account);
			 return ResponseEntity.status(HttpStatus.OK).body(accountModel);
		}catch (NoSuchElementException ex) {
			return ResponseEntity.notFound().build();
		}
	}
	
	
/**
 * ===>출력
 * 
 * {
    "id": 1,
    "accountNumber": "1234567891",
    "balance": 100.0,
    "_links": {
        "self": {
            "href": "http://localhost:8080/api/accounts/1"
        },
        "collection": {
            "href": "http://localhost:8080/api/accounts"
        }
    }
}

	
 */
	
	

	
/**
	@PostMapping("/{id}")
    public ResponseEntity<?> createEvent(@PathVariable("id") Integer id){
		Account account =service.get(id);
        URI createdUri = linkTo(AccountApi.class).slash(id).toUri();
      
        System.out.println(" createUri : " + createdUri);
        return ResponseEntity.created(createdUri).body(account);
    }

*/
	
	
	
	
	
	@PostMapping
	public ResponseEntity<?> add(@RequestBody Account account){
		Account savedAccount =service.save(account);
		
		EntityModel<Account> accountModel=assember.toModel(account);
		
		return ResponseEntity.created(linkTo(methodOn(AccountApi.class).getOne(savedAccount.getId())).toUri()).body(accountModel);
	}
	
	
	
	
	/**==> 출력
	 * {
    "id": 8,
    "accountNumber": "987654321",
    "balance": 2950.0,
    "_links": {
        "self": {
            "href": "http://localhost:8080/api/accounts/8"
        },
        "collection": {
            "href": "http://localhost:8080/api/accounts"
        }
    }
	}

	 */
	
	
	
	
	@PutMapping
	public ResponseEntity<?> replace(@RequestBody Account account){
		Account updatedAccount =service.save(account);
		
		EntityModel<Account> accountModel=assember.toModel(updatedAccount);
		
		
		return ResponseEntity.status(HttpStatus.OK).body(accountModel);
	}
	
	
	
	
	/**
	 * ==>
	 * 
	 * {
		    "id":1,
		    "accountNumber":"1234567891",
		    "balance" : 999
		}
		
		
		
		출력=>
		{
		    "id": 1,
		    "accountNumber": "1234567891",
		    "balance": 999.0,
		    "_links": {
		        "self": {
		            "href": "http://localhost:8080/api/accounts/1"
		        },
		        "collection": {
		            "href": "http://localhost:8080/api/accounts"
		        }
		    }
		}

	 */
	
	
	@PatchMapping("/{id}/deposit")
	public ResponseEntity<?> deposit(@PathVariable("id") Integer id, @RequestBody Amount amount){
		Account updatedAccount=service.deposit(amount.getAmout(), id);
		
		EntityModel<Account> accountModel=assember.toModel(updatedAccount);
	
		return ResponseEntity.status(HttpStatus.OK).body(accountModel);
	}
	
	
	@PatchMapping("/{id}/withdraw")
	public ResponseEntity<?> withdraw(@PathVariable("id") Integer id, @RequestBody Amount amount){
		Account updatedAccount=service.withdraw(amount.getAmout(), id);
		
		EntityModel<Account> accountModel=assember.toModel(updatedAccount);
		
		
		return ResponseEntity.status(HttpStatus.OK).body(accountModel);
	}
	
/**
 * 
 {
    "id": 3,
    "accountNumber": "1223565893",
    "balance": 1000.0,
    "_links": {
        "self": {
            "href": "http://localhost:8080/api/accounts/3"
        },
        "deposits": {
            "href": "http://localhost:8080/api/accounts/3/deposit"
        },
        "withdrawals": {
            "href": "http://localhost:8080/api/accounts/3/withdraw"
        },
        "collection": {
            "href": "http://localhost:8080/api/accounts"
        }
    }
}

 * 	
 */
	
	
	@DeleteMapping("/{id}")
	public ResponseEntity<?> delete(@PathVariable("id") Integer id){
		try {
			service.delete(id);
			return ResponseEntity.noContent().build();
		} catch (Exception e) {
			return ResponseEntity.notFound().build();
		}
	}

	
}

 

 

 

 

 

 

 

 

 

Code REST API for Create Operation
Request URI: /api/accounts
Add new account information
HTTP request method: POST
Request body: JSON document represents account information
Response status code:
201 Created for successful creation
Response body: JSON document of newly added account

 

 

 

 

 

 

 

 

 

 

	
	
	@PostMapping
	public ResponseEntity<?> add(@RequestBody Account account){
		Account savedAccount =service.save(account);
		savedAccount.add(linkTo(methodOn(AccountApi.class).getOne(account.getId())).withSelfRel());
		savedAccount.add(linkTo(methodOn(AccountApi.class).listAll()).withRel(IanaLinkRelations.COLLECTION));
		return ResponseEntity.created(linkTo(methodOn(AccountApi.class).getOne(savedAccount.getId())).toUri()).body(savedAccount);
	}
	
	
	
	
	/**==> 출력
	 * {
    "id": 8,
    "accountNumber": "987654321",
    "balance": 2950.0,
    "_links": {
        "self": {
            "href": "http://localhost:8080/api/accounts/8"
        },
        "collection": {
            "href": "http://localhost:8080/api/accounts"
        }
    }
	}

	 */
	

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

Great barkers are no bites. (짖는 개는 물지 않는다.)

댓글 ( 4)

댓글 남기기

작성