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();
Related
I have developed two tables in Spring Boot, User and UserMeta. User is the parent and UserMeta is the child table. The foreign-key is user_id. I may be looking at it the wrong way, but I want to be able to first create an entity of User. Then, I want to create an entity of UserMeta. Simply UserMeta should contain additional data to User.
However, when first creating a User and then a UserMeta entity, I get e new User entity (ending up with two User entities and one UserMeta entity.)
The problem I think is that I create a UserMeta object with a User, since I want to have a relationship between User and UserMeta. But if I want to be able to first create a User and then a UserMeta, should I simply ignore a foreign-key? Or, does it exists another way of creating a UserMeta entity without creating a new User?
User
public class User {
#Id
#SequenceGenerator(name = "user_sequence", sequenceName = "user_sequence", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_sequence")
//#OneToOne(optional=false)
private Long userId;
private String username;
private String password;
private String email;
#OneToOne(mappedBy = "user")
private UserMeta userMeta;
public User(String username, String email, String password) {
this.username = username;
this.email = email;
this.password = password;
}
}
UserMeta
public class UserMeta {
#Id
#SequenceGenerator(name = "user_meta_sequence", sequenceName = "user_meta_sequence", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_meta_sequence")
private Long userMeta_Id;
private String lastname;
private int age;
#OneToOne(
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
optional = false
)
#JoinColumn(
name = "user_Id",
referencedColumnName="userId"
)
private User user;
public UserMeta(String lastName, int age, User user){
this.lastname = lastName;
this.age = age;
this.user = user;
}
}
UserRepository
public interface UserRepository extends CrudRepository<User, Long> {
}
UserService
public interface UserService {
User saveUser(User user);
}
UserServiceImpl
#Service
public class UserServiceImpl implements UserService {
private UserRepository userRepository;
public UserServiceImpl(UserRepository userRepository) {
super();
this.userRepository = userRepository;
}
#Override
public User saveUser(User user) {
// TODO Auto-generated method stub
return this.userRepository.save(user);
}
UserController
#RestController
public class UserController {
private UserService userService;
public UserController(UserService userService) {
super();
this.userService = userService;
}
#PostMapping("/user")
public ResponseEntity<User> saveUser(#RequestBody User user) {
return new ResponseEntity<User>(userService.saveUser(user), HttpStatus.CREATED);
}
}
UserMetaRepository
public interface UserMetaRepository extends CrudRepository<UserMeta, Long> {
}
UserMetaService
public interface UserMetaService {
UserMeta saveUserMeta(UserMeta userMeta);
}
UserMetaServiceImpl
#Service
public class UserMetaServiceImpl implements UserMetaService{
private UserMetaRepository userMetaRepo;
public UserMetaServiceImpl(UserMetaRepository userMetaRepo) {
super();
this.userMetaRepo = userMetaRepo;
}
#Override
public UserMeta saveUserMeta(UserMeta userMeta) {
return this.userMetaRepo.save(userMeta);
}
}
UserMetaController
#RestController
public class UserMetaController {
public UserMetaService userMetaService;
public UserMetaController(UserMetaService service) {
super();
this.userMetaService = service;
}
#PostMapping("/userMeta")
public ResponseEntity<UserMeta> saveUserMeta(#RequestBody UserMeta userMeta) {
return new ResponseEntity<UserMeta>(this.userMetaService.saveUserMeta(userMeta), HttpStatus.CREATED);
}
}
you should use this constructor in the User class,
public User(String username, String email, String password, UserMeta userMeta) {
this.username = username;
this.email = email;
this.password = password;
this.userMeta = userMeta;
}
now when you save your user the user Meta will be added to your UserMeta table,
If you want to add a user Meta to an existing user you will only need to set the userMeta and save it with a simple userRepository.save(theUpdatedUser)
you can also create userMeta seperately with your code above, and if you want to assign it to a user already in data base or not you can allows use the power of spring data and use simple userRepository.save(userWithTheAssignedMeta)
the same logic applies the other way for metaUser.
The problem here is that your UserMetadata creation logic is using incomplete JSON:
{ "lastName":"foo", "age":1, "user":{ "username":"foo", "password":"bar", "email":"foo-bar" } }
Within this, the problem is the 'user' has all the data, duplicating what was already created the the database, but does not identify it. Since the mapping has cascade.ALL set on it, Spring/JPA will persist the UserMetadata and find this User instance that doesn't have identity, so persist it - giving it identity from the sequence.
There are a few ways you might correct this. First and easiest is to send the User ID in the json from the previously created instance:
{ "lastName":"foo", "age":1, "user":{ "userId":1, "username":"foo", "password":"bar", "email":"foo-bar" } }
This will allow Spring/JPA to recognize the user's identity and merge it and the data provided into the database. It means though that you must send complete data for the User - it will push incomplete data into the DB.
If that is a concern, you can change the cascade options. You may not want cascading persist/merge at all on this relationship, and I suspect when you delete userMetadata you don't really want to delete the User instance, so I think this might have been done incorrectly (maybe put it on the user->UserMetadata relationship instead?). If you remove the cascade settings, spring/JPA will let you just pass in JSON with the USER id specified, as this gives it enough to set the fk:
{ "lastName":"foo", "age":1, "user":{ "userId":1} }
What I have setup are two tables, one for a user created account, and the other that lets the user buy a product.
I have both tables set up like so
Customer Table
#PrimaryKey(autoGenerate = true)
private int custId;
#ColumnInfo(name = "user_name")
private String userName;
#ColumnInfo(name = "password")
private String password;
#ColumnInfo(name = "first_name")
private String firstName;
#ColumnInfo(name = "last_name")
private String lastName;
#ColumnInfo(name = "address")
private String address;
#ColumnInfo(name = "city")
private String city;
#ColumnInfo(name = "postal_code")
private String postalCode;
#ColumnInfo(name = "country")
private String country;
Phone Table
#PrimaryKey(autoGenerate = true)
private int productId;
private String phoneMake;
private String phoneModel;
private String phoneColor;
private String storageCapacity;
private Float price;
What I have set up are two foreign keys, one in each table. My last table is for ordering the phones, which requires using both Primary Keys from each table. What I feel like I need is a ForeignKey, similar in vein to the PrimaryKey already created. The problem is that I am unsure how to implement that into the program. Everything I try doing is not working. I have looked at the documentation, but nothing clicks. I hope you can help me with the correct screenshot. If more is needed let me know (This code is written in Java code)
If you simply want a Customer to have 1 phone, then you have have a single column (member variable) for the relationship that will store the phone's product id.
e.g.
private int mapToPhone; //<<<<< ADDED no need for #ColumnInfo the column name will be as per the variable name.
Obviously you set the value to an appropriate value.
To then get the Customer with the phone's details then you have a POJO that embeds the parent (Customer) using the #Embedded annotation has the child (Phone) using the #Relation annotation.
e.g. :-
class CustomerWithPhoneDetails {
#Embedded
Customer customer;
#Relation(
entity = Phone.class,
parentColumn = "mapToPhone",
entityColumn = "productId"
)
Phone phoneDetails;
}
You can then have a method in the #Dao annotated interface/abstract class which queries the parent table BUT returns the POJO or list/array of the POJO e.g. :-
#Query("SELECT * FROM Customer")
abstract List<CustomerWithPhoneDetails> getAllCustomersWithPhoneDeytails();
Example
Based upon your code, and the additional example code along with an #Database annotated abstract class :-
#Database(entities = {Customer.class,Phone.class}, version = 1, exportSchema = false)
abstract class TheDatabase extends RoomDatabase {
abstract AllDao getAllDao();
private static volatile TheDatabase instance = null;
public static TheDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(context,TheDatabase.class,"the_database.db")
.allowMainThreadQueries()
.build();
}
return instance;
}
}
and an Activity e.g. :-
public class MainActivity extends AppCompatActivity {
TheDatabase db;
AllDao dao;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = TheDatabase.getInstance(this);
dao = db.getAllDao();
long phone01ProductId = dao.insert(new Phone("PhoneMaker001","Model001","Color001","100Mb",111.11F));
long phone02ProductId = dao.insert(new Phone("PhoneMaker002","Model002","Color002","200Mb",222.22F));
dao.insert(new Customer("c001","password001","firstname001","lastname001","address001","city001","country001","postcode001",(int) phone01ProductId));
dao.insert(new Customer("c002","password002","firstname002","lastname002","address002","city002","country002","postcode002",(int) phone02ProductId));
for(CustomerWithPhoneDetails cwpd: dao.getAllCustomersWithPhoneDeytails()) {
Log.d("DBINFO","Customer is " + cwpd.customer.getUserName() + " etc. Phone is " + cwpd.phoneDetails.getProductId() + " etc." );
}
}
}
Note that suitable constructors have been coded in both the Phone and Customer class (default/empty constructor and one, annotated with #Ignore annotation that allows all values bar the id to be passed as used in the example below)
Note that ideally long rather than int should be used for the id columns.
Results
The Log :-
D/DBINFO: Customer is c001 etc. Phone is 1 etc.
D/DBINFO: Customer is c002 etc. Phone is 2 etc.
App Inspection :-
and :-
I am creating a spring boot API which basically asks the user to create an account.
The account details are showed on a form.
I want to fetch the details from the form and save that to the database(MYSQL).
The model class is as follows:
#Table(name = "user")
public class User {
#Id
#Column(name = "ID")
private int ID;
#Column(name = "Fname")
private String fName;
#Column(name = "Lname")
private String lName;
#Column(name = "dob")
private String dob;
#Column(name = "email")
private String email;
#Column(name = "pWord")
private String pWord;
}
The controller class is as follows:
public class MController {
#Autowired
private UserRepository userRepository;
#PostMapping("/successSignUp")
public String dataToDB(#ModelAttribute("User") User formData, Model model) {
userRepository.save(new User(formData.getFname(), formData.getLname(), formData.getDob(), formData.getEmail(), formData.getPassword()));
model.addAttribute("user", new User());
return "welcomeUser";
}
When i am executing this code, i am getting the following error:
java.sql.SQLSyntaxErrorException: Unknown column 'p_word' in 'field list'
What am I doing wrong here?
Thanks in advance.
Spring framework changes the camel case to snake case internally.
This is part of Spring Boot Naming Strategies:
We can override these values, but by default, these will:
Change camel case to snake case
Replace dots with underscores
Lower-case table names
Can you try to update column name as pword instead of pWord ?
#Column(name = "pword")
private String pWord;
it will be considered as p_word if you use 'pWord'. please update column name as 'pword' and try.
example:
#Entity
public class Account {
#Id
private Long id;
private String defaultEmail;
}
And then turn on some SQL debugging in our properties file:
hibernate.show_sql: true
At startup, we'll see the following create statement in our logs:
Hibernate: create table account (id bigint not null, default_email varchar(255))
The easiest solution to fix this is to put the column names in lowercase or uppercase.
#Column(name = "pword")
private String pWord;
or
#Column(name = "PWORD")
private String pWord;
This will avoid that spring convert the name into snakecase.
Name of the columns in MySql are not case sensitive, so it will work.
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 have just one week experience in GAE/Java and trying to port an legacy application(developed using PHP/MySQL) to GAE+JDO. I'm now stuck with a basic issue in creating a relationship between two tables(kinds in GAE).
So here is the case:
We have a Users table which holds the user authentication information. It also has a field user_role which store role_id, which is actually a foreign key of another table user_roles.
From the the Entity-Relationship documentation in GAE, I understand that DataStore doesn't support foreign-key relationships and designed the Users class by adapting the Employee-ContactInfo example in the docs.
When I executed the application, the user_roles kind is inserted each time I add an entry in Users table. The user_roles kind is supposed to have only three static values. But this is having redundant values as I input more records in Users.
I think that I'm missing something very trivial, but I couldn't figure it out due to my inexperience to datastore. It would be very nice if someone could guide me to solve this issue.
Here is the code:
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Users {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
private String userName;
#Persistent
private String password;
#Persistent
private String salt;
#Persistent
private Date createdDate;
#Persistent
private Key createdBy;
#Persistent
private Date lastLogin;
#Persistent
private boolean status;
#Persistent
private String authKey;
#Persistent(defaultFetchGroup="true")
private SecurityRole securityRole;
#Autowired
SecurityRepository securityRepository ;
public SecurityPrincipals(String userName, String password,SecurityRole securityRole,boolean status) {
this.securityRole = securityRole;
this.userName = userName;
this.password = password;
this.status = status;
}
//getters and setters
}
Definition for Roles:
#PersistenceCapable(detachable="true")
public class SecurityRole {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
private String securityRoleName;
#Persistent
private String securityRoleDescription;
#Persistent
private String securityRoleStatus;
#Persistent
private Date securityRoleCreatedDate;
public SecurityRole(String securityRoleName, String securityRoleDescription, String securityRoleStatus,String securityBaseType)
{
this.securityRoleName = securityRoleName;
this.securityRoleDescription = securityRoleDescription;
this.securityRoleStatus = securityRoleStatus;
this.securityBaseType = securityBaseType;
}
// getters and setters
}
The relevant code from Controller:
SecurityRole securityRole = securityRepository.getSecurityRole( securityRoleName);
users = new Users(userName,password,status,securityRole);
iUserRepository.save(employeeDetails);
Here is the definition of getSecurityRole:
public SecurityRole getSecurityRole(String securityRoleName)
{
PersistenceManagerFactory pmf = this.jdoTemplate.getPersistenceManagerFactory();
PersistenceManager pm = pmf.getPersistenceManager();
try {
Query query = pm.newQuery( SecurityRole.class);
query.declareImports("import java.lang.String");
query.declareParameters("String securityRoleName");
query.setFilter("this.securityRoleName == securityRoleName");
List<SecurityRole> securityRoles = (List<SecurityRole>)query.execute(new String(securityRoleName));
SecurityRole temp = null;
for(SecurityRole securityRole: securityRoles)
{
temp = securityRole;
}
return temp;
}
finally {
pm.close();
}
}
Here is the definition of iUserRepository.save():
public void save(Users user) {
jdoTemplate.makePersistent(companyDetails);
}
In the Users class, you have the defined the property
#Persistent(defaultFetchGroup="true")
private SecurityRole securityRole;
This statement creates an "owned" relationship in GAE datastore, which means that when you create an object of the Users class, an object of the SecurityRole class will be created as well.
What you need, is an unowned relationship that can be created as follows:
#Persistent(defaultFetchGroup="true")
private Key securityRole;
In this way, a SecurityRole object is not created each time you create an instance of the Users class. For more information about owned and unowned relationships, take a look at http://code.google.com/appengine/docs/java/datastore/jdo/relationships.html
Hope this helps!