JHipster 4.14.1 : Registering a user with addition

2019-01-25 00:15发布

First of all, I'd like to thank "Paul Etienne" for his helpful question and answer! The answer bellow is a little bit more detailed and has been written considering the last version of JHipster (3/17/2018).

The question is clear: how to add new data field(s) to User entity in a JHipster project.

标签: jhipster
1条回答
我命由我不由天
2楼-- · 2019-01-25 00:31

As it's mentioned in the official JHipster website, the best way to add new fields/relations to the default JHipster User is creating a new Entity and link it to User with a one-to-one relationship. You may handle more relationships in the new Entity. Let's call this new Entity UserExtra. Composition between User and UserExtra

The UML diagram above, shows a composition between User and UserExtra meaning that a User has a UserExtra which is strongly dependent to the User and cannot exist without the User. In other words, there should be a user first so we can assign a phone number to.

Step 1: Creating a new entity

I suggest creating the new entity using terminal: jhipster entity userExtra

or

JDL:

enter image description here

Step 2: Modifying the new entity

You may find the new entity class under the domain folder: enter image description here

We may map the id of UserExtra to the id of User so we can use the id of UserExtra as the foreign-key. If so, then we should not use @GeneratedValue annotation for id in UserExtra anymore. Here is a sample UserExtra class. Here is a sample of a modified UserExtra:

package org.jhipster.domain;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

import javax.persistence.*;

import java.io.Serializable;
import java.util.Objects;

/**
 * A UserExtra.
 */
@Entity
@Table(name = "user_extra")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class UserExtra implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    private Long id;

    @OneToOne
    @MapsId
    private User user;

    @Column(name = "phone")
    private String phone;

    // jhipster-needle-entity-add-field - JHipster will add fields here, do not remove
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getPhone() {
        return phone;
    }

    public UserExtra phone(String phone) {
        this.phone = phone;
        return this;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public User getUser() {
        return user;
    }

    public UserExtra user(User user) {
        this.user = user;
        return this;
    }

    public void setUser(User user) {
        this.user = user;
    }

    // jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here, do not remove

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        UserExtra userExtra = (UserExtra) o;
        if (userExtra.getId() == null || getId() == null) {
            return false;
        }
        return Objects.equals(getId(), userExtra.getId());
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(getId());
    }

    @Override
    public String toString() {
        return "UserExtra{" +
            "id=" + getId() +
            ", phone='" + getPhone() + "'" +
            "}";
    }
}

Step 3: Modifying the configuration of the new entity

