Foros de discusión

Bypassing inheritance with servicebuilder

thumbnail
Jakub Liska, modificado hace 13 años.

Bypassing inheritance with servicebuilder

Regular Member Mensajes: 187 Fecha de incorporación: 25/03/10 Mensajes recientes
Could please anybody explain his way of dealing with absence of class / entity inheritance using this MDA style of development with servicebuilder ? I mean, for developers that are not used to it, it is really hard to create domain model from : Account -> CreditAccount / CashAccount -> CustomerAccout / ProviderAccount ... that would be otherwise done via inheritance processed by orm framework.

One possible option is to create a Single table with a discriminator column that would be always tested to differentiate entities. Or create all these entities, etc. There are a few more ways, but none of them seems good to me.

Thanks in advance, Jakub
thumbnail
Tomas Polesovsky, modificado hace 13 años.

RE: Bypassing inheritance with servicebuilder

Liferay Master Mensajes: 676 Fecha de incorporación: 13/02/09 Mensajes recientes
Hi Jakub

Jakub Liska:

One possible option is to create a Single table with a discriminator column that would be always tested to differentiate entities. Or create all these entities, etc. There are a few more ways, but none of them seems good to me.


Try to look at Hibernate Inheritance. These are standard solutions for ORM inheritance. If you don't use object DB you need to decide which strategy you want to use.

-- tom
thumbnail
Jakub Liska, modificado hace 13 años.

RE: Bypassing inheritance with servicebuilder

Regular Member Mensajes: 187 Fecha de incorporación: 25/03/10 Mensajes recientes
Hi Tomas,

I'm aware of the Hibernate way of doing this, but it seems to me that one must choose between doing plugins either with ServiceBuilder or use Hibernate without ServiceBuilder... Afaik - which is not much right now :-), combining these two approaches doesn't seem to be easy.
thumbnail
Jakub Liska, modificado hace 13 años.

RE: Bypassing inheritance with servicebuilder

Regular Member Mensajes: 187 Fecha de incorporación: 25/03/10 Mensajes recientes
I mean, If I'm using ServiceBuilder and I have a hierarchical entity that would have to be declared like this , I can't create additional portlet-hbm.xml definition, can I ? Moreover it would be impossible due to the other relevant entity relationships declared in service.xml. Then I would have the entity declared in Hibernate, but persistence layer created by serviceBuilder wouldn't be aware of it, because I couldn't declare the entity in service.xml.

I hope it makes sense. I bet that after some time when I read this flow of my mind I'll be embarrassed emoticon
thumbnail
Tomas Polesovsky, modificado hace 13 años.

RE: Bypassing inheritance with servicebuilder

Liferay Master Mensajes: 676 Fecha de incorporación: 13/02/09 Mensajes recientes
Yes, it does make sense.

To be honest, on some projects we decided to drop Service Builder because of its limitations and we use standard Spring Portlet MVC - Hibernate couple. And sometimes we use both emoticon

You can definitely create additional portlet-hbm.xml definition. Look into your /META-INF/hibernate-spring.xml file (generated by Service Builder). There is specification of PortletHibernateConfiguration class. This class is responsible of loading the config file:
public class PortletHibernateConfiguration
	extends PortalHibernateConfiguration {

	protected ClassLoader getConfigurationClassLoader() {
		return PortletClassLoaderUtil.getClassLoader();
	}

	protected String[] getConfigurationResources() {
		return new String[] {"META-INF/portlet-hbm.xml"};
	}

}


So just override this spring definition using your own class. I'm sure you now know how to continue emoticon

Just keep in mind that everything you write into the hibernate configuration files won't be managed by Service Builder. It's up to you whether this is disadvantage or advantage emoticon

-- tom
thumbnail
Jakub Liska, modificado hace 13 años.

RE: Bypassing inheritance with servicebuilder

Regular Member Mensajes: 187 Fecha de incorporación: 25/03/10 Mensajes recientes
Cool :-) I gave it a thought and it doesn't look so impossible now. What bugs me is lack of one2one support

and

this issue

Do you have a workaround for many2many linking table having additional fields Tomas ?
thumbnail
Tomas Polesovsky, modificado hace 13 años.

RE: Bypassing inheritance with servicebuilder

Liferay Master Mensajes: 676 Fecha de incorporación: 13/02/09 Mensajes recientes
Hi Jakub,

by many2many you mean tables:
A <--> A2B <--> B

where A2B table has additional columns? I think I don't understand - why I should need any workaround? Just to create entity for A2B and reference it from A and B doesn't work?

-- tom
thumbnail
Jakub Liska, modificado hace 13 años.

RE: Bypassing inheritance with servicebuilder

Regular Member Mensajes: 187 Fecha de incorporación: 25/03/10 Mensajes recientes
Hi Tomas,

