Service Builder Liferay Relationships - many-to-many

I have "Storage" portlet:
I can click "Add Book":
But I need to have opportunity to add author's name to my book. So I created new project, new Service Builder with new entity "author", and now I can add author too. But how can I make dropdown menu with existing authors when I click "add book"? How can I bind that field with new table - "author"?

You don't need to create another project for the author, in the same service builder as the book entity add author entity and add reference to another table that will hold the primary key's of book and author.
service.xml could be:
<service-builder package-path="com.myproject.db">
<author>SO</author>
<namespace>myp</namespace>
<entity name="Book" local-service="true" remote-service="true" cache-enabled="false">
<column name="bookId" type="long" primary="true" />
<column name="name" type="String" />
<column name="description" type="String" />
<column name="price" type="long" />
<column name="author" type="Collection" entity="Author" mapping-table="Author_Books"/>
</entity>
<entity name="Author" local-service="true" remote-service="true" cache-enabled="false">
<column name="authorId" type="long" primary="true" />
<column name="authorName" type="String" />
<column name="books" type="Collection" entity="Book" mapping-table="Author_Books" />
</entity>
</service-builder>
And when you deploy this portlet it will create another table myp_Author_Books that will have composite key of authorId and bookId.
Than in your rendering method of book form add
List<Author> authorList=AuthorLocalServiceUtil.getAuthor(0, AuthorLocalServiceUtil.getAuthorCount());
renderRequest.addAttribute("listAuthor",authorList )
and use this attribute in jsp with c:forEach and aui:select to create your dropdown, smth like:
<aui:select name="author" label="Author Name">
<c:forEach var="currAuthor" items="${listAuthor}">
<aui:option value="${currAuthor.authorId}" label=" ${student.authorName}"></aui:option>
</c:forEach>
</aui:select>

Related

HTML template in xml - what kind of architecture is this?

My project has many xml files which are using to build html page & page operations. Here is the sample of grid template.
<Contact singular="Contact" indeal="" nodeal="ContactsPlaybook" tooltip="Document Playbook" library="true" tabHidden="true">
<ListingScreen handle = "PlaybookContacts.ashx" suppressCount="true" showFilters="true">
<IncludeScript src="scripts/jjedsEmaUser.js"/>
<SmartIcon editMode="false" requiredAction="Create" name="new" image="new.png" tooltip="Create" separator="false" href="PlaybookContactDetail.ashx?DealRef=${DealRef}" edit="true" />
<SmartIcon editMode="false" requiredAction="Delete" name="BulkDelete" image="delete.png" tooltip="Delete" separator="true"/>
<SmartIcon editMode="false" requiredAction="Read" actionOn="Contact" name="email" image="mail.png" tooltip="Email Team" href="PlaybookEmailTeam.ashx?DealRef=${DealRef}&Subject=Playbook&Body=${LinkToPage}" edit="true" />
<SmartIcon editMode="false" requiredAction="Read" name="print" image="print.png" tooltip="Print" onclick="javascript:window.print()" />
<!--<SmartIcon editMode="false" requiredAction="Administrate" name="CreateEmaUser" image="add_EMA_user.png" tooltip="Create EMA user" onclick="return emans.jjedsEmaUser.create('${DealRef}', '#ListingForm')" />-->
<Filters>
<Filter label="Functional Team" filter="FunctionalCategoryFilter" field="ContactGroupID" prefix="C" empty="FunctionalCategoryRef_NULL">
<PossibleValues displayProperty="Name" />
</Filter>
<Filter label="Country" filter="CountryFilter" by="name" prefix="AD" field="CountryID" displayProperty="Code" empty="-1">
</Filter>
<Filter label="Business Unit" filter="PickListIntFilter" field="BusinessUnit" prefix="C" empty="-1" onlyifsettingtrue="UseSpecialUserDealAccess">
<PossibleValues category="Deal" subcategory="BusinessUnit" />
</Filter>
</Filters>
<Sorting>
<SortColumn name="FullName" dir="asc"/>
</Sorting>
<Query alias="C" ignoreArchiving="true" ignoreDeal="true">
<Block by="C.ContactGroupID" resolveto="DepartmentName" as="Department" />
<JoinTo table="Address" alias="AD" from="C.AddressID" to="AD.AddressID">
</JoinTo>
<Constraint left="C.IsArchived" int="0" />
</Query>
<Column command="true" title="<input type='checkbox' header='true' onclick='ToggleCheckAll(this);'>" editMode="false" special="IsDelete" macro="Checkbox" onClick="ToggleCheckBox(this);"/>
<Column command="true" requiredAction="Update" title="" field="Blank" dbColumn="C.ContactID" macro="ImageLink" fieldType="Contact" tooltip="Edit" image="edit.png" edit="true" />
<Column title="Full Name" field="FullName" macro="LinkToRef" resolveto="FullContactName" from="C.ContactID" contactAlias="C" linkPage="PlaybookContactDetail.ashx"/>
<Column title="Organization" field="C.Affiliation" macro="Text" />
<Column title="Business Unit" field="C.BusinessUnit" property="BusinessUnit" macro="PickList" category="Deal" subcategory="BusinessUnit" storeInt="true" onlyifsettingtrue="UseSpecialUserDealAccess"/>
<Column title="Role" field="C.Role" macro="Text" />
<Column title="Phone" field="C.Phone" macro="Text" />
<Column title="Email" field="C.Email" macro="MailToRef" />
<DeleteDialog name="ContactDelete" info="If you really want to delete {0} please choose the contact which will be used instead."
title="Confirm delete" type="ContactDeleteDialog" >
</DeleteDialog>
</ListingScreen>
</Contact>
Can anyone tell me what kind of architecture is this and what is the real benefit of using this architecture?
It is used in Single page applications to populate the page using an ajax call from the browser to XML files/JSON files in the server thus avoiding reloading of the entire page.
Look into this example
https://www.w3schools.com/js/tryit.asp?filename=tryjs_ajax_xml2
Here on clicking the button the table gets loaded with the xml data from cd_catalog.xml
https://www.w3schools.com/js/cd_catalog.xml
Its architechture is similar to HTML in the way that both are markup languages.
The data is accessed using the nested structure of the tags.In the cd.catalog example the title column is accessed as catalog->cd->title.