[You may skip this step if you've used JDL in the 1st step]

There is a json file keeping the configuration of the entity: enter image description here We need to modify the configuration. here is a sample of a modified configuration json file:

{
    "fluentMethods": true,
    "relationships": [
        {
            "relationshipType": "one-to-one",
            "relationshipName": "user",
            "otherEntityName": "user",
            "otherEntityField": "id",
            "ownerSide": true,
            "otherEntityRelationshipName": "userExtra"
        }
    ],
    "fields": [
        {
            "fieldName": "phone",
            "fieldType": "String"
        }
    ],
    "changelogDate": "20180317190851",
    "dto": "no",
    "service": "no",
    "entityTableName": "user_extra",
    "jpaMetamodelFiltering": false,
    "pagination": "no"
}

There are also some xml change logs: enter image description here Here are sample change logs:

added_entity_UserExtra:

<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
    xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
    xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd
                        http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">

    <property name="now" value="now()" dbms="h2"/>

    <property name="now" value="now()" dbms="mysql"/>
    <!--<property name="autoIncrement" value="true"/>-->

    <property name="floatType" value="float4" dbms="postgresql, h2"/>
    <property name="floatType" value="float" dbms="mysql, oracle, mssql"/>

    <!--
        Added the entity UserExtra.
    -->
    <changeSet id="20180317190851-1" author="jhipster">
        <createTable tableName="user_extra">
            <column name="user_id" type="bigint">
                <constraints primaryKey="true" nullable="false"/>
            </column>
            <column name="phone" type="varchar(255)">
                <constraints nullable="true" />
            </column>

            <!-- jhipster-needle-liquibase-add-column - JHipster will add columns here, do not remove-->
        </createTable>

    </changeSet>
    <!-- jhipster-needle-liquibase-add-changeset - JHipster will add changesets here, do not remove-->
</databaseChangeLog>

added_entity_constraints_UserExtra:

<?xml version="1.0" encoding="utf-8"?>
<databaseChangeLog
    xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd">
    <!--
        Added the constraints for entity UserExtra.
    -->
    <changeSet id="20180317190851-2" author="jhipster">

        <addForeignKeyConstraint baseColumnNames="user_id"
                                 baseTableName="user_extra"
                                 constraintName="fk_user_extra_user_id"
                                 referencedColumnNames="id"
                                 referencedTableName="jhi_user"/>

    </changeSet>
</databaseChangeLog>

Just be aware that JHipster uses a table named "JHI_USER" for the User entity and has created a table named "USER_EXTRA" for the new entity. We are going to name the primary-key/id field of the new entity "USER_ID":

enter image description here

Step 4: Modifying ManagedUserVM.java

ManagedUserVM class is sort of a DTO extending UserDTO which is used in the REST controller. enter image description here

The purpose of this modification is adding the new field(s) we are going to bind to User as extra information. Here is a sample code of this class. The phone attribute has been added to the modified class:

package org.jhipster.web.rest.vm;

import org.jhipster.service.dto.UserDTO;
import javax.validation.constraints.Size;

/**
 * View Model extending the UserDTO, which is meant to be used in the user management UI.
 */
public class ManagedUserVM extends UserDTO {

    public static final int PASSWORD_MIN_LENGTH = 4;

    public static final int PASSWORD_MAX_LENGTH = 100;

    @Size(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH)
    private String password;
    private String phone;

    public ManagedUserVM() {
        // Empty constructor needed for Jackson.
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    @Override
    public String toString() {
        return "ManagedUserVM{" +
            "} " + super.toString();
    }
}

Step 5: modifying UserService.java

There is a method named "registerUser" in this calss which is responsible for registering a new user:

enter image description here

The purpose of modifying this service class is to force it to also add a UserExtra object when a User is getting added. here is a sample of a modified "registeruser" method. You may find the added code with "Create and save the UserExtra entity" at top of it as comment:

public User registerUser(UserDTO userDTO, String password, String phone) {

        User newUser = new User();
        Authority authority = authorityRepository.findOne(AuthoritiesConstants.USER);
        Set<Authority> authorities = new HashSet<>();
        String encryptedPassword = passwordEncoder.encode(password);
        newUser.setLogin(userDTO.getLogin());
        // new user gets initially a generated password
        newUser.setPassword(encryptedPassword);
        newUser.setFirstName(userDTO.getFirstName());
        newUser.setLastName(userDTO.getLastName());
        newUser.setEmail(userDTO.getEmail());
        newUser.setImageUrl(userDTO.getImageUrl());
        newUser.setLangKey(userDTO.getLangKey());
        // new user is not active
        newUser.setActivated(false);
        // new user gets registration key
        newUser.setActivationKey(RandomUtil.generateActivationKey());
        authorities.add(authority);
        newUser.setAuthorities(authorities);
        userRepository.save(newUser);
        cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).evict(newUser.getLogin());
        cacheManager.getCache(UserRepository.USERS_BY_EMAIL_CACHE).evict(newUser.getEmail());
        log.debug("Created Information for User: {}", newUser);

        // Create and save the UserExtra entity
        UserExtra newUserExtra = new UserExtra();
        newUserExtra.setUser(newUser);
        newUserExtra.setPhone(phone);
        userExtraRepository.save(newUserExtra);
        log.debug("Created Information for UserExtra: {}", newUserExtra);

        return newUser;
    }

Step 6: modifying AccountResource.java

This class is a REST controller responsible for Account related activities: enter image description here

There is a method named "registerAccount" in this controller that calls "registerUser" method of UserService class:

User user = userService.registerUser(managedUserVM, managedUserVM.getPassword());

We need to modify this line so we can pass the new field(s) to the method:

    @PostMapping("/register")
    @Timed
    @ResponseStatus(HttpStatus.CREATED)
    public void registerAccount(@Valid @RequestBody ManagedUserVM managedUserVM) {
        if (!checkPasswordLength(managedUserVM.getPassword())) {
            throw new InvalidPasswordException();
        }
        userRepository.findOneByLogin(managedUserVM.getLogin().toLowerCase()).ifPresent(u -> {throw new LoginAlreadyUsedException();});
        userRepository.findOneByEmailIgnoreCase(managedUserVM.getEmail()).ifPresent(u -> {throw new EmailAlreadyUsedException();});
        User user = userService.registerUser(managedUserVM, managedUserVM.getPassword(), managedUserVM.getPhone());
        mailService.sendActivationEmail(user);
    }

Step 7: Modifying the user interface

Finally, you need to add an input element into the html file:

enter image description here

<div class="form-group">
                    <label class="form-control-label" for="phone" data-translate="global.form.phone">Phone number</label>
                    <input type="tel" class="form-control" id="phone" name="phone" #phone="ngModel" placeholder="{{'global.form.phone.placeholder' | translate}}"
                           [(ngModel)]="registerAccount.phone">
                </div>

Then, this is what you'll have in the registration page:

enter image description here

And here is the data layer:

JHI_USER

enter image description here

USER_EXTRA

enter image description here

P.S.

The easiest way to do this is just adding a UserExtra entity with a one-to-one relation to User via JDL and adding a "userExtra" while creating a new user.

查看更多
登录 后发表回答