adding static property data when creating entities - java

Suppose I have two entity types: User and Country.
Country entities are never created and aren't mutable. They're keyed by ISO alpha3 country codes, e.g. "USA". They reside in the table COUNTRY with PK column ID.
Users have a many-to-one mapping to Country w/ cascade = none. Users reside in the USER table with a FK column COUNTRY_ID that references COUNTRY(ID).
When creating a new User I might do something like:
User user = new User();
user.setCountry(em.find(Country.class, "USA"));
em.persist(user);
I gather that's wasteful, though, since it requires a query against COUNTRY first. So I noticed I can also do this:
Country usa = new Country();
usa.setId("USA");
User user = new User();
user.setCountry(usa);
em.persist(user);
Since cascade = "none" this shouldn't need to query the COUNTRY table; it should just insert "USA" directly into USER as COUNTRY_ID. Is that correct?
Now suppose there's some code that only ever creates Users with COUNTRY_ID = "USA". In that case, it occurred to me to store a static instance of Country with ID = "USA" and use it for every new user? For example:
public class UsaUserFactory implements Factory<User> {
private static final Country USA = new Country();
static { USA.setId("USA"); }
public User newInstance() {
User user = new User();
user.setCountry(USA);
return user;
}
}
public SomeOtherClass {
public void persistUser(EntityManager em, Factory<User> uf, ...) {
User user = uf.newInstance();
// set some other properties
em.persist(user);
}
}
Assume persistUser() is called concurrently from multiple threads and from within multiple persistence contexts.
My questions:
Will persisting User entities mutate my singleton "USA" Country instance in any way?
Is this inadvisable for some other reasons entirely?
The two classes above are just to illustrate the question; I'm not actually doing anything quite that silly.

I would be more inclined to try using a cache for the read-only data to reduce the actual database calls. See here for help setting up a cache that might help.

Related

Updating entity with only non-null attributes of passed in object

I have an entity type with several fields. Some of them are references to other entities. I want to create a Rest API endpoint in Spring that lets users to update entities of this type.
Assume that I have an entity User that contains a list of friends. I only want to let users update some specific fields, such as name, age and description, of a user entity, and not list of friends.
Besides, I only want those attribute of the entity to be updated that the corresponding passed in values are not null.
public class UserController {
#RequestMapping(path="",method=RequestMethod.PUT)
public void update(#RequestBody User user) {
userService.save(user);
}
}
How can I make this possible ?
Instead of taking User object as a parameter in your update() method you can take a DTO class that only defines the properties you need to change.
Define a class UpdateUserDTO like so
public class UpdateUserDTO {
private String name;
private String description;
//other fields you want the clients to change.
...
}
Now this UpdateUserDTO can be used as a data transfer object in your update method like so.
public class UserController {
#RequestMapping(path="",method=RequestMethod.PUT)
public void update(#RequestBody UpdateUserDTO dto) {
//validate your dto properties and then update your user entity.
userService.save(user);
}
}
Session session = sessionFactory.getCurrentSession();
String newuserId=newuser.getUserId();
session.clear();
User user=userService.readUser(newuserId);
Property1 prop1=user.getProperty1();
List<Property2> prop2=user.getProperty2();
session.clear();
newuser.setProp1(prop1);
newuser.setProp2(prop2);
newuser.save(); //You should go through services and abstraction layers before saving aka it should be in the DAO implementation layer.
Your question needs improvement but try this answer.
You basically save the missing properties in some variables and you append them to your front end User object.
Based on the HQL output it seems like this way gets the user and adds the other properties to it.

foreign keys in objectify / google app engine

