Message Boards

How to add and display data from 2 tables?

Tiago Machado, modified 5 Years ago.

How to add and display data from 2 tables?

Junior Member Posts: 59 Join Date: 2/21/19 Recent Posts
Hello, im new at Liferay dev, i already posted once here when i was training with only one entity, now i added another entity.
So, i have Movie and Author, i know this is not a good example but im just training and getting used to this.
I had a form with fields to add the movie name, the rating and a description, and same for displaying... now that i have a new entity called Author created 2 more aui:input for the authot name and a biography. since my author needs the movie id im having a little trouble. i was trying to just use the AddMovieMVCActionCommand to add the movie and the author at the same time, shouldnt know the movieID right?
or should i use 2 actionURL in the same jsp? and then i would create a AddAuthorMVCActionCommand... what is the best way to do this? or am i doing something wrong?

thank you
thumbnail
David H Nebinger, modified 5 Years ago.

RE: How to add and display data from 2 tables?

Liferay Legend Posts: 14914 Join Date: 9/2/06 Recent Posts
Assuming you have an addMovie() method in your service layer, it would return the new Movie instance.  From thare you can get the movieId and use it for a corresponding addAuthor() call in your service layer.

For the next level, you might consider that these are all part of the same transaction. In this case, you may want to have an additional addMovieAndAuthor() method in the MovieLocalServiceImpl. Since these are part of the same service layer, you'll find that MovieLocalServiceImpl has an injected AuthorLocalService so you can piggyback one call to another.

This will keep the addition of a movie and an author within the same transaction scope.

Your addMovieAndAuthor() method would first invoke addMovie() and, using the movieId in the returned Movie object, can then call addAuthor() to complete the work.
Tiago Machado, modified 5 Years ago.

RE: How to add and display data from 2 tables?

Junior Member Posts: 59 Join Date: 2/21/19 Recent Posts
Thank you a lot David! that sounds pretty good! gonna give it a try emoticon

Very helpful. thanks
Tiago Machado, modified 5 Years ago.

RE: How to add and display data from 2 tables?

Junior Member Posts: 59 Join Date: 2/21/19 Recent Posts
Hey David, i managed to do what you suggested, the addMovieAndAuthor method is working fine, and i'm adding the movie data to movie table and author data to author table.

But im having a little toubling showing the results, before i was showing the movies using the search container, worked fine, but how can I display the data from the two tables? i want to show the movies and author tables all in one. do i need to use join? or is there any other method?

I tried to do like before but im getting "com.liferay.training.movies.model.impl.AuthorImpl cannot be cast to com.liferay.training.movies.model.Movie"

What i tried was to add another search container row giving it className of the Author, so i had 2 search container rows, one for the Movie and the other for the Author, but i guess i cant do that.

Can you give me some hand here in how to do this?
Thanks!
thumbnail
David H Nebinger, modified 5 Years ago.

RE: How to add and display data from 2 tables?

Liferay Legend Posts: 14914 Join Date: 9/2/06 Recent Posts
So the search container is only going to handle a single entity class.

So the trick here is to expose the author entity as part of the movie entity.  You can modify your MovieImpl (the implementation of your Movie entity) and include a getter for Author.  Then you can either modify your getters in MovieLocalServiceImpl to fetch the author and populate the field, or you could use the static AuthorLocalServiceUtil to fetch the author on demand.

For the getter overrides, you would have a local Author field in MovieImpl and a setAuthor() method, then in each of your MovieLocalServiceImpl methods that can get one or more Movies, you add code to fetch the Author instance and use setAuthor() to assign it.

For the on demand, I normally have a local Author field in MovieImpl and the getAuthor() method. The getAuthor() checks if the field is null; if it is, use the AuthorLocalServiceUtil to fetch the Author and set to the local field, then return the value.  It's a lazy fetch (only fetches when needed) and caches the value.  It suffers though in that it can potentially cache a stale Author if it is later changed for the movie, so you have to figure out if you need to deal with this or not.

