I have two classes of Authors and Books:
public class Authors extends RealmObject {
#PrimaryKey
private String url_base;
private RealmList<Books> books;
... getters & setters...
public RealmList<Books> getBooks() {
return books;
}
public void setBooks(RealmList<Books> books) {
this.books = books;
}
}
public class Books extends RealmObject {
#PrimaryKey
private String url_base;
private Authors author;
... getters & setters...
public Authors getAuthor() {
return author;
}
public void setAuthor(Authors author) {
this.author = author;
}
}
Perform inserts:
Authors author = new Authors();
author.setUrl_base("url_base")
Books book = new Books();
book.setUrl_base("lala");
book.setAuthor(author);
author.getBooks().add(book); // error comes here
realm.beginTransaction();
realm.copyToRealmOrUpdate(author);
realm.commitTransaction();
And the program gives java.lang.NullPointerException.
Error line : author.getBooks().Add (book);
What could be wrong? I ask your help.
You haven't set the books member for your author object (as books is not initialized it is null) so getBooks() will return a null.
Make sure books is initialized before using it :
public class Authors extends RealmObject {
#PrimaryKey
private String url_base;
private RealmList<Books> books = new RealmList<Books>(); // An empty unmanaged books list.
... getters & setters...
or use the setter method (though initializing it on construction will be more correct in this case):
Authors author = new Authors();
author.setUrl_base("url_base")
author.setBooks(new RealmList<Book>());
UPDATE:
As #AndreyAtapin correctly noted in the solution above the list will be unmanaged. If you want a managed list pass the required arguments as specified in the RealmList api but whatever you choose the books list must be initialized before you start manipulating it
According to Realm docs you should instantiate entity objects with special factory method:
realm.beginTransaction();
Authors author = realm.createObject(Authors.class);
author.setUrl_base("url_base")
Books book = realm.createObject(Books.class);
book.setUrl_base("lala");
book.setAuthor(author);
author.getBooks().add(book); // error comes here
realm.commitTransaction();
When you instantiate your entities with constructor, obviously all fields are null by default:
public class Authors extends RealmObject {
#PrimaryKey
private String url_base; // = null
private RealmList<Books> books; // = null
... getters & setters...
}
PS: By the way, using variables names url_base contradicts the Java coding conventions. You better call it urlBase (setUrlBase/getUrlBase accordingly).
Related
I'm learning the Spring Framework and I'm struggling with the Rest services with spring, in particular for the POST call that it's supposed to add a new object to the database.
I've seen a lot of different implementations through the web, but I don't know how to pick the best.
Let's take for example a film class:
#Entity
public class Film {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String title;
private String description;
//Constructor, Getter and Setter Omitted.
}
Assuming the repository extends the JpaRepository<Film,Long>, this would be the Controller class:
#RestController
public class FilmController {
#Autowired
FilmRepository filmRepository;
//Implementation #1
#PostMapping("/film")
public Film addNew(#RequestBody Map<String,String> body){
String title = body.get("title");
String description = body.get("description");
return filmRepository.save(new Film(title,description));
}
//Implementation #2
#PostMapping("/film")
public Film addNew(String title, String description){
Film film = new Film(title,description);
System.out.println(film.getTitle() + " " + film.getDescription());
return filmRepository.save(film);
}
//Implementation #3
#PostMapping("/film")
public Film addNew(#RequestBody Film newFilm){
return filmRepository.save(newFilm);
}
}
Why some implementations have as parameter a Map<String, String> ? Is that a body mapped to a key/value pair ?
Also bear in mind that I managed to implement correctly just the implementation #2, the first and the third gave me a
415 error:"Unsupported Media Type" org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'multipart/form-data;boundary=--------------------------901298977805450214809889;charset=UTF-8' not supported]
(Despite I followed the official Spring tutorial) on REST services.
I also read something about the creation of DTO classes where I can define attributes without exposing the object to the controller, how can be implemented such solution?
Implementation 3 is the best practice, but you should create a lightweight DTO class (maybe FilmDto) to avoid exposing the internal structure of your entity, please see LocalDTO, Martin Fowler.
You may use ModelMapper to map FilmDto to Film, and make sure there are proper getters and setters in both classes, if the getters and setters have the same names in both classes, then ModelMapper will do the conversion smoothly:
public class FilmDto {
private long id;
private String title;
private String description;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
and you controller:
#RestController
#RequestMapping("/api")
public class FilmController {
private final FilmRepository filmRepository;
private ModelMapper modelMapper = new ModelMapper();
#Autowired
public FilmController(FilmRepository filmRepository) {
this.filmRepository = filmRepository;
}
//Implementation #3
#PostMapping("/film")
public ResponseEntity<FilmDto> addNew(#RequestBody FilmDto filmDto){
Film newFilm = modelMapper.map(filmDto, Film.class);
newFilm = filmRepository.save(newFilm);
filmDto.setId(newFilm.getId());//you may use modelMapper here
return ResponseEntity.ok(filmDto);
}
}
you can test using postman by passing the film as below:
{
"title": "some title",
"description": "some description"
}
and the body should be of type "raw", "JSON".
Why some implementations have as parameter a Map<String, String> ?
some implementations use map<key,value> because they need the properties that map interface provide such as non-duplicate key value or the classes that implement map interface such as TreeMap and LinkedHashMap.
about your implementation of the class FilmController i think its not necessary to use map<String,String> for posting your domain in the data base simply you can have this implementation
#RestController
public class FilmController {
#Autowired
FilmRepository filmRepository;
#PostMapping("/film")
public ResponseEntity addNew(#RequestBody Film film){
return ResponseEntity.ok(filmRepository.save(film));
I have a Base Class.
#Data
class BaseDocument{
String id;
String name;
//Other fields
}
Say I have many classes that extends BaseDocument one below.
class NoteDocument extends BaseDocument{
String description;
Long lastModifiedDate;
//etc
}
It does not make sense to me to send entire document to UI in some cases. Most of the cases I need only id and name.
So for every document I have a VO class.
#Data
class BaseVO {
private String id;
private String name;
}
#Data
class NoteVO extends BaseVO{
//Nothing here now
}
And in NoteDocument I have.
public NoteVO getVo(){
Assert.notNull(getId());
NoteVO noteVo = new NoteVO();
noteVo.setName(getName());
noteVo.setId(getId());
return noteVo;
}
Now I have to copy this method in all the classes that extends BaseDocument.
Instead, I changed my BaseDocument like below.
#Data
class BaseDocument<V extends BaseVO>{
String id;
String name;
public V getVo(Class className) {
Assert.notNull(getId());
V vo = null;
try {
vo = (V) className.newInstance();
vo.setName(getName());
vo.setId(getId());
} catch (IllegalAccessException|InstantiationException e){
e.printStackTrace();
}
Assert.notNull(vo);
return vo;
}
}
I am new to generics. My first question, is this a good practice. Are there any problems in using reflection to create instance, any performance issues? Is there any better way to do achieve (write less code) this.
Edit: Suppose I need to display note in UI, Along with note I need to display name of the User who created note. I am using mongodb, when I save the note I also save UserVO in note, which will have user id and name of the user. If I save only user id while saving the note, I will have to do one more query to get the name of user while displaying. I want to avoid this.
Do not use reflection; use inheritance and maybe covariant return types instead. It will be faster, clearer, more precise, and easier to maintain. You may also find it useful to add methods to populate your VOs incrementally. I didn't come up with a clean way to apply generics to this situation, but I don't think you need them:
class BaseVO {
String id;
String name;
void setId(String id) {
this.id = id;
}
void setName(String name) {
this.name = name;
}
}
class NoteVO extends BaseVO {
// ...
}
#Data
class BaseDocument {
String id;
String name;
//Other fields
protected void populateBaseVO(BaseVO vo) {
vo.setId(id);
vo.setName(name);
}
public BaseVO getVO() {
BaseVO vo = new BaseVO();
populateBaseVO(vo);
return vo;
}
}
#Data
class NoteDocument extends BaseDocument {
String description;
Long lastModifiedDate;
// ....
protected void populateNoteVO(NoteVO vo) {
populateBaseVO(vo);
// ...
}
public NoteVO getVO() {
NoteVO vo = new NoteVO();
populateNoteVO(vo);
return vo;
}
}
I am trying out some examples from Beginning Java EE6 with GlassFish3 .So , i created an entity class that basically looks like this ...
#Entity
#Table(name="Book")
public class Book implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(nullable=false)
private String name;
#Column(nullable=false)
private String isbn;
private String description;
public Book()
{
// Empty constructor to facilitate construction.
System.out.println("The variables have not been initialized...Please initialize them using the Setters or use the provided constructor");
}
public Book(String name, String isbn, String description) {
this.name = name;
this.isbn = isbn;
this.description = description;
}
public String getIsbn() {
return isbn;
}
public void setIsbn(String isbn) {
this.isbn = isbn;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#Override
public String toString() {
return this.name + " - " + this.isbn;
}
#PrePersist
public void printPrePersist(){
System.out.println("Persisting the book "+this.name);
}
#PostPersist
public void printPostPersist(){
System.out.println("Persisted the book "+this.name);
}
}
and i tried to persist it like this ...
public class MainClass
{
public static void main(String[] args){
Book book = new Book("Effective Java","ISBN - 1234415","A very good book on Java");
Book book2 = new Book("Learning Java EE","ISBN - 1233415","A good book for Java EE beginners");
// These are the necessary classes
EntityManagerFactory emf = Persistence.createEntityManagerFactory("PersistenceAppPU");
EntityManager em = emf.createEntityManager();
// Persist the book here
EntityTransaction etx = em.getTransaction();
etx.begin();
em.persist(book);
em.persist(book2);
etx.commit();
em.close();
emf.close();
System.out.println("The two books have been persisted");
}
}
It persists , but when i run , i see an output like ...
The variables have not been initialized...Please initialize them using the Setters or use the provided constructor
Persisting the book Effective Java
The variables have not been initialized...Please initialize them using the Setters or use the provided constructor
Persisting the book Learning Java EE
Persisted the book Learning Java EE
Persisted the book Effective Java
The variables have not been initialized...Please initialize them using the Setters or use the provided constructor
The variables have not been initialized...Please initialize them using the Setters or use the provided constructor
The variables have not been initialized...Please initialize them using the Setters or use the provided constructor
The variables have not been initialized...Please initialize them using the Setters or use the provided constructor
[EL Info]: 2012-05-10 12:01:19.623--ServerSession(17395905)--file:/C:/Users/raviteja.s/Documents/NetBeansProjects/PersistenceApp/src/_PersistenceAppPU logout successful
The two books have been persisted
I dont understand , why there are so many default constructor calls when , there is not one made by me ... ?
Could somebody explain me how the flow is in the sample that i have ?
JPA uses a constructor with no arguments in order to instantiate your Entities, and then bind fields in those entities to the correspondent mapped tables and columns.
Those output you see are the calls that JPA does for you every time it manipulates your entities.
I am using MyBatis to do a simple select.
Assume we have the following classes:
class Book {
private String bookName;
public Book(String bookName){
this.bookName = bookName;
}
public String getBookName(){
return bookName;
}
}
class Student {
private String studentName;
private Book book;
public Student(){}
// getters and setters
}
I have an annotation on a method that returns a Student object.
#Select("Select studentName, book from Students")
My Issue is that book is always null. I was under the assumption MyBatis will call the constructor with that JDBC type (in this case String) to populate book. What am I missing or doing incorrectly?
One option is
Use #ConstructorArgs annotations to explicitly call Constructor method.
#Select("Select studentName, book from Students")
#ConstructorArgs(value = {
#Arg(column = "studentName", javaType=java.lang.String.class),
#Arg(column = "book", javaType = java.lang.String.class)
})
and pass them to Student constructor, which calls Book constructor.
while coding the Functional Test for my web app which uses playframework,I created
#Before
public void setup() {
Fixtures.deleteDatabase();
}
#Test
public void testListTagged() {
Fixtures.loadModels("data.yml");
Response response = GET("/books/category/science");
assertNotNull(renderArgs("books"));
List<Book> books = (List<Book>)renderArgs("books");
assertEquals(3,books.size());
the listTagged method checks the Cache for a map object (String:List<Book>)that contains a list of books belonging to the given category, and if the map is null or the list is null,database query is made and the list is rendered as 'books'.
public static void listTagged(String category) {
List<Book> books =null;
Map<String,List<Book>> tagMap = (Map<String, List<Book>>) Cache.get("tagmap");
if(tagMap!= null) {
books = tagMap.get(category);
}
if(tagMap==null || books == null) {
books= Book.findTaggedWith(category);
}
Book book = null;
if (books!=null && books.size()>0) {
book = books.get(0);
}
render(category,book, books);
}
Book class is
#Entity
public class Book extends Model implements Comparable<Book>{
#Required
#Column(unique = true)
public String isbn;
#Required
//#Field
public String name;
...
#ManyToMany(cascade=CascadeType.PERSIST)
public Set<Category> categories;
public Book(String isbn, String name, ...) {
super();
this.isbn = isbn;
this.name = name;
...
this.categories = new TreeSet<Category>();
}
...
public static List<Book> findTaggedWith(String categoryName) {
Map<String,List<Book>> tagMap = (Map<String, List<Book>>) Cache.get("tagmap");
if(tagMap==null) {
tagMap= new HashMap<String,List<Book>>();
}
List<Book> books = Book.find("select distinct book from Book book join book.categories as cat where cat.name=:name").bind("name", categoryName).fetch();
tagMap.put(categoryName, books);
Cache.add("tagmap", tagMap,"20mn");
return books;
}
Running the above test alone caused no problems.But when it was run along with some unit tests which called the database for various books causes a lazy initialization exception
A java.lang.RuntimeException has been caught, java.util.concurrent.ExecutionException: play.exceptions.TemplateExecutionException: failed to lazily initialize a collection of role: models.Book.categories, no session or session was closed
How do I solve this? can someone please advise?
you could use:
#ManyToMany(cascade=CascadeType.PERSIST, fetch=FetchType.EAGER)
public Set<Category> categories;
This will probably solve the problem. Otherwise you'll have to take care, that a session is open. Tested and now working