I'm trying to write an application for the google app engine using Objectify and am having some trouble. I'm new to noSQL datastores so it's probably a conceptual problem.
I have an entity called a message, and each message has a from User - the user who created the message.
#Entity
public class Message {
#Index private Key<User> fromUserKey;
#IgnoreSave private User fromUser;
Annoyingly, I have to have both a User and a Key field in the message. JSON needs the User object to populate the response with useful fields, and the google app engine needs the Key to be able to store the reference to the user. The #IgnoreSave annotation is used to stop Objectify asking the google app engine to try to store the User object (which will fail).
When fetching messages, the from user key is populated, but the from User object is not. Here's what the DAO code looks like for my "getMessages" operation:
public static List<Message> getSentMessages(long userId) {
List<Message> result;
result= ofy().load().type(Message.class).filter("from", Key.create(User.class, userId)).limit(1000).list();
return result;
}
But the fromUser object is not populated on each Message, only the fromUserKey. How do I get the actual User object populated in the response? I need to access such fields as forename, surname, etc - not just the ID of the User. I could get the from User from the DB, loop through the results, then call setFromUSer on each message, but it's ugly. And it also causes a ConcurrentModificationException...
I'm sure I'm doing something wrong here, but I can't work out what.
What you can do is have a property Ref<User> on the Message entity and annotate it with '#Parent'. This means that each Message entity will become part of the user's entity group.
The Ref<?> works like a key but allows you to directly access the actual entity object; that way you can easily get the forename, surname etc.
Change your class as follows:
#Entity
#Cache
public class Message
{
#Id Long id;
#Load #Parent private Ref<User> user;
public User getUser() { return this.user.get(); }
public void setUser(User value) { this.user = Ref.Create(value); }
}
Having done that, you will be able to perform ancestor queries to retrieve all the message entities associated with a particular user:
public static List<Message> getSentMessages(long userId)
{
User parent = ofy().load().type(User.class).id(userId).now();
List<Message> results = ofy().load().type(Message.class).ancestor(parent).limit(1000).list();
return results;
}
You should now be able to do what you wanted, and hopefully not get any more errors.

Mapping ResultSet to Pojo Objects

Well that's really embarrassing I have made a standard pojo class and its dao class for data retrieval purpose. I am having a difficulty to understand a basic procedure to how to handle a customized query data to Pojo class.
let's say my User class is
public class User{
private int userId;
private String username;
private int addressId;
}
public class Address{
private int addressId;
private String zip;
}
public class UserDAO{
public void getUserDetails(){
String getSql = select u.userId, u.username, a.zipcode from user u, address a where u.addressId = a.addressId;
//no pojo class is now specific to the resultset returned. so we can't map result to pojo object
}
}
now how I should model this with my pojo class as if using String to manage this then concept of object oriented vanishes, also complexity would increase in the future as well. kindly guide!
Update for Further Explanation
We know that we can map same table objects with same pojo class, but when the query is customized and there is a data returned which doesn't map to any specific class then what would be the procedure? i.e. should we make another class? or should we throw that data in a String variable? kindly give some example as well.
For this purpose you can use one of implementation of JPA. But as you want to do it manually I will give you small example.
UPD:
public class User {
private int userId;
private String username;
private Address address; // USE POJO not ID
}
public class Address{
private int addressId;
private String zip;
List<User> users;
}
public User getUserById(Connection con, long userId) {
PreparedStatement stmt;
String query = "select u.user_id, u.user_name, a.id, a.zip from user u, address a where a.address_id = u.id and u.id = ?";
User user = new User();
Address address = new Address;
try {
stmt = con.prepareStatement(query);
stmt.setLong(1, userId);
ResultSet rs = stmt.executeQuery();
address.setId(rs.getInt("id"));
address.setZip(rs.getString("zip");
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("user_name"));
user.setAddressId(rs.getInt("address_id"));
user.setAddress(address); // look here
} catch (SQLException e) {
if (con != null) {
try {
System.err.print("Transaction is being rolled back");
con.rollback();
} catch (SQLException excep) {
}
}
} finally {
if (stmt != null) {
stmt.close();
}
}
return user;
}
You shouldn't do new POJO for that query, you should write normal query. And remember - your object model is main, tables in DB is just a way to save data of your application.
We know that we can map same table objects with same pojo class, but when the query is customized and there is a data returned which doesn't map to any specific class then what would be the procedure? i.e. should we make another class?
JPA dynamic instantiation allows you to define a query with a POJO whose constructor specifies only the fields and types you want from the database.
This will perform a JPA selection which will return a List.
If you need to change the query later and the columns are unchanged, your POJO will still work.
If you change the columns, then also change the POJO accordingly.
NOTE:
You must specify fully qualified package and constructor arguments.
Type User must be a JPA-mapped or JPA-annotated entity class.
The entityManager is in JPA EntityManagerFactory.
TypedQuery<User> q;
String sql = "select new com.stuff.User(
int u.userId, String u.username, String a.zipcode)
from User u, Address a where u.addressId = a.addressId";
List<User> list = entityManager.createQuery(sql).getResultList();
for(User u : list) {
doStuff(u);
}
Dynamic instantiation is also handy when you want to select specified columns, but avoid those columns with large data, such as BLOB types.
For example, maybe you want a list of proxy POJO's which represent the fully populated thing, but are themselves not fully populated.
You present the proxy list, and when the user selects one, then you do another query to get the fully populated object.
Your mileage may vary.
There's many ORM frameworks that can do this including Hibernate, myBatis, JPA and spring-JDBC
spring-jdbc and myBatis give you granular control over the SQL whereas with JPA and Hibernate you are usually abstracted away from the SQL.
I suggest you do some reading and figure out which one you like before rolling your own solution.
Your question:
We know that we can map same table objects with same pojo class,
but when the query is customized and there is a data returned
which doesn't map to any specific class then what would be the procedure?
If you have 100 kinds of SQL which returns different combination of columns, could it be to create 100 different POJOs? The answer is "NO, stop using POJO".
This library qood is designed to solve this problem, you can try it.