I meant it regarding service builder. Because service builder doesn't support A2B table that has additional columns. And it's a shame because it would be taken care of many sql join queries etc., that I have to do manually now...
thumbnail
Jakub Liska, modificado hace 13 años.

RE: Bypassing inheritance with servicebuilder

Regular Member Mensajes: 187 Fecha de incorporación: 25/03/10 Mensajes recientes
Look, If you define the m-n mapping table in service.xml definition (the way it should be done in serviceBuilder style) and you define the mapping entity extra in service.xml with all the additional attributes, then mapping table entity source code is generated, and even all DAO methods in those 2 entities regarding m-n relationship are generated.

there are only two flaws

1. sql DDL script must be modified then to create the mapping table as you want

2. in persistenceImpl of those two m-n entities, all those methods regarding m-n (containsEntity, removeEntity, setEntity etc.) are not influenced by presence of the additional attributes in mapping table, except addEntity, that adds an association between the entity1 and the entity2, because there is

"INSERT INTO Mapping_table (entity1ID, entity2ID) VALUES (?, ?)"


this would need to be generated different by serviceBuilder, that would have to consider the fact that there are more attributes and generate something like

"INSERT INTO Mapping_table (entity1ID, entity2ID, myAttribute) VALUES (?, ?, ?)"


It would be a nice feature request/suggestion.....
thumbnail
Tomas Polesovsky, modificado hace 13 años.

RE: Bypassing inheritance with servicebuilder

Liferay Master Mensajes: 676 Fecha de incorporación: 13/02/09 Mensajes recientes
Hi Jakub.

I'm sorry for my delay (too much tired and have no time for the forum).

That looks great. Try to discuss it in suggestions forum and it would be great in the end to create ticket (together with 1:1 mapping problem), so as it can be available in next LFR release.

Thanks.

-- tom
thumbnail
Jakub Liska, modificado hace 13 años.

RE: Bypassing inheritance with servicebuilder

Regular Member Mensajes: 187 Fecha de incorporación: 25/03/10 Mensajes recientes
Hi Tomas,

it's here ... I forget about it a little bit
thumbnail
Tomas Polesovsky, modificado hace 13 años.

RE: Bypassing inheritance with servicebuilder

Liferay Master Mensajes: 676 Fecha de incorporación: 13/02/09 Mensajes recientes
Jakub - great!

Thanks.

-- tom
MICHAIL MOUDATSOS, modificado hace 11 años.

RE: Bypassing inheritance with servicebuilder

Regular Member Mensajes: 110 Fecha de incorporación: 4/10/11 Mensajes recientes
Tomáš Polešovský:

You can definitely create additional portlet-hbm.xml definition. Look into your /META-INF/hibernate-spring.xml file (generated by Service Builder). There is specification of PortletHibernateConfiguration class. This class is responsible of loading the config file:
public class PortletHibernateConfiguration
	extends PortalHibernateConfiguration {

	protected ClassLoader getConfigurationClassLoader() {
		return PortletClassLoaderUtil.getClassLoader();
	}

	protected String[] getConfigurationResources() {
		return new String[] {"META-INF/portlet-hbm.xml"};
	}

}


So just override this spring definition using your own class
...

If I understood correctly, the proposition here is to replace PortletHibernateConfiguration with out own implementation. Right?
If so, I can only assume that this could be done with META-INF/ext-spring.xml, or by replacing the entry right in the META-INF/hibernate-spring.xml (it will be overwritten at subsequent build-service). Anyway let's say we did place our own implementation: what about PortletHibernateConfiguration's and PortalHibernateConfiguration's functionality? From my minimal understanding, I assume it will be lost and therefore something will not work correctly.

