Skip to content

Example: CRUD MVC API exposing the Data Transfer Object (DTO)

Orestes Polyzos edited this page Jan 3, 2020 · 1 revision

Usage Scenario

Expose a MVC API with all the CRUD operations for a User Entity, using a UserDto in the API instead of the User Entity directly. For this specific example Freemarker was used as a Template Engine, but the library has no compatibility limitations as to the Template Engine to be used.
In all the following code blocks, all the imports, constructors, getters and setters are omitted fore brevity

Implementations

User

@Entity(name = "user")
public class User implements ResourcePersistable<Long> {

  @Id
  @Column(name = "id", nullable = false)
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @Column(name = "first_name", nullable = false)
  private String firstName;

  @Column(name = "last_name", nullable = false)
  private String lastName;

  @Column(name = "email", nullable = false, unique = true)
  private String email;

  @Override
  public Long getRpId() {
    return this.id;
  }
}

UserDto

public class UserDto {

  private Long id;

  @NotNull
  @NotBlank
  private String fullName;

  @NotNull
  @NotBlank
  @Email
  private String email;
}

UserRepository

@Repository
public interface UserRepository extends CrudRepository<User, Long> { }

UserDtoService

@Service
public class UserDtoService implements RpService<User, Long, UserDto> {

  private final UserRepository userRepository;

  @Override
  public CrudRepository<User, Long> getRepository() {
    return userRepository;
  }

  @Override
  public Function<User, UserDto> getEntityToDtoConverter() {
    return user -> UserDto.builder()
        .id(user.getId())
        .fullName(user.getFirstName() + " " + user.getLastName())
        .email(user.getEmail())
        .build();
  }

  @Override
  public Function<UserDto, User> getDtoToEntityConverter() {
    return userDto -> User.builder()
        .id(userDto.getId())
        .firstName(userDto.getFullName().split(" ")[0])
        .lastName(userDto.getFullName().split(" ")[1])
        .email(userDto.getEmail())
        .build();
  }
}

UserDtoViewController

@RequestMapping("/view/user/dto")
@Controller
public class UserDtoViewController implements RpViewController<UserDto, Long> {

  private final UserDtoService userService;

  @Override
  public RpService<User, Long, UserDto> getService() {
    return userService;
  }

}

userdto/userdto.ftlh

See the whole file
<#import "/spring.ftl" as spring/>
<html>
<head>
  <title>Users</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css">
  <link rel="stylesheet" type="text/css" href="/swip-styles.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
  <div class="page-header">
    <h1>Users</h1>
  </div>
    <#if infoMessage??>
      <div class="panel panel-default">
        <div class="panel-body infoMessage">${infoMessage}</div>
      </div>
    </#if>
    <#if errorMessage??>
      <div class="panel panel-default">
        <div class="panel-body errorMessage">${errorMessage}</div>
      </div>
    </#if>
  <div class="panel panel-default">
    <div class="panel-heading"><a href="#create-resource-panel" data-toggle="collapse">Create</a></div>
    <div id="create-resource-panel" class="panel-body collapse">
      <form action="/view/user/dto" method="POST" id="user" name="user">
        <div class="form-group">
          <label for="fullName">FullName</label>
            <@spring.bind "userdtofullName"/>
          <input type="text" class="form-control" id="fullName" name="fullName" value="${userdtofullName!""}"/>
            <#list spring.status.errorMessages as error>
              <span class="errorMessage">${error}</span>
            </#list>
        </div>
        <div class="form-group">
          <label for="email">Email</label>
            <@spring.bind "userdtoemail"/>
          <input type="text" class="form-control" id="email" name="email" value="${userdtoemail!""}"/>
            <#list spring.status.errorMessages as error>
              <span class="errorMessage">${error}</span>
            </#list>
        </div>
        <div class="col-12 controls">
            <span>
                <button type="submit" id="btn-submit" class="btn btn-success btn-md">Submit</button>
                <button type="reset" id="btn-clear" class="btn btn-danger btn-md">Clear</button>
            </span>
        </div>
      </form>
    </div>
  </div>

  <div class="panel panel-default">
    <div class="panel-heading"><a href="#search-resource-panel" data-toggle="collapse">Search</a></div>
    <div id="search-resource-panel" class="panel-body collapse">
      <form action="/view/user/dto/search" method="POST" id="userSearch" name="userSearch">
        <div class="attention">Specific search fields should be added here</div>
        <div class="col-12 controls">
            <span>
                <button type="submit" id="btn-submit" class="btn btn-primary">Search</button>
                <button type="reset" id="btn-clear" class="btn btn-danger">Clear</button>
            </span>
        </div>
      </form>
    </div>
  </div>