JPA 2 - OneToOne cross reference

I have been struggling with this issue for a while now, and can't seem to find a solution anywhere. Maybe I have misunderstood something, but would really appreciate it if someone could clear up things for me.
I am trying to create a OneToOne bidirectional relationsship between a User.class and a FacebookProfile.class.
That is I want to be able to look up the corresponding User entity from the FacebookProfile entity, and vice versa:
userInstance.getFbprofile();
fbprofileInstance.getUser();
My User.class:
#Entity
#Access(value=AccessType.FIELD)
public class User implements UserDetails {
#OneToOne(mappedBy="user")
private FacebookProfile fbprofile;
}
My FacebookProfile.class:
#Entity
#Access(value=AccessType.FIELD)
public class FacebookProfile {
#OneToOne (cascade=CascadeType.ALL)
#JoinColumn(name="USER")
private User user;
}
The new FacebookProfile and User instances are created in a Controller, and then sent through a Service layer to a DAO class which persists the objects.
I first create and persist a new User object.
I then create a new FacebookProfile and puts the newly created User in it (setUser). I then persist the FacebookProfile object.
The User ID is now stored in the FacebookProfile database table, but there is no reference from the User to the FacebookProfile so the following code returns a NullPointerException:
User tempUser = userService.findUserById(newUser.getId());
System.out.println("ID "+tempUser.getFbprofile().getId());
Below is the code from the Controller if something is unclear:
//Check if the Facebook user already exist
FacebookProfile fbprofile = facebookProfileService.findFacebookProfileById(fbId);
User newUser;
//Create a new FacebookProfile if it doesn't exist
if(fbprofile == null){
//Check if there is a user registered with the facebook email
newUser = userService.findUserByEmail(fbEmail);
// No User and No FacebookProfile exists
if(!newUser.isEnabled()){
newUser = new User();
newUser.setFirstname(fbFirstname);
newUser.setLastname(fbLastname);
//Set email
Email mail = new Email();
mail.setAddress(fbEmail);
mail.setName("Facebook");
newUser.addEmail(mail);
//Set gender
if(fbGender.equalsIgnoreCase("female")){
newUser.setGender(Gender.FEMALE);
}else if(fbGender.equalsIgnoreCase("male")) {
newUser.setGender(Gender.MALE);
}
userService.createUser(newUser);
}
FacebookProfile newProfile = new FacebookProfile();
newProfile.setId(Long.parseLong(fbId));
newProfile.setUsername(fbUsername);
newProfile.setFirstname(fbFirstname);
newProfile.setLastname(fbLastname);
newProfile.setName(fbName);
newProfile.setEmail(fbEmail);
if(!fbHometown.equals("")){
newProfile.setHometown(fbHometown);
newProfile.setHometownID(Long.parseLong(fbHometownID));
}
if(!fbLocation.equals("")){
newProfile.setLocation(fbLocation);
newProfile.setLocationID(Long.parseLong(fbLocationID));
}
newProfile.setLink(fbLink);
if(fbGender.equalsIgnoreCase("male")){
newProfile.setGender(Gender.MALE);
}else if (fbGender.equalsIgnoreCase("female")) {
newProfile.setGender(Gender.FEMALE);
}
newProfile.setUser(newUser);
this.facebookProfileService.createNewFacebookProfile(newProfile);
//newUser.setFbprofile(newProfile);
}else { //There already exists a FacebookProfile
newUser = fbprofile.getUser();
}
I feel like I have tried every possible solution to this and haven't got it to work.
I suspect that in one of my earlier attempts that the fbprofile reference was set in the User object, but never persisted because it was set after the User object was persisted. I am using JPA2 and EclipseLink.
If someone's got a solution to this it would be much appreciated.
Bi-Directional relations is not maintained by JPA. JPA designers had thought that maintaining bi-directional relations can cause deadlocks, inconsistencies... Toplink was supporting bi-directional relations before JPA.. I have used Toplink for many years, I did come accross some inconsistencies related to bi-directional relations.. You should maintain bi-directional relations manually.. Such as ;
newProfile.setUser(newUser);
newUser.setFbProfile(newProfile);
EDIT :
Well, you want me to elaborate my answer.. You should change your code that persist FbProfile entity such as ;
newProfile.setUser(newUser);
newUser.setFbprofile(newProfile);
this.facebookProfileService.createNewFacebookProfile(newProfile);
As an alternative , you could also change FbProfile setUser method ;
public void setUser(User aUser) {
if (this.user==aUser)
return;
if (this.user != null) {
this.user.setFbProfile(null);
}
this.user = aUser;
if (this.user!=null) {
this.user.setFbprofile(this);
}
}

