I have a user class, users should have a list of friends. Friends are also users so every user should have many friends and obviously each of those friends(users) also have many friends(users).
So a many-to-many relationship of users is what I've assumed so far. This is currently the code I have been using (with hibernation JPA annotations)
Here is my MyUser code:
#Entity
public class MyUser {
#Id
private String username;
private String userPassword;
#ManyToMany
#JoinTable(
name="USR_USR",
joinColumns={#JoinColumn(name="USR1_ID", referencedColumnName="USERNAME")},
inverseJoinColumns={#JoinColumn(name="USR2_ID", referencedColumnName="USERNAME")})
private List<MyUser> friends = new ArrayList<MyUser>();
public void setUsername(String username) {
this.username = username;
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
public String getUsername() {
return username;
public String getUserPassword() {
return userPassword;
}
public void setFriends(List<MyUser> friends) {
this.friends = friends;
}
public List<MyUser> getFriends() {
return friends;
}
I'm trying to create a second table by joining the USERNAME column with itself (which doesn't really sound right to me I'll be honest.)
So I'm trying to create a relationship in my database where each MyUser can have many MyUser's.
I'm pretty stuck with this so some help would be appreciated.
Thanks a lot!
I would suggest changing your database schema to something like this:
MyUser(id, ....);
Friends(id1, id2);
Where MyUser.id is the primary key of MyUser, and Friends id1 and id2 are foreign keys to MyUser.id.
This is a pretty common pattern that will give you less problems in the future, and is a bit more maintainable. User A (w/ ID 1) is friends with User B (w/ ID 2) if there is a tuple in the Friends database (1, 2).
Hope that helps,
Related
I am creating a web application only using Java and not any framework.
I am at this point where I have to get data from the Database. I am doing this using DAO pattern but I have a problem to understand some logic about relationships (one-to-one, one-to-many, and many-to-many).
To understand my problem better I will explain by taking an exact example.
I have two entities (tables) in the database User and Role. The entity User has attributes id, name, lastname, username, and password, and the table Role has attributes id, role, description.
From this I have the relation that one User can have more than one Role (so a user can be both a simple user and an admin of the web app), and one Role can be in many User. From this point I creat another table tha represents many-to-many relationship named UserRoles that has attributes user_id, role_id.
Now in Java I have a class named `User:
public class User
{
private int id; (with getters and setters)
private String name; (with getters and setters)
private String lastname; (with getters and setters)
private String username; (with getters and setters)
private String password; (with getters and setters)
// and two constructors with and without parameters together with toString method
}
and the interface named UserDAO:
public interface UserDAO
{
public User find(intid);
public User find(String email, String password);
public List<User> users();
public void create(User user);
public void update(User user);
public void delete(User user);
public boolean existEmail(String email);
public void changePassword(User user);
}
I have the class for manipulation with MySQL queries named UserDAOJDBC:
public class UserDAOJDBC implements UserDAO
{
private static final String FIND_BY_ID = "SELECT * FROM user WHERE id=?";
#Override
public User find(int id) {
return find(FIND_BY_ID, id);
}
private User find(String sql, Object... values){
User user = null;
try {
ResultSet resultSet = DBConnectionPool.executeQuery(sql, values);
if(resultSet.next()){
user = new User();
user.setId(Integer.parseInt(resultSet.getString("id")));
user.setName(resultSet.getString("name"));
user.setLastname(resultSet.getString("lastname"));
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
}
DBConnectionPool.getConnection().close();
} catch (Exception e) {
System.out.println(e);
}
return user;
}
}
Now when I want to get Role for one User what is a better practice?
So far I have this solution:
1) I have to include Role in the User class:
public class User
{
private int id; (with getters and setters)
private String name; (with getters and setters)
private String lastname; (with getters and setters)
private String username; (with getters and setters)
private String password; (with getters and setters)
private List<Role> roles; (with getters and setters)
// and two constructors with and without parameters together with toString method
}
and create in class UserDAOJDBC a method named findWithRoles that makes the join with the tables as:
SELECT * FROM user AS u INNER JOIN userroles as ur ON u.id = ur.user_id
and then the second query that goes through results of the previous one:
SELECT * FROM role AS r INNER JOIN userroles as ur ON r.id = ur.role_id
and from the ResultsSet of this query to populate the array List<Role> roles.
You don't need to include Role into the User class (even though you can). It is enough to create the UserRoles table and read the data from it. You should probably create UserRolesDAO class that will implement all the methods for you to find all the roles of a specific user, and all the users to specific role, as well as save new user, role combination and read it back.
I have an example on github where I combined Students and Courses in exactly same way using JDBC and DAO pattern so feel free to take a look.
Blog post about dao
I have a question. I edited my Room entity from this:
#Entity(tableName = "users")
public class User {
public User(String username, String email, String password){
this.username = username;
this.email = email;
this.password = password;
}
public User() {}
#PrimaryKey
#ColumnInfo(name = "Username")
private String username;
#ColumnInfo(name = "Email")
private String email;
#ColumnInfo(name = "Password")
private String password;
//getters setters
To this form:
#Entity(tableName = "users")
public class User {
public User(String username, String email, String password){
this.username = username;
this.email = email;
this.password = password;
}
public User() {}
//changes here, move userId to int instead of username
#NonNull
#PrimaryKey(autoGenerate = true)
private int id;
#NonNull
#ColumnInfo(name = "Username")
private String username;
#ColumnInfo(name = "Email")
private String email;
#ColumnInfo(name = "Password")
private String password;
//getters setters.
And this is how looks my RoomDatabase class:
#Database(entities = {User.class}, version = 1)
public abstract class ApplicationDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
My question is when I edit an entity if the database in my android device change? What if I had data in my database? If I need to drop the database on my device first? What about my constructor for user, it will work now?
Thanks for any advice
When you edit your entities you need to change the version of your database, in that case, change the annotation to #Database(entities = {User.class}, version = 2)
It is up to you to decide whether you want to drop old data of your database or you want to migrate the old version to the newer one (I would recommend it if you have a user base and you don't want them to lose their saved data in the database otherwise I would recommend you to drop the information in the old database because it is the easier solution).
In case you want to read further about migrating your room database I have added a link:
https://developer.android.com/training/data-storage/room/migrating-db-versions
As mentioned by you, you don't have any issue in deleting and recreating your database. Then in that case, just add fallbackToDestructiveMigration() to your Room db builder like:
Room.databaseBuilder(getApplicationContext(), MyDb.class, "database-name")
.fallbackToDestructiveMigration() //recreate DB in case of conflict due to cache/backup
.build();
This will recreate all the tables when you bump your database version, losing all your previous data.
In case you want to update tables while preserving older data in database, you will need to use MIGRATIONS like:
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
#Override
public void migrate(SupportSQLiteDatabase database) {
database.execSQL("CREATE TABLE `Fruit` (`id` INTEGER, "
+ "`name` TEXT, PRIMARY KEY(`id`))");
}
};
Room.databaseBuilder(getApplicationContext(), MyDb.class, "database-name")
.addMigrations(MIGRATION_1_2).build();
I'm strugling with JPA. I tried several things but I can't figure out the right way to put the annotations.
What is want is like an Order/OrderLine relationship.
Thus:
Order( PK=orderId, fields=[...])
OrderLine (Pk1=orderId,Pk2=orderLineId, fields=[...])
Obviously, OrderLine.orderId refers to the 'Order' table.
What I functionally want to do is at least:
retrieve the Order with and without all orderlines. It should have a Set
retrieve an orderline by full PK, but without the associated Order.
retrieve a list of orderlines by orderId.
I only want these 2 tables and classes. nothing more nothing less.
I tried several things. Can anybody help me out with putting in the right annotations and members on these two classes?
Edit: what i've done so far.
Note that in this real example User=Order and UserRun=OrderLine. So, i am not interested in a seperate 'Run'-entity. Merely a UserRun as described by the Orderline.
#Entity
#Table(name = "user_runs")
public class UserRun {
#EmbeddedId
private UserRunKey id;
public UserRun(){};
public UserRun(String userName, String runUuid) {
this.id = new UserRunKey(userName, runUuid);
}
public String getUserName() {
return this.id.getUserName();
}
public String getRunUuid() {
return this.id.getRunUuid();
}
}
#Embeddable
class UserRunKey implements Serializable {
#Column(name = "username")
private String userName;
#Column(name = "run_uuid")
private String runUuid;
public UserRunKey(){};
public UserRunKey(String userName, String runUuid) {
this.runUuid = runUuid;
this.userName = userName;
}
public String getUserName() {
return userName;
}
public String getRunUuid() {
return runUuid;
}
}
This created a userruns/orderline table with the PK in the wrong way:
create table user_runs (run_uuid varchar(255) not null, username varchar(255) not null, primary key (run_uuid, username))
I want the primary key in reverse.
I want username as FK to User
I want a Set in my User-class.
When I do the following in my User-class:
#OneToMany
private Set<UserRun> userRuns;
It will create a
create table user_user_runs (user_username varchar(255) not null, user_runs_run_uuid varchar(255) not null, user_runs_username varchar(255) not null, primary key (user_username, user_runs_run_uuid, user_runs_username))
And that's something I definitely don't want! Once again, I don't want a Run-object (same as nobody's interested in a Line-class, from OrderLine)
I think I figured it out.
The UserRun/Orderline class:
#Entity
#Table(name = "user_runs")
public class UserRun {
#EmbeddedId
private UserRunKey id;
public UserRun(){};
public UserRun(String userName, String runUuid) {
this.id = new UserRunKey(userName, runUuid);
}
public String getUserName() {
return this.id.getUserName();
}
public String getRunUuid() {
return this.id.getRunUuid();
}
}
#Embeddable
class UserRunKey implements Serializable {
#Column(name = "username")
private String userName;
#Column(name = "run_uuid")
private String zrunUuid; //starts with a z, so the PK will be pk(username,run_uuid). Apparently, order in PK is determined from the variable names (alphabetic order)....
public UserRunKey(){};
public UserRunKey(String userName, String zrunUuid) {
this.zrunUuid = zrunUuid;
this.userName = userName;
}
public String getUserName() {
return userName;
}
public String getRunUuid() {
return zrunUuid;
}
}
In the userclass:
#OneToMany(mappedBy = "id.userName", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<UserRun> userRuns;
Unfortunately, there are 2 downsides:
I see that there are 2 queries executed instead of a Join on username. One to retrieve user, and 1 to retrieve the Set...
I needed to alter variablenames of the PK (compound/Embeddable). It seems there is no clean way to define the PK order. (Seriously?). Fortunately, the variable name is private, and not exposed by getter.
If anybody knows a cleaner way for these 2 issues. Let me know!
I think what you have to do is the following:
Because the primary key is compound key you need an ID class, as you already did:
#Embeddable
class OrderLinePK implements Serializable {
// you can use physical mapping annotations such as #Column here
#Column(name="...")
private Integer orderLineID;
// This is foreign key and the physical mapping should be done
// on the entity, and not here
private Integer orderID;
public OrderLinePK(){}
// getters + setters
// orverride equals() and hashCode() methods
}
Implement OrderLine entity
#Entity
public class OrderLine {
#EmbededId private OrderLinePK id;
#Mapsid("orderID")
#ManyToOne
#JoinColumn(name = "ORDER_ID", referencedColumn="ID")
private Order order;
// getters + setters ....
}
And the Order entity:
#Entity
public class Order {
#Id
private Integer id;
#OneToMany(fetch = FetchType.LAZY) // actually default by 1-to-n
private Coolection<OrderLine> orderLines;
// getters + setters ....
}
Requirement is to add username column for every audit entry.
After googling a lot and going through the enverse docs I figured out how to implement it. I have implemented it as follows:
Implemented a revision listener:
#Configurable
public class UserRevisionListener implements RevisionListener
{
public void newRevision(Object revisionEntity)
{
UserRevEntity revision = (UserRevEntity) revisionEntity;
String username = "";
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Object principal = auth.getPrincipal();
if (principal != null && principal instanceof DepotUser)
username = ((DepotUser) principal).getUsername();
revision.setUsername(username);
}
}
Create an entity class:
#Entity
#Table(name = "USER_REV_ENTITY")
#RevisionEntity(UserRevisionListener.class)
public class UserRevEntity extends DefaultRevisionEntity
{
private static final long serialVersionUID = 1L;
private String username;
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
}
I have referred a link that has an example of the same "Thinking in Software ".
Default auditing is working fine.
Firstly, the table for the audit revision with the new username column should get auto generated (I think!). Its not getting generated.
So, I tried by creating the table and adding the table mapping in UserRevEntity. But no luck.
Can anyone help my identifying the issue?
Thanks, in advance.
#MappedSuperclass
public class DefaultRevisionEntity implements Serializable {
#Id
#GeneratedValue
#RevisionNumber
private int id;
#RevisionTimestamp
private long timestamp;
// ... rest of the class body here....
This is DefaultRevisionEntity, which has already declared with id & timestamp.
For your new column username, it has to be annotated as #Column in UserRevEntity as given below.
#Column(name="username")
private String username;
I'm using play framework with ebean.
I have 2 classes that share a many-to-many relationship
#Entity
public class profiles extends Model {
#Id
#Column(name = "profile_ID")
public Integer _id;
public String profile;
public String description;
#ManyToMany(cascade = CascadeType.ALL)
public List<role> roles;
......
#Entity
public class role extends Model {
#Id
#Column(name = "role_ID")
public Integer _id;
public String role;
public Integer parent;
public String description;
public Integer sequence;
#ManyToMany(cascade = CascadeType.ALL)
public ArrayList<profiles> prof_ids = new ArrayList<profiles>();
.....
I'm having trouble trying to generate a list containing all the roles that a particular profile has.
could anyone show me how this is done?
You need to provide more information on what specifically you're trying to do.
See below for an action that creates a profile with 2 roles, persists the profile (and roles), finds all profiles, logs the found profiles and their associated roles and then renders them as JSON:
public class Profiles extends Controller {
public static Result create() {
profiles profile = new profiles();
profile.description = "Testing";
profile.profile = "Test Profile";
role role = new models.role();
role.description = "Test Role 1";
role.role = "Role 1";
profile.roles.add(role);
role = new models.role();
role.description = "Test Role 2";
role.role = "Role 2";
profile.roles.add(role);
profile.save();
List<profiles> profiles = Ebean.find(profiles.class).findList();
for (profiles p : profiles) {
Logger.info("Profile: {}", p.profile);
for (role r : p.roles) {
Logger.info("\t-> has role: {}", r.role);
}
}
return ok(Json.toJson(profiles)).as("application/json");
}
}
To get the list of roles, note the reference to p.roles above. Is that what you want?
See here for more advanced queries etc.
Some things to keep in mind:
Always start your class names with a capital letter in Java. The code above is hard to read using your lowercase model/entity names...
Name your entity identifier id not _id, it causes issues when Ebean generates queries.