So far I like what I found in GLORP.
-- Having built a ORM solution in the past (still being used successfully), I still have a few questions about what is the proper way to use GLORP. 1) Should I create a new GlorpSession everytime I want to read/save/update an object? It is... is a GlorpSession "a window" to the objects persisted in the database. 2) I know some objects can live outside of a UnitOfWork (UOW), but how do they behave when used inside a UOW. 3) If a UnitOfWork fails, does the session remain usable? or should I throw it away? I'm going to use GLORP in the context of a Seaside application (where I can have one glorp session per seaside session) and also independently by means of a globally accessible (i.e. singleton) system object. 4) Can I use the same login object for different database accessors? 5) Can the 31 table name limit be lifted? I know this has to with ORACLE (everybody hates Oracle), but I'm using PostgreSQL, and won't change it for anything else other than something more modern (which is unlikely). 6) Can I map a class name to a column, and an ID to other, and use those two to instantiate an object? In the ORM I made, you have a special mapping that works together with other kind of mapping, that when combined allow you to have a reference to ANY object. I.e., you can have N:M relationships stored in a single table. The tuple would be something like PARENT_CLASS, PARENT_ID, CHILD_CLASS, CHILD_ID. Of course this can't use the underlying database referential integrity. But after several years using it, it hasn't presented any issues. Does exist "the good GLORP user guide" somewhere? :-) So far I like what I see. Regards, -- Esteban. You received this message because you are subscribed to the Google Groups "glorp-group" group. To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email]. To post to this group, send email to [hidden email]. Visit this group at http://groups.google.com/group/glorp-group. For more options, visit https://groups.google.com/groups/opt_out. |
On 9/23/13 5:40 PM, Esteban A. Maringolo wrote:
> So far I like what I found in GLORP. > Having built a ORM solution in the past (still being used > successfully), I still have a few questions about what is the proper > way to use GLORP. > > 1) Should I create a new GlorpSession everytime I want to > read/save/update an object? > It is... is a GlorpSession "a window" to the objects persisted in the > database. Probably not. Creating a new session creates a new descriptor system instance, (I think) a new DatabaseAccessor, and a new, empty cache. The biggest factor is the cache, which prevents you from loading duplicate copies of objects referenced multiple times in an object graph. Depending on your application, it might make sense to start by using one session per user transaction, from data retrieval to data update. > > 2) I know some objects can live outside of a UnitOfWork (UOW), but how > do they behave when used inside a UOW. I don't completely understand what you mean by this. Store (the Cincom Smalltalk source code management application only creates a unit of work as a part of writing the new or changed objects to the database). To look at Store, you can download the Personal Use version of Cincom VisualWorks, load the StoreForPostgres parcel and look at the app. > > 3) If a UnitOfWork fails, does the session remain usable? or should I > throw it away? It should remain usable, but it depends on why the UnitOfWork fails... > > I'm going to use GLORP in the context of a Seaside application (where > I can have one glorp session per seaside session) and also > independently by means of a globally accessible (i.e. singleton) > system object. > > 4) Can I use the same login object for different database accessors? Please explain this question further. > > 5) Can the 31 table name limit be lifted? I know this has to with > ORACLE (everybody hates Oracle), but I'm using PostgreSQL, and won't > change it for anything else other than something more modern (which is > unlikely). Where are you seeing this limit? > > 6) Can I map a class name to a column, and an ID to other, and use > those two to instantiate an object? > In the ORM I made, you have a special mapping that works together with > other kind of mapping, that when combined allow you to have a > reference to ANY object. > I.e., you can have N:M relationships stored in a single table. > The tuple would be something like PARENT_CLASS, PARENT_ID, > CHILD_CLASS, CHILD_ID. > Of course this can't use the underlying database referential > integrity. But after several years using it, it hasn't presented any > issues. exactly this. On the other hand, if your application requires much data at all, this doesn't perform well. > > > > Does exist "the good GLORP user guide" somewhere? :-) Cincom ObjectStudio and Cincom VisualWorks both include a pdf Glorp manual. There is no Glorp best practices manual at this point. Hope this helps you a bit.... Regards, Tom Robinson -- You received this message because you are subscribed to the Google Groups "glorp-group" group. To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email]. To post to this group, send email to [hidden email]. Visit this group at http://groups.google.com/group/glorp-group. For more options, visit https://groups.google.com/groups/opt_out. |
In reply to this post by Esteban A. Maringolo
On 23 September 2013 16:40, Esteban A. Maringolo <[hidden email]> wrote:
Normally you shouldn't. It is a window to the objects, but creating one is relatively expensive. As Tom says, it holds onto a descriptor system and also to a cache. In the model of a client-server application normally you would create a session when the user logs in and discard it when they log out. In a web application you may want to associate one with the user's session. In a really high-volume application where you don't want to keep a per-user cache around you might create and destroy them more frequently. Or more likely, empty the cache and re-use them.
When you begin a unit of work, any objects which you register have copies made of their state. And any objects you read during the unit of work automatically are registered and have copies made. When you commit the unit of work we compare the state of the objects and write out any differences or any new objects. If the new object isn't reachable from another registered object, then you also need to register it. If you roll back the unit of work, the state of the objects reverts to what it was when the copy was made.
It's completely usable. However, if the reason it failed was because of an optimistic locking conflict, so the state of the objects in the session is no longer consistent with the database, you may need to refresh the objects. Or, in a web context you could just throw the session away and then re-start, which is roughly the same as throwing away the entire cache and re-reading. But in general, it's very important to not have references to objects from other sessions. If you do, then you have two objects that Glorp thinks ought to be identical, but they aren't, and much confusion can happen. That's one reason that in a GUI application you rarely discard the session, because you don't want a window open that has a reference to an object that is no longer valid.
I think that's fine, but don't have the code in front of me. But it should be pretty clear from the code for Login, which isn't too complicated.
I think that limit only applies when generating table or sequence names. And that limit might be factored out to the platform class anyway. If it isn't, it ought to be.
You could probably do this with an AdHocMapping if you wanted it to be utterly arbitrary. But such a mapping will be limited, in that it will be both not very efficient and you couldn't use it across a join. That is, you can write expressions like [:each | each parent something attribute = 3] and have that translated to a join expression. You can't do that if Glorp doesn't know the set of possible values to use in the join. But with an AdHocMapping you ought to be able to do pretty much anything, it's just your responsibility to do the work. A good scary example is StoreDescriptorSystem>>descriptorForStoreBlob:class:. There are also some in GlorpTests, which is a good source of examples.
A more limited form that might work across a join would be a ConditionalMapping, where you enumerate the different possibilities so it knows which tables to join. Finally an inheritance mapping does a certain amount of this automatically. That is, if you map to a superclass, you have to have a way to identify the subclass rows. So that's more limited, but reasonably well supported
There are a few bits of documentation, but they are in various different levels of outdatedness :-(
You received this message because you are subscribed to the Google Groups "glorp-group" group. To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email]. To post to this group, send email to [hidden email]. Visit this group at http://groups.google.com/group/glorp-group. For more options, visit https://groups.google.com/groups/opt_out. |
Hi Alan, Tom, First of all, thanks for your responses. I'm developing a system that is not going to be "high demand" but will have around 50 concurrent users. With peaks of REAL concurrence of 20 or so users. Coming through a REST API from a mobile application.
As for the Seaside users, they're going to be a few (around 10) doing backoffice tasks and administration. I have trouble understanding how the interaction between objects read "outside" of a UOW behave with objects read within a UOW, and if such interaction is recommendable.
Seems to me that a UOW is: + a in-memory transaction + a database interaction (insert/update/delete) if it finishes ok based on the object "touched" during the UOW (how is this detected I don't know) New set of questions: 1) let's suppose I have aCustomer, read in aSession outside of a UOW. So it is "detached" (is this the proper word?) from the UOW.
But the user have some "ToMany" relations, i.e. #orders. Is it ok to add or remove orders to such customers inside of a a UOW? E.g.
| customer | customer := session read: Customer where: [:each | each name = 'Customer 1']. session inUnitOfWorkDo: [ | order | order := Order forCustomer: customer.
3 timesRepeat: [order addSampleItem]. customer addOrder: order. ]. 1.a) Is this a good practice? 1.b) Will the order and its items be saved in the database automatically?
2) So, to not be "too detached from reality" (the DB) the session cache should be flushed regularly, otherwise the in-memory cache would diverge from what's in the database and cause conflicts.
3) If I want to be as close as possible to what's in the DB (paying the IO+instantiation penalty) I should work inside a UOW and flush the session cache at the end of each unit of work.
4) Does the framework come with some sort of session/connection pooling? I'm using the Pharo / OpenDBX version of it. 5) How can I get rid of the 31 characters limit of constraints/table names, etc?
I'm already working with my own subclass of PostgresSQLPlatform Regards, Esteban. Esteban A. Maringolo
2013/9/25 Alan Knight <[hidden email]>
You received this message because you are subscribed to the Google Groups "glorp-group" group. To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email]. To post to this group, send email to [hidden email]. Visit this group at http://groups.google.com/group/glorp-group. For more options, visit https://groups.google.com/groups/opt_out. |
On 25 September 2013 19:22, Esteban A. Maringolo <[hidden email]> wrote:
--
A UOW is really just the in-memory transaction. However, if you're not already inside a database transaction, then at the end of the UOW it will begin a transaction, write the objects, then commit. If you're already inside a transaction, then it will just write the objets.
It detects if the objects are touched by comparing their current state against the state they had when they were registered with the unit of work. We don't have a portable write barrier across all Smalltalks, so we rely on the user telling us. If you're in VisualWorks there is a write barrier you can use hooked up to Glorp.
So the important interaction is that if an object was read outside of a UOW, then we don't know anything about its state, and the user has to tell us that this object might get modified.
No. Good practice would be to add the line
session register: customer. before doing anything that affects it.
No.
That's one strategy. There is an optimistic locking mechanism available to find conflicts, but you still might be presenting the user with stale data. You can also refresh individual objects or groups of objects to ensure they're consistent while still retaining object identity. But for REST users, it might be quite reasonable to just empty the cache between every REST request, or to empty it except for very rarely-changing objects that are always used.
Yes. Or work inside a transaction.
There is a basic mechanism for connection pooling at the Glorp level in VisualWorks, I don't know if it was ported. It often works better if you can exploit a database-level mechanism for that. But for REST interactions you might reasonably just maintain your own pool of active sessions, which should be very simple.
Where are you seeing it? I don't know why that should be affecting Postgres. I'd say either try stepping through table creation and see where it's happening, or look for methods with words like maximum and length on their name in the platform hierarchy.
You received this message because you are subscribed to the Google Groups "glorp-group" group. To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email]. To post to this group, send email to [hidden email]. Visit this group at http://groups.google.com/group/glorp-group. For more options, visit https://groups.google.com/groups/opt_out. |
Esteban A. Maringolo
2013/9/26 Alan Knight <[hidden email]> > A UOW is really just the in-memory transaction. However, if you're not already inside a database transaction, then at the end of the UOW it will begin a transaction, write the objects, then commit. If you're already inside a transaction, then it will just write the objets. > > It detects if the objects are touched by comparing their current state against the state they had when they were registered with the unit of work. We don't have a portable write barrier across all Smalltalks, so we rely on the user telling us. If you're in VisualWorks there is a write barrier you can use hooked up to Glorp. Okay, so I should only use UOW if I want to be able to rollback a change made in memory. But if I already have other method (like mementos, buffered copies, etc.) to rollback or verify changes before storing it in the database then I could avoid the use of UOW for normal things like saving a new customer or adding an order to it (or simply saving the order). > So the important interaction is that if an object was read outside of a UOW, then we don't know anything about its state, and the user has to tell us that this object might get modified. Perfectly clear. Couldn't the write barrier be implemented by means of immutability? We did that in Dolphin... >> 1.b) Will the order and its items be saved in the database automatically? > No. But if I read the object inside the UOW then it will. >> 2) So, to not be "too detached from reality" (the DB) the session cache should be flushed regularly, otherwise the in-memory cache would diverge from what's in the database and cause conflicts. > That's one strategy. There is an optimistic locking mechanism available to find conflicts, but you still might be presenting the user with stale data. You can also refresh individual objects or groups of objects to ensure they're consistent while still retaining object identity. But for REST users, it might be quite reasonable to just empty the cache between every REST request, or to empty it except for very rarely-changing objects that are always used. So for a REST API, I should implement a pool of sessions (which in turn could share a pool of connections), and perform a cycle of aquire, use, flush and release on every HTTP request. >> 3) If I want to be as close as possible to what's in the DB (paying the IO+instantiation penalty) I should work inside a UOW and flush the session cache at the end of each unit of work. > Yes. Or work inside a transaction. Isn't the transaction just a "wrapper" to the underlying DB transaction? It is... transaction cycle is: 1. Begin DB Transaction 2. Begin UOW 3. Execute transaction block 4. Commit UOW 5. Commit DB Transaction Is this correct? Or does the transaction does something related with the session cache? >> 4) Does the framework come with some sort of session/connection pooling? >> I'm using the Pharo / OpenDBX version of it. > There is a basic mechanism for connection pooling at the Glorp level in VisualWorks, I don't know if it was ported. It often works better if you can exploit a database-level mechanism for that. But for REST interactions you might reasonably just maintain your own pool of active sessions, which should be very simple. The REST sessions are 100% stateless, so a pool like the mentioned above should do the job. >> 5) How can I get rid of the 31 characters limit of constraints/table names, etc? >> I'm already working with my own subclass of PostgresSQLPlatform > Where are you seeing it? I don't know why that should be affecting Postgres. I'd say either try stepping through table creation and see where it's happening, or look for methods with words like maximum and length on their name in the platform hierarchy. I'm seeing it ForeignKeyConstraint>>#generateName, it referes to the Platform #maximumLengthOfTableName, which in PostgreSQLPlatform is set to 31. I just tested creating a 60+ FK name and it worked okey in PG 9.3. As a side not, can I specify a "ON DELETE CASCADE" in the foreign key definition? Thanks for your prompt responses. Regards, -- You received this message because you are subscribed to the Google Groups "glorp-group" group. To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email]. To post to this group, send email to [hidden email]. Visit this group at http://groups.google.com/group/glorp-group. For more options, visit https://groups.google.com/groups/opt_out. |
Sorry, this fell off the radar. On 26 September 2013 17:25, Esteban A. Maringolo <[hidden email]> wrote: Esteban A. Maringolo Well, sort of. Except there's no save operation, there's only UOW. That's how Glorp expects to work. And UOW does a bunch of other things as well, like ordering and batching writes, which are problems if you work at the level of saving individual objects. Not to mention dealing with back references.
Yes, there's a VW package that does precisely that. But it's not portable. So you'd need a version for whichever dialect(s) you're using. Assuming the immutability has the right semantics.
Yes. Or if you register it before modifying it.
Yes.
Yes, the transaction is just a wrapper. The default cycle is. 1. Begin UOW 2. Execute transaction block.
3. Commit UOW 4. Start DB transaction. 5. Write stuff. 6. Commit DB transaction. That minimizes long database transactions. But if you manually start a DB transaction first then everything will be done within it, and you'll get a read-consistent view, and commit will fail if there's a conflict. But just reading and then flushing works, though you do have to be aware that you might still have someone else's transaction commit between one of your reads and another if you aren't in a transaction, so you might get an inconsistent view.
Note that the caches are class-specific, so you if you have reference data that very rarely changes and doesn't refer back to transient data you can avoid flushing and re-reading it every time.
OK, that may have been an old PG limitation, or it may have just inherited that as a default even though it doesn't apply. If it's actually implemented on PostgreSQLPlatform as 31 it seems like that should be updated.
As a side not, can I specify a "ON DELETE CASCADE" in the foreign key No. The problem with delete cascade is that it doesn't tell us what it's done, so we have no way to update our information. There's an exclusive attribute you can set on a relationship that says to delete the target when the source is deleted, but it's just doing that the slow way. If you're just throwing away your cache every time you could use cascaded deletes in the database, but there isn't any support for specifying them in the table generation.
You received this message because you are subscribed to the Google Groups "glorp-group" group. To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email]. To post to this group, send email to [hidden email]. Visit this group at http://groups.google.com/group/glorp-group. For more options, visit https://groups.google.com/groups/opt_out. |
Free forum by Nabble | Edit this page |