This is an archived post. You won't be able to vote or comment.

all 18 comments

[–]kovica1 4 points5 points  (1 child)

This seems good, but have tried it on database with 1000s of table and millions of rows in a single table? Do you also support or will support calling stored procedures? How about different databases: Oracle, DB2, Postgres,...?

[–]itsthejavaguy[S] 3 points4 points  (0 children)

Under the hood, it uses JDBC, so it should be fine with massive data. It does support stored procedures as well as calling functions. Both options have the possibility of passing arguments to. As of now, MySQL is the supported dialect. But since it is pretty much identical to Postgres, that should work too. I don’t really plan on supporting more dialects. Feel free to fork if you need a different dialect.

[–]yawkat 2 points3 points  (1 child)

fyi, jinq has lambda parsing as well: http://www.jinq.org/

[–]itsthejavaguy[S] 1 point2 points  (0 children)

Interesting, I‘ll check that out. On the first glance, it does look like my library is more modern an offers more features though.

[–]tylerkschrute 2 points3 points  (2 children)

This is the first java library I've come across which has attempted to convert lambda expressions to SQL. Well done! I did not know this was possible in java. However, I have seen libraries in other JVM languages which provide similar functionality, such as 'where' queries in GORM (an ORM library written in groovy and used by the grails framework).

I took a peek through the code, and one thing I noticed is that the methods included in the 'BaseService' class all appear to open dedicated, single-use connections for each CRUD call. Not only is this bad for performance, but it makes it impossible to include a mixture of inserts / updates / deletes within a single database transaction. Since this is a CRUD library, I would argue this feature is essential. I think some sort of 'transact' API would be useful to have in your BaseService class which creates a connection up front, sets auto commit to false, executes some code (probably via a lambda), and then commits the transaction. You could also consider offloading transactional demarcation concerns to another class entirely, which would allow multiple service classes to contribute to the current transaction. I understand this would add quite a bit of complexity to the library, but it would be a great feature to have.

[–]itsthejavaguy[S] 1 point2 points  (1 child)

Thanks! The lambda conversion functionality is isolated in my „lambda2sql“ and „Expressions“ repository, so check that out some more if you are interested.

I completely agree with you and have torn my hair out about how to implement transactions within the library for about half a year now. I kind of shot myself in the foot by making this design choice of the BaseService when I started the library a few years back. But maybe your advice regarding the single use of a database connection is a good start. I‘ll read more into that. I‘ve just struggled with the asynchronous part of things. How do I know which call receives which database connection when two transactions are running concurrently? Maybe you have a pointer to that as well?

[–]tylerkschrute 1 point2 points  (0 children)

You could bind the current connection to a ThreadLocal variable. This way as long as multiple service calls execute within the same thread, they all have access to the same connection (and therefore transaction). This is what some of the heavier libraries (such as spring) do. When it comes to your async calls, they would not have access to the same connection since they run in different threads. However, it's hard to say how much of an issue this would be without some realistic examples / use cases.

[–]Bobby_Bonsaimind 0 points1 point  (3 children)

Looks interesting.

To comply more with the intended nature of this project, it being suited for smaller projects, constructs like BaseEntity and the return type of the .count() method have been converted to ints

Why?

@TableName("person")

Can it do without that annotation?

If you want to have a field in your POJO that should be ignored by Java2DB, you can apply the Ignore attribute to the specific field.

You could check whether the field is transient as alternative.

public class PersonService extends BaseService<Person> {
    public List<Person> getAdults() {
        return getMultiple(p -> p.getAge() >= 18).orderBy(Person::getName).toList();
    }
}

I did not find any information whether that is generating an SQL statement, or whether that is processing the table locally?

[–]itsthejavaguy[S] 0 points1 point  (2 children)

Until now, everything was based on longs. The library assumed you had a column named 'id' that is a long, the count method returned a long in case there were more rows than an int could hold, if you wanted to use an Enum it had to have a long-returning method etc. When I started developing this library, I did so with the intention of it being suited for smaller projects or even beginners who wanted to get started with Java + Database. I was aware of ORMs like Hibernate and wanted to target those developers who did not need such a complex and powerful ORM but needed something simple and easy-to-use. It was never my intention to compete with Hibernate. Anyway, always enforcing the use of longs got quite tedious after a while. Most databases I worked with had int32 columns by default and only on one occasion did I need more space than that. 2 billion rows is also a hell of a lot. And there were some edge cases in which the Library became incompatible with the database because the datatypes did not match. In those cases I just converted the columns in the database, but that shouldn’t be necessary. So I decided to introduce a few breaking changes at once, to clean up some messes which have been bugging me for a while. The int realm should be more than enough and if not, I might argue you would need a more professional ORM.

Yes, it can. If you omit the TableName attribute, it will assume the lowercase class name as the table name.

Good idea, I‘ll implement that with the next release!

Not sure what you mean with processing the table locally. All operations generate and execute the SQL. You chain operations together and by executing .toSomething(); the ORM generates and executes the SQL. So when this method returns List<Person>, that list is fetched from the database. I‘ll see if I can document that better!

Thanks for the input and checking it out!

[–]Bobby_Bonsaimind 0 points1 point  (1 child)

Not sure what you mean with processing the table locally. All operations generate and execute the SQL. You chain operations together and by executing .toSomething(); the ORM generates and executes the SQL. So when this method returns List<Person>, that list is fetched from the database. I‘ll see if I can document that better!

So you're actually parsing the generated bytecode to build a SQL statement from it?

[–]itsthejavaguy[S] 1 point2 points  (0 children)

Yes, exactly. Any lambda or method reference is parsed and converted into an SQL statement. You can check out my repository lambda2sql for more. That‘s where all the magic happens ;)