Bulk Database processing in liferay

I need to insert 200k records into MySQL database, using liferay service builder.
Currently it is taking 20+ minutes to process.
Is there a way to optimize this? I want to reduce processing time by 40-50%
Bulk processing and multiple values insertion is not feasible as it will require changes in configuration.
Below is the service.xml file
<entity name="table1"
table="iguv_table1" data-source="defaultdbDataSource"
local-service="true" remote-service="false" session-factory="defaultdbSessionFactory"
tx-manager="defaultdbTransactionManager">
<!-- PK Fields -->
<column name="orderNo" type="String" primary="true" />
<!-- Group instance -->
<column name="groupId" type="long" />
<!-- Audit Fields -->
<column name="companyId" type="long" />
<column name="createrUserId" type="long" />
<column name="createDate" type="Date" />
<column name="lastUpdaterUserId" type="long" />
<column name="modifiedDate" type="Date" />
<column name="approverUserId" type="long" />
<!-- Other Fields -->
<column name="rmsOrderNo" type="String" />
<column name="supplier" type="String" />
<column name="currencyCode" type="String" />
<column name="terms" type="String" />
<column name="notBeforeDate" type="Date" />
<column name="notAfterDate" type="Date" />
<column name="otbEowDate" type="Date" />
<column name="dept" type="int" />
<column name="status" type="String" />
<column name="exchangeRate" type="double" />
<column name="includeOnOrdInd" type="String" />
<column name="orderDetailsId" type="String" />
<column name="origInd" type="String" />
<column name="ediPoInd" type="String" />
<column name="preMarkInd" type="String" />
<column name="supplierName" type="String" />
<column name="hasNewItems" type="boolean" />
<column name="retryCount" type="int"></column>
<column name="successfullyPostedToRMS" type="boolean" convert-null="false"></column>
<finder name="rmsOrderNo" return-type="PurchaseOrder">
<finder-column name="rmsOrderNo" />
</finder>
<finder name="notSyncedPurchaseOrders" return-type="Collection">
<finder-column name="successfullyPostedToRMS"></finder-column>
<finder-column name="status"></finder-column>
</finder>
</entity>