</div>
<div class="container-fluid">
    <#if userdtoList?has_content>
      <div class="panel panel-default">
        <div class="panel-heading"><a href="#results-resource-panel" data-toggle="collapse">Results</a></div>
        <div id="results-resource-panel" class="panel-body collapse in">
          <div class="table-responsive">
            <table id="resource-table" class="table table-hover table-striped">
              <thead>
              <tr>
                <th>ID</th>
                <th>FullName</th>
                <th>Email</th>
                <th>Edit</th>
                <th>Delete</th>
              </tr>
              </thead>
              <tbody>
              <#list userdtoList as resource>
                <tr>
                  <td>${resource.id!""}</td>
                  <td>${resource.fullName!""}</td>
                  <td>${resource.email!""}</td>
                  <td>
                    <form action="/view/user/dto/${resource.id}/edit" method="GET">
                      <button type="submit" class="btn btn-success">
                        <span class="glyphicon glyphicon-cog"></span>
                      </button>
                    </form>
                  </td>
                  <td>
                    <form action="/view/user/dto/${resource.id}/delete" method="POST">
                      <button type="submit" class="btn btn-danger" onclick="return confirm('Are you sure?')">
                        <span class="glyphicon glyphicon-remove"></span>
                      </button>
                    </form>
                  </td>
                </tr>
              </#list>
              </tbody>
            </table>
          </div>
        </div>
      </div>
    </#if>
</div>
</body>
</html>

userdto/edit-userdto.ftlh

See the whole file
<#import "/spring.ftl" as spring/>
<html>
<head>
  <title>Edit User</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css">
  <link rel="stylesheet" type="text/css" href="/swip-styles.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
  <div class="page-header">
    <h1>Edit User</h1>
  </div>
    <#if infoMessage??>
      <div class="panel panel-default">
        <div class="panel-body infoMessage">${infoMessage}</div>
      </div>
    </#if>
    <#if errorMessage??>
      <div class="panel panel-default">
        <div class="panel-body errorMessage">${errorMessage}</div>
      </div>
    </#if>
  <div class="panel panel-default">
    <div class="panel-heading"><a href="#edit-resource-panel" data-toggle="collapse">Edit</a></div>
    <div id="edit-resource-panel" class="panel-body collapse in">
      <form action="/view/user/dto/${user.id}/edit" method="POST" id="user" name="user">
        <div class="form-group">
          <label for="id">ID</label>
            <@spring.bind "userdto.id"/>
          <input type="text" class="form-control" id="id" name="id" value="${userdto.id!""}" readonly/>
            <#list spring.status.errorMessages as error>
              <span class="errorMessage">${error}</span>
            </#list>
        </div>
        <div class="form-group">
          <label for="fullName">FullName</label>
            <@spring.bind "userdto.fullName"/>
          <input type="text" class="form-control" id="fullName" name="fullName" value="${userdto.fullName!""}"/>
            <#list spring.status.errorMessages as error>
              <span class="errorMessage">${error}</span>
            </#list>
        </div>
        <div class="form-group">
          <label for="email">Email</label>
            <@spring.bind "userdto.email"/>
          <input type="text" class="form-control" id="email" name="email" value="${userdto.email!""}"/>
            <#list spring.status.errorMessages as error>
              <span class="errorMessage">${error}</span>
            </#list>
        </div>
        <div class="col-12 controls">
            <span>
                <button type="submit" id="btn-submit" class="btn btn-success btn-md">Submit</button>
                <button type="button" id="btn" class="btn btn-danger btn-md" onclick="window.history.back();">Cancel</button>
            </span>
        </div>
      </form>
    </div>
  </div>
</div>
</body>
</html>