In this series of blog entries we will describe the development of a mobile chat app using SAP NetWeaver Cloud as a backend. The last time we explained the web service which the mobile app consumes. Now we want to tell you more about how we store the data – on NW Cloud and also on the mobile device.
We thought it would be useful to be able to read messages although you don’t have an Internet connection. Therefore we decided to save some data on the iPhone. We had the opportunity to use a SQLite Database, as we did in former applications, or to use Core Data. If you choose the SQLite Database you have to use SQL Statements to access to the data. Core Data is object-orientated instead, as you might now from JPA on NW Cloud. To read more about data management in iOS check out Apple developer’s website. We decided to choose Core Data because Apple recommends it and it’s always good to try something new.
There is a getting-started documentation from Apple available which explains the first steps when you want to use Core Data. When you create a new Xcode project there is a small check box with the caption “use Core Data”. If you check this one some necessary methods will already be created for you in the new application.
When you start using Core Data you will read a lot about the managedObjectContext. This is a powerful object with a central role in Core Data. You have to pass it to every ViewController which wants to access to the stored data. You can easily handle this with the few lines below in every viewDidLoad method.
// Core Data if(managedObjectContext == nil){ managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext]; } |
We don’t want to talk much about Core Data because there are a lot of tutorials online and there is also the documentation from Apple. So we hope that’s enough at this point but if you have questions or suggestions please feel free to comment.
When you have some experiences with iOS development you probably know that you have to take care of the memory yourself. And that’s sometimes a difficult topic. Since iOS 4 Apple provides the opportunity to use Automatic Reference Counting (ARC) to make the memory management easier for developers. When you are using ARC you do not have to use retain, release or autorelease anymore. We decided to use ARC to have all the advantages and an easier memory management. One error source for us was how to access the objects the right way. You have to put “self.” in front of an object to do so. If you forget that, the object will be released too early.
As we mentioned in our previous blog posts we use JPA to save all the data which is necessary for our web service. In blog post 2 [LINK] we showed you the connection between our three entities user, message and chat room. We want to show you how we implemented the link between this entities by providing you the code we need to create a new user and chat rooms for him. Let’s start with the declaration of the user entity:
// mark id-property as primary key via @ID-annotation private String id; // simple property of the entity private String name; // one user writes lots of messages, but not the other way round @OneToMany(mappedBy = "user", cascade = CascadeType.PERSIST) //war all private List<Message> messages; // a user is in lots of chatrooms, chatrooms consists of lots of users @ManyToMany(cascade = CascadeType.PERSIST) private List<Chatroom> chatrooms; private String devicetoken; private String authtoken; // another simple property private String email; |
We leave out the corresponding getter and setter methods as they contain only one single line of code and you can use eclipse to generate them.
The other entities and their properties are created the same way. The relationship between two entities is for the other entity of course the other way round. For example in the messages entity, there is following declaration:
@ManyToOne private User user; |
Let’s take a deeper look into the code we need to create a new user. In a nutshell: Create a new user-object, set its properties and persist the object. To make it a little more complex we also create some new chat rooms for the new created user with every other user (so in the end the new user has a single 1:1 chat room with every other user).
// begin the transaction em.getTransaction().begin(); String newusername = requestjson.getString("user").toUpperCase(); // create user object User newUser = new User(); newUser.setId(newusername); newUser.setName(requestjson.getString("user")); // create chat rooms for new user, therefore fetch all other users List<User> userlist = em.createQuery("SELECT u FROM User u WHERE u.id <>'" + newusername + "'", User.class).getResultList(); // create a 1:1 chatroom for every single user for (User oneotheruser : userlist) { // create new chat room Chatroom chatroom = new Chatroom(); // set (default) name of chat room on the basis of the name of the two users chatroom.setName(requestjson.getString("user") + " and " + oneotheruser.getName()); // add existing user to new chat room chatroom.getUsers().add(oneotheruser); // add new user to new chat room chatroom.getUsers().add(newUser); // assign new created chat room to both users oneotheruser.getChatrooms().add(chatroom); newUser.getChatrooms().add(chatroom); // persist new chatroom em.persist(chatroom); // persist existing user because his chat rooms were updated em.persist(oneotheruser); } // persist new created user after all chat rooms were created em.persist(newUser); // commit whole transaction em.getTransaction().commit(); |
That’s it. Regarding the topic JPA you might be interested how you can read the connection between two entities. For example we need to know which chat rooms a certain user attends. Because of the many-to-many-relationship that’s where the JOIN-statement comes in:
List<Chatroom> allchatrooms = em.createQuery("SELECT c FROM Chatroom c JOIN c.users u WHERE u.id ='" + requestjson.getString("user").toUpperCase() + "'", Chatroom.class).getResultList(); |
After this statement you can iterate over allchatrooms as it’s showed above.
Usually by using JPA you don’t need to take care about the generated SQL. But sometimes JPA fails to transfer your JPQL into proper SQL statements. One time during our development we had the strange issue that our project worked fine in the localhost environment, but in the cloud JPA was not able to create the data model. We finally solved the problem by reading the SQL logs. You have to know that in the persistence.xml of your data model project there is a setting called logging level where you can adjust the precision of the logs. Several values are possible, we use following line:
<property name="eclipselink.logging.level" value="FINE"/> |
You can access the generated logs via the accounts-web site of NW Cloud (for the public test landscape follow this link). With the help of these logs we solved our issue: One of our properties was called “time” which is of course not allowed as it’s a code word.
One last thing regarding the JPA topic: Whenever we want to clear the database we temporary set the schema generation / DDL generation type to “Drop and Create Tables” and deploy our project again. If there is a more elegant way of resetting the database, please let us know.
At the beginning of our development we struggled using special characters in the messages. The reason was that we didn’t implemented UTF8 completely. We had to enable the charset UTF8 at three locations:
response.setCharacterEncoding("UTF-8"); |
byte[] utf8 = onemessage.getContent().getBytes(); String utf8message = new String(utf8, "UTF-8"); |
It would be better when we could set the general charset of the database directly to UTF8. When you find a way how to do that, please comment on this blog post.
NSString *jsonAsString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; |
Furthermore, we have to encode the HTTPBody of our NSMutableURLRequest with UTF8.
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:glUrl]; NSString *params = [[NSString alloc] initWithFormat:@"post=%@", jsonAsString]; [request setHTTPMethod:@"POST"]; [request setHTTPBody:[params dataUsingEncoding:NSUTF8StringEncoding]]; |
And finally the data we get as a response of our request has to be encoded with UTF8.
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]; |
Now you know some details about our application. In the next blog post we will describe how we implement the iOS-Push-technology to send new chat messages directly to the device. We always appreciate your feedback so please feel free to suggest any ideas of improvement.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
29 | |
17 | |
15 | |
13 | |
11 | |
9 | |
8 | |
8 | |
8 | |
7 |