Multiple one-to-many relationships to same entity type and table?

I have an interesting use-case where I'd like Hibernate to manage multiple one-to-many relationships to the same entity type.
For example: BookShelf fictionBooks relationship to Book(s), but also BookShelf nonFictionBooks mapped to Book(s). The Hibernate mapping would look something like this:
<class name="com.example.BookStore" table="BOOK_SHELF">
<id name="id" type="long" column="bookShelfId">
<generator class="native" />
</id>
<set name="fictionBooks" table="SHELF_BOOK" cascade="all-delete-orphan" lazy="false">
<key column="bookShelfId" />
<one-to-many class="com.example.Book" />
</set>
<set name="nonFictionBooks" table="SHELF_BOOK" cascade="all-delete-orphan" lazy="false">
<key column="bookShelfId" />
<one-to-many class="com.example.Book" />
</set>
</class>
<class name="com.example.Book" table="SHELF_BOOK">
<id name="id" type="long" column="shelfBookId">
<generator class="native" />
</id>
<property name="name" not-null="true" unique="true"/>
</class>
Is there a way for the relationship owner BookShelf to specify some discriminator value which could be used to differentiate between Fiction and Non-Fiction books? If possible, the discriminator would be stored as an additional column in SHELF_BOOK table and Hibernate would automatically filter on that.
Is there a way to do this without resorting to either a many-to-many association or extending the Book entity with a Table per class strategy?
Ideally you should have a "type" or "flag" column in SHELF_BOOK table indicating the book is fiction or non-fiction.
Suppose you have added this "type" column, then I think you could specify a filter statement in the set:
<set name="fictionBooks" table="SHELF_BOOK" cascade="all-delete-orphan" lazy="false">
<filter name="myfilter" condition=":type = 'FICTION'"/>
<key column="bookShelfId" />
<one-to-many class="com.example.Book" />
</set>
You can refer to http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html_single/#objectstate-filters
From what you posted, I can say that in order to achieve what you wanted you need to modify your relationship owner BookShelf to only store reference to Book and add the property, say bookType, to Book entity.
<class name="com.example.BookStore" table="BOOK_SHELF">
<id name="id" type="long" column="bookShelfId">
<generator class="native" />
</id>
<set name="books" table="SHELF_BOOK" cascade="all-delete-orphan" lazy="false">
<key column="bookShelfId" />
<one-to-many class="com.example.Book" />
</set>
</class>
<class name="com.example.Book" table="SHELF_BOOK">
<id name="id" type="long" column="shelfBookId">
<generator class="native" />
</id>
<property name="name" not-null="true" unique="true"/>
<property name="bookType" not-null="true"/>
</class>
There is no other(except ManytoMany) way by which you can find out the type of book by looking into BookShelf entity. You can also use Single Table Strategy which will automatically add the discriminator to the inserted values but in order to do that you need to create two separate classes for FictionalBook and NonFictionalBook .

Linq to SQL Foreign Keys & Collections

I'm a still trying to wrap myself around LINQ to SQL and foreign key relationships.
I have a table called "Parent" and another called "Children". I have a OneToMany relationship, where a Parent can have multiple Children. My DBML looks something like:
<Table Name="" Member="Parents">
<Type Name="Parent">
<Column Member="ParentID" Type="System.String" IsPrimaryKey="true" CanBeNull="false" />
<Column Member="ChildID" Type="System.String" CanBeNull="false" />
<Association Name="Parent_Child" Member="Childs" ThisKey="ParentID" OtherKey="ParentID" Type="Child" />
</Type>
</Table>
<Table Name="" Member="Childs">
<Type Name="Child">
<Column Member="ChildID" Type="System.String" IsPrimaryKey="true" CanBeNull="false" />
<Column Member="ParentID" Type="System.String" CanBeNull="false" />
<Association Name="Parent_Child" Member="Parent" ThisKey="ParentID" OtherKey="ParentID" Type="Parent" IsForeignKey="true" />
</Type>
</Table>
In my code, I would like do to something like:
// parent has already been loaded from the datacontext
parent.Childs = <some collection of children>
db.SubmitChanges();
But when I do that I get the following error:
A member defining the identity of the object cannot be changed.
Consider adding a new object with new identity and deleting the existing one instead.
Can anyone tell me how to properly accomplish this?
this is actually the error of datacontext,i think u have multiple instance of datacontext..
well u can not add any entity in datacontext witch is already fetched through another instance of datacontext...
even u can so it by setting datacontext.objecttrackingenabled to false and then u can add it to another data context then it will work for sure....

