Java Android - Room with multiple databases for each user logged in - java

I'm creating an app with a local database using Room. Every user have an option to create a 'Group' model, and these Groups are displayed in the main feed.
The groups are stored in the database, but when the user logs out and then logs in with a different account he can still see the previous account groups.
How can I fix that? Is there a way to implement the database so that after calling the logout function the DB will swap to a different DB?
Thanks.

How can I fix that?
2 Ways (at least) as will be shown
Is there a way to implement the database so that after calling the logout function the DB will swap to a different DB?
Yes. However I'd suggest that it is not the best way and is a little more complicated than the "Universal way".
Universal Way - Recommended approach
Have a single Database the log include the user (userId) as a column in the log and showing just the logs for the current user.
Mulltiple or UserOnly approach
You have at least 3 databases one for the User's so they can login and a database for each use with the log table. Switching between users makes this more complex as it requires opening another database when the user is switched. Unless using arrays (why would you? (rhetoric)) then if switching from a user to another then you should ideally close the first user's database.
Working Example
The following is a working example/demo that utilises BOTH approaches.
Hopefully the comments and names used explain.
The Entities first :-
User (common to both approaches) :-
#Entity
class User {
#PrimaryKey
Long userId;
#ColumnInfo(index = true)
String userName;
String userPassword;
public User(){};
#Ignore
public User(Long userId, String userName, String userPassword) {
this.userId = userId;
this.userName = userName;
this.userPassword = userPassword;
}
#Ignore
public User(String userName, String userPassword) {
this(null,userName,userPassword);
}
public Long getUserId() {
return userId;
}
public String getUserName() {
return userName;
}
public String getUserPassword() {
return userPassword;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public void setUserName(String userName) {
this.userName = userName;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
}
UserLog (common to both approaches)
#Entity(tableName = "log")
class UserLog {
#PrimaryKey
Long id;
Long timestamp;
Long userId;
String logData;
public UserLog(){}
#Ignore
public UserLog(User user, String logData) {
this.id = null;
this.timestamp = System.currentTimeMillis();
this.userId = user.getUserId();
this.logData = logData;
}
public Long getId() {
return id;
}
public Long getUserId() {
return userId;
}
public Long getTimestamp() {
return timestamp;
}
public String getLogData() {
return logData;
}
public void setId(Long id) {
this.id = id;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public void setTimestamp(Long timestamp) {
this.timestamp = timestamp;
}
public void setLogData(String logData) {
this.logData = logData;
}
}
That's the Entities and both are common to each approach.
Now the Dao's
UniversalDao (for the recommended approach)
#Dao
interface UniversalDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
long insertUser(User user);
#Insert
long insertLog(UserLog userLog);
#Query("SELECT * FROM user WHERE userId=:userId")
User getUserById(long userId);
#Query("SELECT * FROM log WHERE userId=:userId")
List<UserLog> getUserLogs(long userId);
#Query("SELECT userId FROM user WHERE userName=:userName AND userPassword=:password")
long verifyUserLogin(String userName, String password);
}
The getuserLogs Dao being used for getting and showing a specific user's logs.
UserOnlyDao
#Dao
interface UserOnlyDao {
#Insert
long insertUserLog(UserLog userLog);
#Query("SELECT * FROM Log")
List<UserLog> getUserLogs();
}
simpler BUT ....
The Database (#Database) classes utilsing singletons
UniversalUserDatabase (all-in-one database approach - Recommended)
#Database(entities = {User.class,UserLog.class},version = 1)
abstract class UniversalUserDatabase extends RoomDatabase {
abstract UniversalDao getAllDao();
private static UniversalUserDatabase instance;
static UniversalUserDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(
context,
UniversalUserDatabase.class,"universaluser.db"
).allowMainThreadQueries()
.build();
}
return instance;
}
}
Pretty straightforward
UserOnlyDatabase (may need some time to understand this one)
#Database(entities = {UserLog.class},version = 1)
abstract class UserOnlyDatabase extends RoomDatabase {
abstract UserOnlyDao getUserOnlyDao();
private static volatile UserOnlyDatabase instance;
private static volatile User currentUser;
static UserOnlyDatabase getInstance(Context context, User user) {
if (user == null) {
throw new RuntimeException("Attempt to open Invalid - CANNOT continue");
}
if ( currentUser == null || (currentUser.getUserId() != user.getUserId())) {
if (instance != null) {
if (instance.isOpen()) {
instance.close();
}
instance = null;
}
}
if (instance == null) {
instance = Room.databaseBuilder(context,UserOnlyDatabase.class,user.userName+ "_log")
.allowMainThreadQueries()
.build();
}
return instance;
}
}
Note this works but has only been tested for a simple sceanrio
the getInstance method is where the swapping of userlog database is undertaken
Now putting it all together in an activity that demonstrates.
Note for convenience and brevity this runs on the main thread. So no consideration for not running it on the main thread has been included. However, as singletons are used there is probably little that isn't covered, just beware that there may be issues to consider.
MainActivity
public class MainActivity extends AppCompatActivity {
static int MAXLOGINATTEMPTS = 5;
static String TAG = "USERLOGINFO";
UniversalUserDatabase uniDB;
UniversalDao uniDao;
UserOnlyDatabase uoDB;
UserOnlyDao uoDao;
User currentUser = new User();
int loginAttempts = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
uniDB = UniversalUserDatabase.getInstance(this);
uniDao = uniDB.getAllDao();
// Add Some Users
uniDao.insertUser(new User("Fred","password"));
uniDao.insertUser(new User(Long.valueOf(1000),"Mary","password"));
// Login to the first user logging the login attempt
if (forceLogin("Fred","password",true)) {
Log.d(TAG,currentUser.userName + " successfully logged in.");
uniDao.insertLog(new UserLog(currentUser,"Logged In Successfully"));
uoDB = UserOnlyDatabase.getInstance(this,currentUser);
uoDao = uoDB.getUserOnlyDao();
uoDao.insertUserLog(new UserLog(currentUser,"UOLogged in was Good"));
}
// Write the logs to the log for Universal and userOnly approach
logCurrentUserLog();
logUserOnlyLog();
// SWITCH USER and database for User Only approach logging login to the logs
if (forceLogin("Mary","password",true)) {
Log.d(TAG,currentUser.userName + " successfully logged in.");
uniDao.insertLog(new UserLog(currentUser,"Logged in Successfuly"));
uoDB = UserOnlyDatabase.getInstance(this,currentUser);
uoDao = uoDB.getUserOnlyDao();
uoDao.insertUserLog(new UserLog(currentUser,"UOLogged in was Good"));
}
logCurrentUserLog();
logUserOnlyLog();
}
private boolean addUser(String userName, String password) {
return uniDao.insertUser(new User(userName,password)) > 0;
}
private boolean login(String userName, String password) {
long userId;
if (++loginAttempts >= MAXLOGINATTEMPTS) return false;
if (!((userId = uniDao.verifyUserLogin(userName,password)) > 0)) return false;
currentUser = uniDao.getUserById(userId);
loginAttempts = 0;
return true;
}
private boolean forceLogin(String userName, String password, boolean crashIfTooManyAttempts) {
while (!login(userName,password)) {
if (loginAttempts >= MAXLOGINATTEMPTS) {
if (crashIfTooManyAttempts)
throw new RuntimeException("Too Many Login Attempts - Goodbye");
return false;
}
}
return true;
}
private void logCurrentUserLog() {
for(UserLog ul: uniDao.getUserLogs(currentUser.getUserId())) {
Log.d(TAG,ul.timestamp + " User = " + currentUser.userName + "Log = " + ul.logData);
}
}
private void logUserOnlyLog() {
for(UserLog ul: uoDao.getUserLogs()) {
Log.d(TAG + "_UO",ul.timestamp + " UO ID = " +ul.userId + " " + ul.id + ul.logData);
}
}
}
So this :-
Gets an instance of the Universal Database and gets the Dao.
Adds 2 users Fred and Mary (Mary with an id forced to be 1000 just for show, makes no difference to the demo what Mary's id is)
1.Logs on to Fred writing.
A login entry is added for Fred in the Universal Database log.
An instance of Fred's UserOnly database is obtained and a login entry is made in Fred's log in Fred's database.
Fred's log entries, from the Universal database, are output to the Device's log.
Fred's log entries from his personal database are output to the Device's log.
USER is SWITCHED to Mary when Mary log's on.
A login entry is added for Mary in the Universal Database Log.
An instance of Mary's UserOnly database is obtained and a login entry is made in Mary's log in Mary's database.
Mary's log entries. from the Universal Database Log are output to the Device's log.
NOTE Fred's entries ARE NOT output even though they exist in the Log..
Mary's log entries from his personal database are output to the Device's log.
Results
The Device's Log after the first run :-
2021-04-18 13:38:18.381 D/USERLOGINFO: Fred successfully logged in.
2021-04-18 13:38:18.412 D/USERLOGINFO: 1618717098382 User = FredLog = Logged In Successfully
2021-04-18 13:38:18.415 D/USERLOGINFO_UO: 1618717098385 UO ID = 1 1UOLogged in was Good
2021-04-18 13:38:18.419 D/USERLOGINFO: Mary successfully logged in.
2021-04-18 13:38:18.453 D/USERLOGINFO: 1618717098419 User = MaryLog = Logged in Successfuly
2021-04-18 13:38:18.457 D/USERLOGINFO_UO: 1618717098429 UO ID = 1000 1UOLogged in was Good
The database's via Database Inspector
Showing the Universal Database Log (recommended apporaoch) :-
As Fred's database was closed this is shown as closed
Showing The UserOnly log (Mary's) :-

You can shoud clear your databases every time a users logs out:
YourDataBase.getInstance(context).clearTables()

Related

Android Room - How can I check if an entity with the same name already exists before inserting?

I'm creating an app using the mvvm pattern with android room, but I've ran into some trouble validating user input. When a user wants to add an ingredient to the app, they are required to enter a name for this ingredient. I want the app to notify the user if the name is already in use. I have tried some stuff using the Transformations.Map() functions but without any success.
I'm fairly new to the mvvm pattern and LiveData, and I've been stuck on this for quite a while now so any advice would be appreciated.
This is the ingredient entity:
#Entity(tableName = "ingredient")
public class BaseIngredient {
#PrimaryKey(autoGenerate = true)
private int id;
private String name;
private String category;
#ColumnInfo(name = "cooking_time")
private int cookingTime;
#Ignore
public BaseIngredient() {
}
public BaseIngredient(int id, #NonNull String name, #NonNull String category, int cookingTime)
throws InvalidValueException {
this.id = id;
setName(name);
setCookingTime(cookingTime);
setCategory(category);
}
public void setName(String name) throws InvalidNameException {
if (name == null || name.isEmpty())
throw new InvalidNameException("Name is empty");
if (!name.matches("[A-z0-9]+( [A-z0-9]+)*"))
throw new InvalidNameException("Name contains invalid tokens");
this.name = name;
}
public void setCategory(String category) throws InvalidCategoryException {
if (category == null || category.isEmpty())
throw new InvalidCategoryException("Category is empty");
if (!category.matches("[A-z0-9]+"))
throw new InvalidCategoryException("Category contains invalid tokens");
this.category = category;
}
public void setCookingTime(int cookingTime) throws InvalidCookingTimeException {
if (cookingTime < 1)
throw new InvalidCookingTimeException("Time must be positive");
this.cookingTime = cookingTime;
}
/* getters */
public boolean isValid() {
return name != null && category != null && cookingTime != 0;
}
This is the IngredientRepository I'm using:
private IngredientDao ingredientDao;
private LiveData<List<BaseIngredient>> ingredients;
public IngredientRepository(Application application) {
LmcfyDatabase database = LmcfyDatabase.getDatabase(application.getApplicationContext());
ingredientDao = database.ingredientDao();
ingredients = ingredientDao.getAllIngredients();
}
public LiveData<List<BaseIngredient>> getAllIngredients() {
return ingredients;
}
public LiveData<List<BaseIngredient>> getIngredientsWithQuery(String query) {
return ingredientDao.getIngredientsWithQuery("%" + query + "%");
}
public void insert(BaseIngredient ingredient) {
LmcfyDatabase.databaseWriteExecutor.execute(() -> {
ingredientDao.insert(ingredient);
});
}
public LiveData<Integer> getIngredientsWithNameCount(String name) {
return ingredientDao.getIngredientsWithNameCount(name);
}
The IngredientDao:
#Insert(onConflict = OnConflictStrategy.IGNORE, entity = BaseIngredient.class)
long insert(BaseIngredient ingredient);
#Delete(entity = BaseIngredient.class)
void delete(BaseIngredient ingredient);
#Query("SELECT * FROM ingredient")
LiveData<List<BaseIngredient>> getAllIngredients();
#Query("SELECT * FROM ingredient WHERE name LIKE :query")
LiveData<List<BaseIngredient>> getIngredientsWithQuery(String query);
#Query("SELECT COUNT(id) FROM ingredient WHERE name LIKE :name")
LiveData<Integer> getIngredientsWithNameCount(String name);
And finally the ViewModel that is used to create an Ingredient
private final IngredientRepository repository;
private final BaseIngredient ingredient;
private final MutableLiveData<String> nameError;
private final MutableLiveData<String> categoryError;
private final MutableLiveData<String> cookingTimeError;
private final MutableLiveData<Boolean> ingredientValidStatus;
public AddIngredientViewModel(#NonNull Application application) {
super(application);
repository = new IngredientRepository(application);
ingredient = new BaseIngredient();
nameError = new MutableLiveData<>();
categoryError = new MutableLiveData<>();
cookingTimeError = new MutableLiveData<>();
ingredientValidStatus = new MutableLiveData<>();
}
public void onNameEntered(String name) {
try {
ingredient.setName(name);
nameError.setValue(null);
} catch (InvalidNameException e) {
nameError.setValue(e.getMessage());
} finally {
updateIngredientValid();
}
}
public void onCategoryEntered(String category) {
try {
ingredient.setCategory(category);
categoryError.setValue(null);
} catch (InvalidCategoryException e) {
categoryError.setValue(e.getMessage());
} finally {
updateIngredientValid();
}
}
public void onCookingTimeEntered(int cookingTime) {
try {
ingredient.setCookingTime(cookingTime);
cookingTimeError.setValue(null);
} catch (InvalidCookingTimeException e) {
cookingTimeError.setValue(e.getMessage());
} finally {
updateIngredientValid();
}
}
private void updateIngredientValid() {
ingredientValidStatus.setValue(ingredient.isValid());
}
public boolean saveIngredient() {
if (ingredient.isValid()) {
Log.d(getClass().getName(), "saveIngredient: Ingredient is valid");
repository.insert(ingredient);
return true;
} else {
Log.d(getClass().getName(), "saveIngredient: Ingredient is invalid");
return false;
}
}
The onXXEntered() functions in the viewmodel are linked to the textViews in the fragment, and the saveIngredient() function is called when a save button is pressed. The XXError LiveData's are used to display errors to the user.
The real problem lies in the fact that LiveData is async, and the user can change their input and click the save button before the LiveData contains the result from the database. If I want the check the input upon saving it, the 'add activity' will have already finished before the check is done.
Again, any help would be very much appreciated.
I had to do something similar in one of my recent projects. What I did was:
Room cannot create columns with SQLite Unique constraint (if it is not the PrimaryKey - which is your case). So don't initialize the database in your app code using Room. Instead, create a database file outside your application. Add a Unique constraint on the 'name' column. Then add the database file in your project under assets folder. (for example, create a subfolder inside assets - 'db_files' - and copy your pre-created database file under this folder)
I guess you use Singleton pattern for your #DataBase class. Replace your 'getInstance()' method with following:
public static MyDB getInstance(final Context context) {
if(INSTANCE == null) {
synchronized (AVListDB.class) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
MyDB.class,"myDB.db")
.createFromAsset( "db/myDB.db")
.build();
}
}
return INSTANCE;
}
This creates a copy of your pre-packaged database file under applications database files path.
With unique constraint in place, your #Insert and #Update annotated methods will respect the constraint condition, and will throw a SQLiteConstraintException if you try to insert a previously used name. You can catch this exception, and pass it to your View or ViewModel as you wish (I implemented a simple centralized event publisher component).
I hope that helps.

myBatis: how to return pojo with insert?

I have a simple POJO:
#Data
#NoArgsConstructor
public class User {
private Long id;
private String username;
private String password;
public User(String username, String password) {
this.username = username;
this.password = password;
}
}
I have mapper, saving this POJO and returning ID.
#Insert("insert into users(username, password) values(#{user.username}, #{user.password})")
#Options(useGeneratedKeys = true, keyProperty = "id")
Long save(#Param("user") User user);
Than, service receive entity by this ID:
#Override
public User save(User user) {
return Optional.ofNullable(
mapper.findById(
Optional.ofNullable(mapper.save(user))
.orElseThrow(() -> new EntityNotSavedException("Не удалось сохранить: " + user)))
)
.orElseThrow(() -> new EntityNotSavedException("Не удалось сохранить: " + user));
}
In this case, we have one request to insert entity & second - to select it from id. Costs too much.
How to return entity after inserting in one request?
You don't need to perform the select.
It's a common misunderstanding, but #Insert method returns the number of updated rows, not the generated key (this is how the underlying JDBC API works).
As you specified keyProperty="id", the generated key is set to id property of the passed User parameter.
Note that you should specify keyColumn as well.
It's required since version 3.5.0.
If your service method has to return a User, it may look something like this.
#Override
public User save(User user) {
if (mapper.save(user) != 1) {
// this may not happen.
// an exception will be thrown if insert failed.
}
return user;
}
Usually, the service method just performs INSERT and the caller keeps using the original User instance for later operation.
The service method:
#Override
public void save(User user) {
mapper.save(user);
}
The class calling the service method would look like...
User user = new User("John", "xyz");
service.save(user);
model.addAttribute("user", user);
...

Jdbc returns empty list but SQL query succesfully gets data [Spring]

I am trying to execute this query:
#Override
public UserInfo get(Long id) {
String sql = "SELECT * FROM users WHERE id = ? ";
List<UserInfo> list = jdbcTemplate.query(sql,new UserInfoMapper(),id);
return list.get(0);
}
but jdbc return empty list and I get exception at return line.
But if try to execute directly though the console it returns:
Query, Answer
Query was executed with id 1 and retured correct anwser;
But in method its returned this
I couldn't find any same questions so that may be point at my inattention to something. But I can't see any problem that may cause this. Thanks in advance;
Updated 1
Changing code to
#Override
public UserInfo get(Long id) {
String sql = "SELECT * FROM users WHERE id = ? ";
List<UserInfo> list = jdbcTemplate.query(sql, new Object[] {id},new UserInfoMapper());
return list.get(0);
}
resulted in same: result
Updated 2
#Override
public UserInfo mapRow(ResultSet resultSet, int i) throws SQLException {
UserInfo info = new UserInfo();
info.setId(resultSet.getLong("id"));
info.setFirstname(resultSet.getString("firstname"));
info.setMiddlename(resultSet.getString("middlename"));
info.setLastname(resultSet.getString("lastname"));
info.setUsername(resultSet.getString("username"));
info.setPassword(resultSet.getString("password"));
info.setEmail(resultSet.getString("email"));
info.setMobilephone(resultSet.getString("mobilephone"));
info.setPosition(resultSet.getString("position"));
return info;
}
public class UserInfo {
private Long id;
private String firstname;
private String middlename;
private String lastname;
private String username;
private String password;
private String email;
private String mobilephone;
private String position;
public UserInfo() {
}
}
Getter and setters for each field is there but I think there is no need to show them up.
Check user credentials that you are using to connect database from your application and the user credentials in console. And also check owner schema , table owner schema in your application.

findRecord in Google CloudDatastore with Objectify

I want to use Objectify to query Google Cloud Datastore. What is an appropriate way to find a record based on a known key-value pair? The record is in the database, I verified this by Google's Datastore viewer.
Here is my method stub, which triggers the NotFoundException:
#ApiMethod(name="getUser")
public User getUser() throws NotFoundException {
String filterKey = "googleId";
String filterVal = "jochen.bauer#gmail.com";
User user = OfyService.ofy().load().type(User.class).filter(filterKey, filterVal).first().now();
if (user == null) {
throw new NotFoundException("User Record does not exist");
}
return user;
}
Here is the User class:
#Entity
public class User {
#Id
Long id;
private HealthVault healthVault;
private String googleId;
public User(String googleId){
this.googleId = googleId;
this.healthVault = new HealthVault();
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public HealthVault getHealthVault() {
return healthVault;
}
public void setHealthVault(HealthVault healthVault) {
this.healthVault = healthVault;
}
public String getGoogleId() {
return googleId;
}
public void setGoogleId(String googleId) {
this.googleId = googleId;
}
}
I think it fails because of transaction. You need to make a transctionless call like:
User user = OfyService.ofy().transactionless().load().type(User.class).filter(filterKey, filterVal).first().now();
More info about transactions on App Engine:
https://cloud.google.com/appengine/docs/java/datastore/transactions
https://github.com/objectify/objectify/wiki/Transactions
EDIT
Your object needs #Index annotation. It will add field to datastore index. Only properties that are in the index can be searchable. Filter method is one of them.
#Id
Long id;
#Index
private HealthVault healthVault;
#Index
private String googleId;
P.S. delete your object with googleId jochen.bauer#gmail.com and write it again to database after you updated your entity. And objectify will find it.
First add #Index in your fields model. I didn't see filterVal as an email in your model. Even so, to get the entity based in your filterVal assuming that is googleId is the field of your entity.
User user = OfyService.ofy().load().type(User.class).filter("googleId", filterVal).now();
And so if your filterKey is the id of your entity.
User user = OfyService.ofy().load().key(Key.create(User.class, filterKey)).now();

Returns null when calling method in another class

Very simple question but very little luck.
My problem is that when I attempt to call on get methods getUserID and getPassword of class User in class Login, I get null.
I have looked at other posts with a similar problem to mine. Although I found few posts to guide me, a common issue was that the poster had not initialised the class object but I have done that.
I need specific code here because I've tried a lot of things but can't get it to work. I hope that I provided enough info to help you assist me.
User class
public class User
{
private String userID;
private String password;
private Employee employee;
private String authorityLevel;
/**
* Constructor for User class - Initialise a fixed password and employee object.
*/
public User()
{
employee = new Employee();
password = "password";
}
/**
* Create a user ID and print the user the details of their user account.
*/
public void createUser(Employee employee)
{
// Combine staff ID with authority key to make the user ID.
userID = employee.getID() + "" + employee.getAuthorityLevel();
// Check that there is a staff ID to create the user ID.
// It also ensures that an employee profile has been created before an attempt
// to make a user account.
if(employee.getID() == null){
System.out.println("There are no Employee details to make a User with.");
System.out.println("Please enter the Employee details before you make a user");
}
else{
System.out.println("Your user ID is: "+userID);
System.out.println("Your user password is: "+password);
}
}
/**
* #return The user ID.
*/
public String getUserID()
{
return userID;
}
/**
* #return The password.
*/
public String getPassword()
{
return password;
}
}
I want to access the userID and password getter methods in the User class to use in the Login class.
Login class
public class Login
{
private User user;
private boolean accessGranted;
private String userID;
private String password;
private boolean loggedIn;
private boolean loggedOut;
/**
* Constructor for the Login class - initialise a user object.
*/
public Login()
{
user = new User();
}
/**
* Attempt to start a login session.
*/
public void login(String userID,String password)
{
// Check that credentials entered are correct for the account the user wishes to log in to.
if((password == user.getPassword()) && (userID == user.getUserID())){
accessGranted = true;
if((accessGranted == true) && (userID.contains("H"))){
System.out.println("Your login session has started.");
System.out.println("You are now viewing Yuconz System as HR staff.");
}
if((accessGranted == true) && (userID.contains("D"))){
System.out.println("Your login session has started.");
System.out.println("You are now viewing Yuconz System as Director staff.");
}
if((accessGranted == true) && (userID.contains("E"))){
System.out.println("Your login session has started.");
System.out.println("You are now viewing Yuconz System as Employee staff.");
}
loggedIn = true;
}
else{
System.out.println("ACCESS DENIED BRUTHA!");
}
}
One class creates the user account where the only details are a userID and a password. The other class is where the user can use the details of that account to login. In Login's login method I am checking that the ID and password entered match the ID and password of the account that they are trying to access.
It seems that you never call public void createUser(Employee employee) on the instance of User.
SO
private String userID; in never initialized...
Try this :
public class User{
private String userID;
private String password;
private Employee employee;
private String authorityLevel;
public User(Employee employee){
this.employee = employee;
password = "password";
createUser();
}
private void createUser(){
userID = employee.getID() + "" + employee.getAuthorityLevel();
if(userID == null){
System.out.println("There are no Employee details to make a User with.");
System.out.println("Please enter the Employee details before you make a user");
}else{
System.out.println("Your user ID is: "+userID);
System.out.println("Your user password is: "+password);
}
}
public String getUserID(){
return userID;
}
public String getPassword(){
return password;
}
}
Passing the Employee the the User's constructor is the best solution if you need an instance of Employee to creat a User.
If you do not have any instance of Employee in your login but just an id and a password then you can change the User's constructor : :
public class User{
private String userID;
private String password;
private String authorityLevel;
public User(String userID, String password){
this.userID = userID;
this.password = password;
checkUser();
}
private void checkUser(){
if(userID == null){
System.out.println("There are no Employee details to make a User with.");
System.out.println("Please enter the Employee details before you make a user");
}else{
System.out.println("Your user ID is: " + userID);
System.out.println("Your user password is: " + password);
}
}
//...
}
You can instanciate the User in the Login
public class Login
private User user;
public Login(){
}
public void login(String userID,String password){
user = new User(userID, password);
//...
}
The strings userID and password belong to the Employee object. You're not initialising those values in your User class, this is why they are null.
You could do it like this
public String getUerID() {
return employee.getUserID();
If you want them to belong to the User class the way you declared them in the class, you need to change your User constructor. e.g.
public User(String id, String pwd) {
this.userID = id;
this.password = pwd;
}
Then you need to figure out if you still want the Employee object or not.
You should reconsider some design decisions

Categories