On the other hand the statement
Tomáš Polešovský:
So just override this spring definition using your own class.
would make perfect sense if we could extend PortletHibernateConfiguration and override etConfigurationResources(), but we can't because it's in portal-impl.jar (Yeah I know, we can use an ext-plugin, I'm trying not to resort to that at the moment emoticon).

Finally, there's always the promising definition of META-INF/ext-hbm.xml, in portal.properties, but I'm not sure how exactly I'm going to combine this with spring. From plain-hibernate point of view I'm missing the way to add a mapping of a class. I assume I could use my own hibernate.cfg.xml but I'm afraid there will be ramification on the functionality of 'liferaySessionFactory' bean, defined in META-INF/hibernate-spring.xml.

Having mentioned that by the way, could there be a way to fetch all the configuration values of the PortletHibernateConfiguration, somehow? It would be cool, since it would relieve one from the burden to maintain this info twice, in portal and portlet level (assuming everything else worked haha!) EDIT: Could we use com.liferay.portal.kernel.util.PropsUtil? Is PropsUtil.getProperties("jdbc.default.", true);enough?
Of course that would raise yet another issue, that of controlling the load order of apps in the application server: liferay should load before our portlet so that if there was a way to fetch the configuration, it should be performed after the information would be available. I'm not sure but I think that, in LR 6.0.6 that was taken care in tomcat 6 bundle by setting
<host ... deployxml="false"></host>
in tomcat/conf/server.xml, but I don't see this defined in LR 6.1.1 tomcat 7 bundle and I'm no app-server expert, so I can only expect trouble there...

P.S. I liked your former avatar better emoticon
MICHAIL MOUDATSOS, modificado hace 11 años.

RE: Bypassing inheritance with servicebuilder

Regular Member Mensajes: 110 Fecha de incorporación: 4/10/11 Mensajes recientes
MICHAIL MOUDATSOS:
but we can't because it's in portal-impl.jar (Yeah I know, we can use an ext-plugin, I'm trying not to resort to that at the moment emoticon).
...
From plain-hibernate point of view I'm missing the way to add a mapping of a class.
...
P.S. I liked your former avatar better emoticon
For anyone that might be interested in this here's a perspective. Not very elegant but works. Concerning my quotes:

1) I had to use an ext-plugin to override PortletHibernateConfiguration class in portal-impl project. I essentially added my custom hbm.xml there, let's say META-INF/portlet-ext-hbm.xml. Actually, what I did, was to read from portal-ext.properties a custom property let's say hibernate.configs.portlet where a list of included hbm.xml-like definitions should be listed, in order to make it more elegant and customizable. For example I listed in that property both the original META-INF/portlet-hbm.xml and my custom portlet-ext-hbm.xml (comma separated). If no such property existed I used the default configuration (only META-INF/portlet-hbm.xml).

There! I finally had a way to extend the hibernate entity mappings generated by service builder. As an added bonus I could use any inheritance strategy I wanted and even define multiple levels of inheritance, not to mention inter-entity mapping associations (Oh yeah!)

2)While testing this, I figured out that I didn't even have to explicitly declare this mapping anywhere else (that is, I didn't have to do a mapping such as the one done in session-factory configuration) It just worked!

"OK..." one would say, "How did you figure out that it worked?" Here comes the interesting part...

Since I had my entities defined, I wanted a session to test against. So in a LocalServiceUtil (practically in xxxImpl) I fetched the EntityPersistence and opened a session. That was it! I could do a session.save()! But I got greedy and wanted more! I tried the EntityLocalServiceUtil.update(), merely by passing my extended, hibernate mapped, object and it worked! It saved all its data (both the service builder defined part and my own extended one even in a multi-level inheritance scheme using joined-subclass definitions!) "How cool!" I thought

For the get part I just had to call session.get() and pass the extended Entity's Class along with the identifier. It worked like a charm! I could even make a LocalServiceUtil method where I passed the identifier along with a Class<?> argument. Of course,I had to return the service builder's model interface, whose implementation constituted the base of my inheritance, but that incorporated no problems since my own classes already implemented that interface thanks to inheritance! So the only implication was that I had to typecast the result to the class I already gave as a parameter, but this is nothing unusual, when fetching objects with get() anyway!

The only tricky part was how to get all the entities (or better how to do a [hql] query) using this approach. Iwanted to make a service method to perform this task, such as EntityLocalServiceutil.getEnttiies(), so I browsed through code to see how it was done in the generated code and figured out that an HQL query was done. "OK" I said, "I'm going to do the same!". Alas! "Not so fast pal!". One negligible pitfall here is that in META-INF/portlet-hbm.xml definition there was an auto-import="false" configuration, which I chose to follow in my own mapping file, thus I had to use fully qualified Entity names in HQL queries to avoid the relative errors. But as I said, that was the least of my problems. When it came down to perform the query I tried to use what generated code did, i.e., run
com.liferay.portal.kernel.dao.orm.QueryUtil.list(com.liferay.portal.kernel.dao.orm.Query query, com.liferay.portal.kernel.dao.orm.Dialect dialect, int start, int end)
I provided my query, all pos for start and end, and for the dialect I typed my fetched entityPersistence.get... ...and where is the getDialect() method? It was not defined in the interfaces contract!

I assumed my EntityPersistence interface referrence was actually a EntityPersistenceImpl object but no! Typecasting yielded errors since it was some Proxy... I meticulously browsed through the exposed methods, using reflection and there it was! So I fetched the method using reflection and invoked it on the proxy object, got my dialect and ran my query. I know... "Yaiks...!". It couldn't have been less elegant! But it did the job... If any one knows a more robust and correct way to do that, please share it! Oh, I forgot to say that I was able to return a List<?> as a method result, which is what QueryUtil.list(...) returns too, so I could cast my method's result to any inherited class for which the method was called (Of course I had passed the Class<?> which I was querying against)

All the above pretty much cover the majority of stuff one would want to do with his/her extended entities.

3) Tomáš, put your former avatar back! (I believe I express the sentiments of many community members emoticon) Maybe, it was not that Enterprise-friendly, but it is more fun (and probably more community-friendly emoticon )!