hibernate many-to-many association and spring hibernatetemplate doesn't work

I am using Spring HibernateTemplate, OpenSessionInViewFilter(actually I extended this class and created my own to switch to FLUSH.AUTO Mode) and Mysql for implementing hibernate many-to-many association. However when I save an object, corresponding many-to-many table's values are not inserted. Does anybody can help me? Thank you.
here is the mapping xml
<hibernate-mapping>
<class name="com.intelli.epub.domain.Content" table="CONTENT">
<id name="id" type="java.lang.Long">
<column name="ID" />
<generator class="native" />
</id>
<property name="title" type="java.lang.String">
<column name="TITLE" />
</property>
<property name="text" type="java.lang.String">
<column name="TEXT" />
</property>
<many-to-one name="writer" class="com.intelli.epub.domain.User" fetch="join">
<column name="WRITER" />
</many-to-one>
<property name="createdDate" type="java.util.Date">
<column name="CREATEDDATE" />
</property>
<set name="menus" table="MENU_CONTENT" cascade="all">
<key column="CONTENT_ID"></key>
<many-to-many column="MENU_ID" class="com.intelli.epub.domain.Menu"/>
</set>
</class>
</hibernate-mapping>
another one:
<hibernate-mapping>
<class name="com.intelli.epub.domain.Menu" table="MENU">
<id name="id" type="java.lang.Long">
<column name="ID" />
<generator class="native" />
</id>
<property name="text" type="java.lang.String">
<column name="TEXT" />
</property>
<set name="contents" table="MENU_CONTENT" inverse="true">
<key column="MENU_ID"></key>
<many-to-many column="CONTENT_ID" class="com.intelli.epub.domain.Content"/>
</set>
</class>
and when saving like this:
Content content = new Content();
content.setCreatedDate(new Date());
content.setWriter(some user here);
content.setText("some text here");
Menu menu1 = new Menu("menu1");
Menu menu2 = new Menu("menu2");
Set<Menu> menus = new HashSet();
menus.add(menu1);
menus.add(menu2);
content.setMenus(menus);
contentDao.saveOrUpdate(content);
Now menu1 and menu2 would be saved in the MENU table, However nothing happens to MENU_CONTENT table; MENU_CONTENT table doesn't have a primary key field, instead MENU_ID and CONTENT_ID are primary key together. I don't know if it's the problem. Please help me. Thank you.
I found a solution. Instead of using Spring HibernateTemplate. I wrapped it in regular session and transaction like this.
Session session = contentDao.getHibernateTemplate().getSessionFactory().getCurrentSession();
transaction = session.beginTransaction();
Content content = new Content();
content.setCreatedDate(new Date());
content.setWriter(some user here);
content.setText("some text here");
Menu menu1 = new Menu("menu1");
Menu menu2 = new Menu("menu2");
Set<Menu> menus = new HashSet();
menus.add(menu1);
menus.add(menu2);
content.setMenus(menus);
session.save(content);
transaction.commit();
session.close();
And here is my session filter which inherited from OpenSessionInViewFilter
public class SessionFilter extends OpenSessionInViewFilter {
protected Session getSession(SessionFactory sessionFactory)
throws DataAccessResourceFailureException {
Session session = super.getSession(sessionFactory);
session.setFlushMode(FlushMode.AUTO);
return session;
}
}
Does anybody know a way to handle this without bothering to write session management myself?