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.
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:
Step 2: Modifying the new entity
You may find the new entity class under the domain folder:
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:
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:
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":
Step 4: Modifying ManagedUserVM.java
ManagedUserVM class is sort of a DTO extending UserDTO which is used in the REST controller.
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:
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:
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:
<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:
And here is the data layer:
JHI_USER
USER_EXTRA
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.