many-to-many JPA mapping inserting but not fething the child collections

i've hit a block once again with hibernate.I've posted numerous times on different aspects of the user and contact management that i've been building.
The sad thing is that i didn't really have the time to play with it and understand it better before actually starting working with it. Sorry but English is not my native language, i rather speak french. And again i've started coding in java in an autodidact way.i'm doing all of this by reading books and haven't gone to school for it. with time constraints it's hard to read a book from beginning to the end.
I'm not sure i should put every of my codes dealing with an issue here and from what i've learned from other forum is to post just the necessary and being concise.
So in my User model i have UserAccount class, Profile that holds details like name, preferences etc , AccountSession and Phone.
my contact management model have Contact and Group.
UserAccount has one-to-one association with Profile, one-to-many with AccountSession,contact and group, all bidirectional.the one-to-many association with phone is unidirectional because contact also has and unidirectional with Phone.
Contact has a bidirectional many-o-many with group and one-to-many with phone that i said earlier.
Group also has a many-to-many bedirectional with contact.
here are the mappings
// UserAccount
......
#OneToOne(targetEntity=UserProfileImpl.class,cascade={CascadeType.ALL})
#org.hibernate.annotations.Cascade(value=org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
#JoinColumn(name="USER_PROFILE_ID")
private UserProfile profile;
#OneToMany(targetEntity=ContactImpl.class, cascade={CascadeType.ALL}, mappedBy="userAccount")
#org.hibernate.annotations.Cascade(value=org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
private Set<Contact> contacts = new HashSet<Contact>();
#OneToMany(targetEntity=GroupImpl.class, cascade={CascadeType.ALL}, mappedBy="userAccount")
#org.hibernate.annotations.Cascade(value=org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
private Set<Group> groups = new HashSet<Group>();
.......
//Group
#ManyToOne(targetEntity=UserAccountImpl.class)
#JoinColumn(name="USER_ACCOUNT_ID",nullable=false)
private UserAccount userAccount;
#ManyToMany(targetEntity=ContactImpl.class,cascade={CascadeType.PERSIST, CascadeType.MERGE})
#JoinTable(name="GROUP_CONTACT_MAP", joinColumns={#JoinColumn(name="GROUP_ID")},
inverseJoinColumns={#JoinColumn(name="CONTACT_ID")})
private Set<Contact> contacts = new HashSet<Contact>();
//Contact
....
#ManyToOne(targetEntity=UserAccountImpl.class)
#JoinColumn(name="USER_ACCOUNT_ID",nullable=false)
private UserAccount userAccount;
#ManyToMany(targetEntity=GroupImpl.class, mappedBy="contacts")
private Set<Group> groups=new HashSet<Group>();
....
// helper methods from group
public void addContact(Contact contact) {
try{
this.getContacts().add(contact);
contact.getGroups().add(this);
}catch(Exception e) {
}
}
//helper method from group
public void removeContact(Contact contact) {
contact.getGroups().remove(contact);
this.getContacts().remove(contact);
}
//helper method from contact
public void addGroup(Group group) {
try{
this.getGroups().add(group);
group.getContacts().add(this);
} catch(Exception e) {
e.printStackTrace();
}
}
//Helper method from group
public void removeGroup(Group group){
try{
group.getContacts().remove(this);
this.getGroups().remove(group);
} catch(Exception e) {
e.printStackTrace();
}
}
//UserAccount setter from Contact.All the children with many-to-one have the same
/**
* #param userAccount the userAccount to set
*/
public void setUserAccount(UserAccount userAccount) {
this.userAccount = userAccount;
}
I'ld like to pull the UserAccount by its email field which is an unique field in the UserAccount table.
In the UserAccountDAO the method i call to get the UserAccount is getUserAccountByEmail here below.So i expect this method to load all the children collections of the UserAccount namely its Contact collection, group collection.I want it in such a way that when UserAccount is loaded with Contacts collection each of the contact object has its reference with its belonging groups collection if any etc and vice versa.
public UserAccount getUserAccountByEmail(String email) {
// try {
logger.info("inside getUserAccountByEmail");
logger.debug(email);
Session session = (Session) this.getDBSession().getSession();
UserAccount user = (UserAccount) session.createCriteria(this.getPersistentClass())
.setFetchMode("contacts", FetchMode.SELECT) //recently added
.setFetchMode("groups", FetchMode.SELECT) // recently added
.add(Restrictions.eq("email", email))
.uniqueResult();
logger.debug(user);
return user;
// } catch(NonUniqueResultException ne) {
// logger.debug("Exception Occured: getUserAccountByEmail returns more than one result ", ne);
// return null;
// } catch(HibernateException he){
// logger.debug("Exception Occured: Persistence or JDBC exception in method getUserAccountByEmail ",he);
// return null;
// }catch(Exception e) {
// logger.debug("Exception Occured: Exception in method getUserAccountByEmail", e);
// return null;
// }
Since there has to be an UserAccount before any contact and groups, in my unit test when testing the saving of a contact object for which there must be an existing group i do this in order
a create userAccount object ua.
b create group object g1;
c create contact object c1;
d ua.addGroup(g1);
e c1.setUserAccount(ua);
f c1.addGroup(g1);
g uaDao.save(ua); // which saves the group because of the cascade
h cDao.save(c1);
Most of the time i use the session.get() from hibernate to pull c1 by its it id generated by hibernate and do all the assertions which works actually.
but in Integration test when i call getUserAccountByEmail with and without the setFetchMode and it returns the right object but then all the children collections are empty. i've tried the JOIN and the SELECT.the query string changes but then the result set is still the same. So this arises some questions :
1. What should i do to fix this?
2. the helper method works fine but it's on the parent side(i do it in the test).What i've been wondering about is that doing c1.setUserAccount(ua); is enough to create a strong relationship between UserAccount and contact.most of the time there will not be cases where i save the userAccount with contact but yet the helper method that set the association in both side and which is in UserAccount will not been called before i save the contact for a particular userAccount.So i'm little confused about that and suspecting that setting of the association is part of the why something is not working properly.and then calling session.get(UserAccount.class, ua.getID()) i think goes what i want and i'ld like getUserAccountByEmail to do the same.
3. ChssPly76 thinks the mapping has to be rewrite.So i'm willing to let you guide me through this.I really need to know the proper way to do this because we can't lean everything from a good book.So i you think i should change the mapping just show me how.and probable i'm doing things the wrong way without even been aware of that so don't forget i'm still learning java itself.THanks for the advise and remarks and thanks for reading this
I agree with you that it seems likely that the associations between your parent objects and their child collections are not getting persisted properly. I always like to start out by looking at what is in the database to figure out what's going on. After you run your test what do you see in the actual database?
It seems likely that one of two things is happening (using UserAccount as an example):
The items in the child collection are not getting saved to the database at all, in which case you'll be able to see in the database that there are no records associated with your UserAccount. This could be caused by saving the UserAccount object before you've added the child object to the UserAccount's collection.
The items in the child collection are getting saved to the database, but without the needed association to the parent object, in which case you'll see rows for your child items but the join column (ie 'userAccount' will be null). This could be caused by not setting the userAccount() property on the child object.
These are the two scenarios that I've run into where I've seen the problem you describe. Start by taking a look at what goes into your database and see if that leads you farther.

Categories