Typically you choose depending upon whether the related entity will always be used or not; since you are showing as part of the listing for the movie, the prefetch approach makes more sense than the on demand approach (although on demand is often easier to implement).
Tiago Machado, modified 5 Years ago.

RE: How to add and display data from 2 tables?

Junior Member Posts: 59 Join Date: 2/21/19 Recent Posts
RE: How to add and display data from 2 tables?
Tiago Machado, modified 5 Years ago.

RE: How to add and display data from 2 tables?

Junior Member Posts: 59 Join Date: 2/21/19 Recent Posts
Hey, I'm trying really hard but I'm not being able to do it. I'm trying what you told, i created getter, setter and a private Author in the MovieImpl.

Then in the MovieLocalServiceImpl I added a private Author author; and setted the author in the addMovie and AddMovieAndAuthor method but im getting null pointer when i add a new movie

i added the property too in the jsp inside the search container, -> property="<%=currentMovie.getAuthor().getAuthorName() %>"

what am I doing wrong?

I printed the movie.getAuthor and its null, if i comment the setter in the MovieLocalServiceImpl and the data is persisted in the 2 tables from the same form. but if i try to do that i get nullpointer. I know I'm doing something wrong but I'm not getting it.

One more thing just to understand a little bit more, since I'm persisting the data when i add a movie (the movie and author data), and then im showing only the movie data in the search container (the obj is the show both movie and author as i said) when I'm setting the author to the movie, exposing the author to the movie, after the adding the data wont i lose the reference?

can you help me understand a little more and tell me what am I doing wrong please? 
​​​​​​​thanks a lot!

(im sorry with the post above, i dont know what happened, i actually writted the text before and Published it but it didnt appear and i could edit)
thumbnail
David H Nebinger, modified 5 Years ago.

RE: How to add and display data from 2 tables?

Liferay Legend Posts: 14914 Join Date: 9/2/06 Recent Posts
Well, the NPE comes from your code clearly; I can't really help you with what I don't have.

​​​​​​​
Tiago Machado, modified 5 Years ago.

RE: How to add and display data from 2 tables?

Junior Member Posts: 59 Join Date: 2/21/19 Recent Posts
Yeah, sorry,  I have the following code:

MovieImpl:
​​​​​​​@ProviderType
public class MovieImpl extends MovieBaseImpl {
&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;private Author author;
&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;public MovieImpl() {
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;public Author getAuthor() {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return author;
&nbsp;&nbsp; &nbsp;}

&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;public void setAuthor(Author author) {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;this.author = author;
&nbsp;&nbsp; &nbsp;}
}
​​​​​​​MovieLocalServiceImpl:
​​​​​​​public class MovieLocalServiceImpl extends MovieLocalServiceBaseImpl {
&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;//Author author;
&nbsp;&nbsp; &nbsp;public Movie addMovie(long groupId, String movieName, String description,&nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;int rating, ServiceContext serviceContext) throws PortalException {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//Group is used for the scoping the Movie entity to the site
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Group group = groupPersistence.findByPrimaryKey(groupId);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//getting the user, first get the user id go get the user : userPersistence || userLocalService
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;long userId = serviceContext.getUserId();
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;User user = userLocalService.getUserById(userId);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//Generate primary key for the new movie - referencing the movie class in the specific movie about to create
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;long movieId = counterLocalService.increment(Movie.class.getName()); //counterLocalService helps with primary key
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//create new movie object
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Movie movie = super.createMovie(movieId);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//populate all movie object fields
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;movie.setCompanyId(group.getCompanyId());
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;movie.setGroupId(groupId);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;movie.setUserId(userId);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;movie.setDescription(description);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;movie.setMovieName(movieName);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;movie.setRating(rating);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;movie.setUserName(user.getScreenName());
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;movie.setModifiedDate(serviceContext.getModifiedDate(new Date()));
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;movie.setCreateDate(serviceContext.getCreateDate(new Date()));
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//movie.setAuthor(author);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//test
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//System.out.println("autho: " + movie.getAuthor());
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//System.out.println("author name test: " + movie.getAuthor().getAuthorName());
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//persist the movie
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;movie = super.addMovie(movie);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//add permission resources
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;boolean portletActions = false;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;boolean addGroupPermissions = true;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;boolean addGuestPermissions = true;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//when creating the movie, we create the permission resource along it.
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;resourceLocalService.addResources(group.getCompanyId(),groupId, userId, Movie.class.getName(),&nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;movie.getMovieId(), portletActions, addGroupPermissions, addGuestPermissions);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return movie;
&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;public Movie addMovieAndAuthor(long groupId, String movieName, String description, int rating,
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;String authorName, String biography, ServiceContext serviceContext) throws PortalException {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Movie movie = addMovie(groupId, movieName, description, rating, serviceContext);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Author author = AuthorServiceUtil.addAuthor(movie.getMovieId(), authorName, biography, serviceContext);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;author = AuthorLocalServiceUtil.addAuthor(author);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//movie.setAuthor(author);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return addMovie(movie);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;}
​​​​​​​AuthorLocalServiceImpl:
​​​​​​​public class AuthorLocalServiceImpl extends AuthorLocalServiceBaseImpl {
&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;public Author addAuthor(long movieId, String authorName, String biography, ServiceContext serviceContext)&nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;throws PortalException {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Movie movie = MovieLocalServiceUtil.getMovie(movieId);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;long userId = serviceContext.getUserId();
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;User user = UserLocalServiceUtil.getUser(userId);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//generate the author primary key
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;long authorId = counterLocalService.increment(Author.class.getName());
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//create author obj
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Author author = authorLocalService.createAuthor(authorId);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//set author properties
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;author.setCompanyId(movie.getCompanyId());
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;author.setGroupId(movie.getGroupId());
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;author.setAuthorId(authorId);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;author.setAuthorName(authorName);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;author.setBiography(biography);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;author.setUserName(user.getScreenName());
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;author.setMovieId(movieId);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;author.setUserId(userId);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;author.setCreateDate(new Date());
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;author.setModifiedDate(new Date());
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//persist data
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;super.addAuthor(author);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;return author;
&nbsp;&nbsp; &nbsp;}
AddMovieMVCActionCommand:
​​​​​​​
@Component(
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;immediate = true,
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;property = {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;"javax.portlet.name=" + MoviesPortletKeys.MOVIES,
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;"MVC.command.name=" + MVCCommandNames.ADD_MOVIE,
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;},&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;service = MVCActionCommand.class
)
public class AddMovieMVCActionCommand extends BaseMVCActionCommand {
&nbsp;&nbsp; &nbsp;@Reference
&nbsp;&nbsp; &nbsp;MovieService movieService;
&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;public static final Log log = LogFactoryUtil.getLog(Movie.class);
&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;@Override
&nbsp;&nbsp; &nbsp;protected void doProcessAction(ActionRequest actionRequest, ActionResponse actionResponse) throws Exception {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(WebKeys.THEME_DISPLAY);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;ServiceContext serviceContext = ServiceContextFactory.getInstance(Movie.class.getName(), actionRequest);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;String movieName = ParamUtil.getString(actionRequest, "movie");
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;String description = ParamUtil.getString(actionRequest, "description");
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;int rating = ParamUtil.getInteger(actionRequest, "rating");
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;String authorName = ParamUtil.getString(actionRequest, "author");
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;String biography = ParamUtil.getString(actionRequest, "biography");
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;try {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;MovieLocalServiceUtil.addMovieAndAuthor(themeDisplay.getScopeGroupId(), movieName, description, rating, authorName,&nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;biography, serviceContext);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;SessionMessages.add(actionRequest, "movie-addded");
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;hideDefaultSuccessMessage(actionRequest);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;sendRedirect(actionRequest, actionResponse);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}catch (MovieValidationException e){
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;actionResponse.setRenderParameter("mvcRenderCommandName", MVCCommandNames.EDIT_MOVIE);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}catch (PortalException e){
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;log.error(e);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;SessionErrors.add(actionRequest, MVCCommandNames.EDIT_MOVIE);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;}
}


​​​​​​​

update.jsp (This is actually add movie. bad naming here)
​​​​​​​<portlet:renderurl var="goBackURL">
&nbsp;&nbsp; &nbsp;<portlet:param name="jspPage" value="/view.jsp" />
</portlet:renderurl>

<h1>Add Movie</h1>

<portlet:actionurl name="<%=MVCCommandNames.ADD_MOVIE %>" var="addMovieURL"></portlet:actionurl>

&nbsp; &nbsp; &nbsp; &nbsp; <aui:form action="<%= addMovieURL %>" name="<portlet:namespace />fm">

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <aui:fieldset>

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <aui:input name="movie" />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <aui:input name="description" />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <aui:input name="rating" />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <aui:input name="author" />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <aui:input name="biography" />

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </aui:fieldset>

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <aui:button-row>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <aui:button type="submit"></aui:button>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <aui:button type="cancel" onclick="<%= goBackURL.toString() %>"></aui:button>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </aui:button-row>
&nbsp; &nbsp; &nbsp; &nbsp; </aui:form>
​​​​​​​view_test.jsp (all data is viewed here)
​​​​​​​<h1 align="center">Movies List</h1>

<portlet:actionurl var="goBackURL">
&nbsp;&nbsp; &nbsp;<param name="jspPage" value="/view.jsp">
</portlet:actionurl>

<liferay-portlet:renderurl varimpl="iteratorURL">
&nbsp;&nbsp; &nbsp;<liferay-portlet:param name="jspPage" value="/view_test.jsp" />
</liferay-portlet:renderurl>

<liferay-ui:search-container emptyresultsmessage="there-are-no-movies"     headernames="movieName, description, rating, action"  iteratorurl="<%=iteratorURL%>"  delta="10"  curparam="sc1"  deltaconfigurable="true">
&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;<liferay-ui:search-container-results>
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&lt;%
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;int totalMovies = MovieLocalServiceUtil.getMoviesCount();
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;results = MovieLocalServiceUtil.getMovies(searchContainer.getStart(), searchContainer.getEnd());
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;searchContainer.setTotal(totalMovies);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;searchContainer.setResults(results);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;%&gt;
&nbsp;&nbsp; &nbsp;</liferay-ui:search-container-results>
&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;<liferay-ui:search-container-row classname="com.liferay.training.movies.model.Movie"         keyproperty="movieId" modelvar="currentMovie">
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<liferay-portlet:renderurl varimpl="rowURL">
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<portlet:param name="backURL" value="<%=currentURL %>" />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<portlet:param name="jspPage" value="/view_movie.jsp" />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<portlet:param name="movieId" value="<%=String.valueOf(currentMovie.getMovieId()) %>" />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;</liferay-portlet:renderurl>
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<liferay-ui:search-container-row-parameter name="rowURL" value="<%=rowURL.toString() %>" />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<liferay-ui:search-container-column-text href="<%=rowURL %>" name="Name" property="movieName" />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<liferay-ui:search-container-column-text href="<%=rowURL %>" name="description" property="description" />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<liferay-ui:search-container-column-text href="<%=rowURL %>" name="rating" property="rating" />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &lt;%--
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<liferay-ui:search-container-column-text href="<%=rowURL %>" name="author" property="<%=currentMovie.getAuthor().getAuthorName() %>" />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<liferay-ui:search-container-column-text href="<%=rowURL %>" name="biography" property="<%=currentMovie.getAuthor().getBiography() %>" />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;--%&gt;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;<liferay-ui:search-container-column-jsp align="center" path="/button.jsp" />
&nbsp;&nbsp; &nbsp;</liferay-ui:search-container-row>
&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;<liferay-ui:search-iterator searchContainer="<%=searchContainer %>" paginate="<%=true %>" />
</liferay-ui:search-container>


<aui:button-row>
&nbsp;&nbsp; &nbsp;<aui:button type="cancel" onclick="<%=goBackURL.toString() %>"></aui:button>
</aui:button-row>
​​​​​​​
Thanks a lot!
thumbnail
David H Nebinger, modified 5 Years ago.

RE: How to add and display data from 2 tables?

Liferay Legend Posts: 14914 Join Date: 9/2/06 Recent Posts
Okay, so the issue is in your service layer...

MovieLocalServiceImpl's addMovie() should not set the author because, well, you're just adding a movie.

However, addMovieAndAuthor() should be:
public Movie addMovieAndAuthor(long groupId, String movieName, String description, int rating,
    String authorName, String biography, ServiceContext serviceContext) throws PortalException {

  // add the new movie
  Movie movie = addMovie(groupId, movieName, description, rating, serviceContext);

  // now create the author
  Author author = _authorLocalService.addAuthor(movie.getMovieId(), authorName, biography, serviceContext);

  // set the author back to the movie
  movie.setAuthor(author);

  // nothing else to update, just return the new movie.
  return movie;
}

Now the problem with your JSP though is that you're using MovieLocalServiceUtil.getMovies(long, long) but you haven't overridden it to do the author lookups. You'd need something like the following in MovieLocalServiceImpl:
@Override
public List<movie> getMovies(long startPos, long endPos) {
&nbsp; // first fetch the list of movies
&nbsp; List<movie> movies = super.getMovies(startPos, endPos);

&nbsp; // now need to fetch the authors
&nbsp; if ((movies != null) &amp;&amp; (! movies.isEmpty())) {
&nbsp;   Author author;

&nbsp;   for (Movie movie : movies) {
&nbsp;     // you'll need a finder in AuthorLocalServiceImpl to get the author for the given movie id.
&nbsp;     author = _authorLocalService.getAuthorByMovieId(movie.getMovieId());

&nbsp;     // Note that normally a get method in the service layer will throw one of the NotFound exceptions
&nbsp;     // if there was no record found. So either it will throw one or perhaps you're returning null, either
      // way it may be something you need to deal with.

      // save back to the movie
&nbsp;     movie.setAuthor(author);
&nbsp;   }
&nbsp; }

&nbsp; return movies;
}</movie></movie>

​​​​​​​But this was what I meant when I said you'd have to override the getters in the MovieLocalServiceImpl so that you could fetch and populate authors before returning.
Tiago Machado, modified 5 Years ago.

RE: How to add and display data from 2 tables?

Junior Member Posts: 59 Join Date: 2/21/19 Recent Posts
Thank you again for the answer!

Damn you are an angel! are you single? ;) just kidding emoticon

That did the trick, i just needed to set the author in the AddMovieAndAuthor method, create a finder in the AuthorServiceImpl to get the author by the movie id and, override the getter like you told me, just one thing there, i had to change the long to int or i would get an error on the super telling me that i  cant invoke directly the abstract method.

after that i just needed to change in the JSP where i was telling the porperty, because there is no property with the info i just persisted ofc... so i replaced the property tag with value tag and now i can see the info from the 2 tables.

thanks a lot!!!
Tiago Machado, modified 5 Years ago.

RE: How to add and display data from 2 tables?

Junior Member Posts: 59 Join Date: 2/21/19 Recent Posts
Hey David, i want to go further if possible, is there a way to display the data like i did but in a different way? for example, could i create a model for the view?
or can I implement code to view the data i wish in some portlet action command class and then call it in the jsp without  the search container?

the view model seems good, whenever i would need to view something I would have it there. but I need some documentation, would apreciate your suggestion, as always, if you have some useful link for me to study that and implement it please share with me.

​​​​​​​thank you!!!
thumbnail
David H Nebinger, modified 5 Years ago.

RE: How to add and display data from 2 tables?

Liferay Legend Posts: 14914 Join Date: 9/2/06 Recent Posts
You can build a raw HTML table, if that's what you want to manage on your own.

Liferay tends to use the search container because of the contained functionality; column sorting, pagination, row highlighting, model bean handling, column types like an external JSP page for encapsulate column rendering code, ...

But it is not the only way. Anything you can build using HTML DOM, CSS and JS, you can build into your portlet.

​​​​​​​It just depends how much you want to manage yourself.
Tiago Machado, modified 5 Years ago.

RE: How to add and display data from 2 tables?

Junior Member Posts: 59 Join Date: 2/21/19 Recent Posts
I think I didn't explain well what i wanted to do now.

Search container is pretty good. what I wanted to ask is, if there is another way of showing the data without having to override de getter, is there an option to that? could I create for example a package and there a view model for just displaying all the data isntead of overriding the getter?

or is there any liferay tool to implement a class with code that would provide  the JSP with the data do show, and in the JSP all i needed to do is call the class?
I'm just curious about the alternatives emoticon
thumbnail
David H Nebinger, modified 5 Years ago.

RE: How to add and display data from 2 tables?

Liferay Legend Posts: 14914 Join Date: 9/2/06 Recent Posts
In MovieImpl, you could do something like:
private Author author = null;

public Author getAuthor() {
  if (author == null) {
    author = AuthorLocalServiceUtil.getAuthorByMovieId(getMovieId());
  }

  return author;
}

This was the other option that I had suggested, it doesn't require overrides of all of the getters in the MovieLocalServiceImpl to populate Author.

By caching the value, though, you run the risk of the stale data if someone changes the author after the value had been fetched.

You could not use the caching option, opting instead to always return the fetched value:
public Author getAuthor() {
  return AuthorLocalServiceUtil.getAuthorByMovieId(getMovieId());
}

This avoids the stale data issue, but it means that any time getAuthor() is called it could be a new db retrieval.
Tiago Machado, modified 5 Years ago.

RE: How to add and display data from 2 tables?

Junior Member Posts: 59 Join Date: 2/21/19 Recent Posts
Thank you very much about the info!
I'm searching about it too xD
what if i created classes for the view? or doest make any sense?
i was seeing this post here: https://stackoverflow.com/questions/23648832/viewmodels-in-mvc-mvvm-seperation-of-layers-best-practices
i know that is c# but you get the idea...

a model to view the data only. is that possible here?

​​​​​​​thanks!
thumbnail
David H Nebinger, modified 5 Years ago.

RE: How to add and display data from 2 tables?

Liferay Legend Posts: 14914 Join Date: 9/2/06 Recent Posts
You can construct DTOs, that's up to you. Even using service builder "fake entities", you can create managed DTOs that are not bound to database tables.

It doesn't, however, eliminate the need to pull the DB entities, plus it adds overhead to marshal the entities to/from the DTOs.

Everything comes at a cost, you just have to know how much you're willing to pay.

The referenced post, for example, presents a clean design where changes can be isolated to the various layers of concern. What it excludes is the runtime overhead involved with that level of separation.

For every data retrieve, you marshal to the view model. When your UI populates the view model, you have to marshal back to the entity for persistence requirement. All of this is processed on every retrieve and on every update, and this overhead negatively affects your overall capacity.

So sure, from a maintenance perspective a changed entity can require a UI changes, increasing the cost of the development effort. The problem is that this cost can be measured and evaluated, so it appears to be clear. But that overhead, the loss of capacity, the increased response time with the marshaling effort, all of these things represent an invisible cost that is never measured, evaluated or estimated.

I would argue that the hidden cost far surpasses the apparent cost for a change request.
Tiago Machado, modified 5 Years ago.

RE: How to add and display data from 2 tables?

Junior Member Posts: 59 Join Date: 2/21/19 Recent Posts
thank you very much for the information! you'r a boss.