Spring-data-jdbc - How to set schema in many-to-many relationship - spring-boot

I am following the spring.io blog here: https://spring.io/blog/2018/09/24/spring-data-jdbc-references-and-aggregates and have a sample SpringBoot app with Book and Author entities here: https://github.com/b-sridhar/spring-data-jdbc-example.
I get the following error: No value supplied for the SQL parameter 'demobook': No value registered for key 'demobook'
And while debugging noticed the SQL query that is executed is: INSERT INTO demo.book_author (author, demo.book) VALUES (:author, :demobook) which should have been INSERT INTO demo.book_author (author, book) VALUES (:author, :book). This happens because I have the #Table("demo.book") as the annotation for the Book entity. If I remove the demo schema in the table name then the tests in BookAndAuthorsTests go through fine.
How to set the schema for the table Book?

This is a bug in the 1.0.x release. It is fixed for 1.1.
If you upgrade your spring-boot-starter-parent to 2.2.0.M5 you'll get the spring-data-jdbc version 1.1.0.RC2 which contains the fix.
With that fix table names in #Table annotations do work as well as the approach of having the NamingStrategy return a schema.
Note: that your configuration then needs to extend AbstractJdbcConfiguration instead of JdbcConfiguration.
Another note: Your example project then chokes because the AuthorRef needs an explicit mapping to the table BOOK_AUTHOR.
I'll leave a PR in a second.

Related

Entity Framework 6 and Oracle: The table/view does not have a primary key defined. The Entity is read-only

I have an ASP.NET Core application that uses EF6 for dealing with a third-party application's database.
Everything is working as expected, but I'm unable to insert rows into a joining table.
I have two tables, Users and Groups, and a joining table GroupUser that identifies which users are members of which groups. Users has a PK of UserId, and Groups has a PK of GroupId.
GroupUser has only 3 columns: GroupId, UserId and another column (which is irrelevant for this post). The two foreign keys in this table identify a unique record.
Every time I try to insert into GroupUser, I get the inner exception
The table/view does not have a primary key defined. The entity is read-only
The error is correct. There is no PK, but both of the FKs are marked as keys in the model. Shouldn't VS be able to use those as a PK somehow?
The inserts used to work as some point, but required some manual modification of the .edmx file as XML in order to work. Unfortunately, our version control records containing this modification have been lost (and I wasn't the one originally working on this).
I've looked at and tried about a dozen articles around this, but they generally have to do with views instead of tables, so don't seem applicable to my case. The ones that did seem applicable didn't solve the issue.
The only other clue I have for a solution is this comment I found in the code:
// Important note: If you have updated the edmx file in the [redacted]
// project and suddenly start having problems, the edmx file may need to be
// edited as an xml file so that you can make changes necessary to make
// VS believe that the GroupUser table has a primary key. See revision #[redacted]
I'm able to insert into User and Group tables just fine, and as I've said, I don't have access to the revision log mentioned.
Edit: The database is for a third-party application, and unfortunately, it's not as simple as just modifying the table to add a PK. I wish it was. Problem would be solved. But I've been advised by the vendor not to make this change, as it may have unexpected consequences, and would void our support.
How can I 'trick' EF into thinking the table has a key? I'm also open to other workarounds. Modifying the DB structure is currently out of the question.

Is it necessary to use Entity annotation for Select query records from Database

I have spring boot application with JPA and MySQL. The table is already created and data are present. From the spring boot application, we need to create two API - get a particular record using id and get all the records. I created a model class but confused with usage of #Entity. Because I'm not going to insert/delete a record from the database. I want to use only for select the record.
For Select query (findBy), do we need to use #Entity annotation at the top of model class?
Yes, you would need to use #Entity to mark your model class, this tells JPA that your class is used for mapping to and from a database table. If you want to make sure that you don't accidentally overwrite the data, you could implement a check via #EntityListener, as described in this answer.

Using Oracle's GUID()-generated ID's in Grails/Hibernate

I trying to use Grails Scaffolding to throw a quick CRUD application together around some legacy database tables. It is an Oracle database, and the primary key value is intended to be populated by Oracle's GUID() function.
Based on this earlier StackOverflow question, I tried specifying "guid" as the Hibernate generator for this column in my Grails domain class:
...
static mapping = {
table name: "OWNER"
version false
columns {
id column: "OWNER_OID", generator: "guid"
name column: "NAME"
...
}
}
...
When I run my Grails app, viewing and even editing records works just fine. However, when I try to create a new record, things blow up with the Oracle error message "ORA-02289: sequence does not exist".
I enabled SQL logging for my datasource, and see Grails/Hibernate trying to execute the following during a save operation:
select hibernate_sequence.nextval from dual
This doesn't look right at all, and doesn't match the generated SQL from that earlier StackOverflow question linked above. Does anyone see something I am missing here, or otherwise know how to make Grails/Hibernate populate a primary key column with Oracle GUID values?
Whew... after another day of wrestling with this, I think I have my arms around the thing. This answer covers a bit more ground than the original question description, but that's because I found yet more problems after getting past the Hibernate generator issue.
Issue #1: Getting an Oracle GUID() value
As covered by Adam Hawkes' answer, the "guid" Hibernate generator is unmaintained and only works for older versions of the Oracle dialect.
However, if you use the Hibernate generator "assigned" (meaning that you want to set primary keys manually rather than have Hibernate auto-generate them), then you can insert values pulled from an Oracle SYS_GUID() call.
Even though Hibernate's newer Oracle dialects don't support "guid" seamlessly, they still understand the SQL necessary to generate these values. If you are inside of a Controller, you can fetch that SQL query with the following:
String guidSQL = grailsApplication.getMainContext().sessionFactory.getDialect().getSelectGUIDString()
If you are inside of a domain class instead, you can still do this... but you will need to first inject a reference to grailsApplication. You probably want to do this in a Controller, though... more on this below.
If you're curious, the actual String returned here (for Oracle) is:
select rawtohex(sys_guid()) from dual
You can execute this SQL and fetch the generated ID value like this:
String guid = grailsApplication.getMainContext().sessionFactory.currentSession.createSQLQuery(guidSQL).list().get(0)
Issue #2: Actually using this value in a Grails domain object
To actually use this GUID value in your Grails domain class, you need to use the Hibernate generator "assigned". As mentioned earlier, this declares that you want to set your own ID's manually, rather than letting Grails/GORM/Hibernate generate them automatically. Compare this modified code snippet to the one in my original question above:
...
static mapping = {
table name: "OWNER"
version false
id column: "OWNER_OID", generator: "assigned"
name column: "NAME"
...
}
...
In my domain class, I changed "guid" to "assigned". I also found that I needed to eliminate the "columns {}" grouping block, and move all my column information up a level (weird).
Now, in whichever Controller is creating these domain objects... generate a GUID as described above, and plug it into the object's "id" field. In a Controller generated automatically by Grails Scaffolding, the function will be "save()":
def save() {
def ownerInstance = new Owner(params)
String guidSQL = grailsApplication.getMainContext().sessionFactory.getDialect().getSelectGUIDString()
ownerInstance.id = grailsApplication.getMainContext().sessionFactory.currentSession.createSQLQuery(guidSQL).list().get(0)
if (!ownerInstance.save(flush: true, insert: true)) {
render(view: "create", model: [ownerInstance: ownerInstance])
return
}
flash.message = message(code: 'default.created.message', args: [message(code: 'owner.label', default: 'Owner'), ownerInstance.id])
redirect(action: "show", id: ownerInstance.id)
}
You might think to try putting this logic directly inside the domain object, in a "beforeInsert()" function. That would definitely be cleaner and more elegant, but there are some known bugs with Grails that prevent ID's from being set in "beforeInsert()" properly. Sadly, you'll have to keep this logic at the Controller level.
Issue #3: Make Grails/GORM/Hibernate store this properly
The plain truth is that Grails is primarily intended for virgin-new applications, and its support for legacy databases is pretty spotty (in fairness, though, it's a bit less spotty than other "dynamic" frameworks I've tried). Even if you use the "assigned" generator, Grails sometimes gets confused when it goes to persist the domain object.
One such problem is that a ".save()" call sometimes tries to do an UPDATE when it should be doing an INSERT. Notice that in the Controller snippet above, I have added "insert: true" as a parameter to the ".save()" call. This tells Grails/GORM/Hibernate explicitly to attempt an INSERT operation rather than an UPDATE one.
All of the stars and planets must be in alignment for this to work right. If your domain class "static mapping {}" block does not set the Hibernate generator to "assigned", and also set "version false", then Grails/GORM/Hibernate will still get confused and try to issue an UPDATE rather than an INSERT.
If you are using auto-generated Grails Scaffolding controllers, then it is safe to use "insert: true" in the Controller's "save()" function, because that function in only called when saving a new object for the first time. When a user edits an existing object, the Controller's "update()" function is used instead. However, if you are doing your own thing in your own custom code somewhere... it will be important to check on whether a domain object is already in the the database before you make a ".save()" call, and only pass the "insert: true" parameter if it really is a first-time insert.
Issue #4: Using natural keys with Grails/GORM/Hibernate
One final note, not having to do with Oracle GUID values, but related to these Grails issues in general. Let's say that in a legacy database (such as the one I've been dealing with), some of your tables use a natural key as their primary key. Say you have an OWNER_TYPE table, containing all the possible "types" of OWNER, and the NAME column is both the human-readable identifier as well as the primary key.
You'll have to do a couple of other things to make this work with Grails Scaffolding. For one thing, the auto-generated Views do not show the ID field on the screen when users are creating new objects. You will have to insert some HTML to the relevant View to add a field for the ID. If you give the field a name of "id", then the auto-generated Controller's "save()" function will receive this value as "params.id".
Secondly, you have to make sure that the auto-generated Controller's "save()" function properly inserts the ID value. When first generated, a "save()" starts off by instantiating a domain object from the CGI parameters passed by the View:
def ownerTypeInstance = new OwnerType.get( params )
However, this does not handle the ID field you added to your View. You will still need to set that manually. If on the View you gave the HTML field a name of "id", then it will be available in "save()" as "params.id":
...
ownerTypeInstance = new OwnerType()
ownerTypeInstance.id = params.id
// Proceed to the ".save()" step, making sure to pass "insert: true"
...
Piece of cake, huh? Perhaps "Issue #5" is figuring out why you put yourself through all this pain, rather than just writing your CRUD interface by hand with Spring Web MVC (or even vanilla JSP's) in the first place! :)
Support for using SYS_GUID() is dependent upon the Oracle dialect that you are using. Looking at the hibernate source on GitHub it appears that the dialect was only setup to use the Oracle-generated guid in Oracle9Dialect.java and Oracle8iDialect.java. Therefore, it won't work with the 9i or 10g dialects.
You should submit a patch to hibernate which will add the required function(s) to enable the same functionality as the other dialects.

Adding columns to DB after publishing using EF CodeF and mvc nugget Scaffold

I have created a web site using mvc 3 and Ef code first , now after publishing the site and it's DB I have found out that I need to add a new columns to one of my DB table,
(the DB already has a lot of data in it )
should I add the columns direct to the DB or should I add to the class?
(just a simple string with get and set)
And how can I do it without losing my data in the DB ?
thanks
Adding the columns to the class should be enough. Evidence you can find here.
Here is the full list of changes that migrations can take care of automatically:
Adding a property or class
Nullable columns will be assigned a value of null for any existing rows of data
Non-Nullable columns will be assigned the CLR default for the given data type for any existing rows of data
Renaming a property or class
See ‘Renaming Properties & Classes’ for the additional steps required here
Renaming an underlying column/table without renaming the property/class
(Using data annotations or the fluent API)
Migrations can automatically detect these renames without additional input
Removing a property
See ‘Automatic Migrations with Data Loss’ section for more information
I suggest you to add the columns direct to the DB and to the class, and test it on the local machine.

Spring Roo Oracle and Underscore

I have a question concerning spring roo and databases.
I have a field called personName, in oracle to column is create as person_Name
I there a way to avoid the underscore. I suppose naming my column personname would fix this, but can I ask spring not to add the underscore ?
If you need a general solution (instead of "fixing" some single points (abaloghs answer)), you can specify a Naming Strategy for your JPA provider.
For an example see: JPA (Hibernate) and custom table prefixes
Roo by default refers to the JPA implementation to determine column names. You can override the defaults with the --column property:
entity --class Foo
field string --fieldName FooBar --column fooBar
Bonjour,
by the way, I do not think that it is possible to reverse engineer a database with underscores in table names :
the corresponding domain classes will be created and compiled since Java accept undersocres in class names
the tests will be performed without raising any issue
everything will be scaffold for the GUI
you will succesfully deploy it on tomcat and your application page will show up in your browser
You may fill the form to create a new instance of your object
But if you click on SAVE --> internal error
If you have a look at the tomcat log, you will fid the well known exception : javax.servlet.jsp.JspTagException: No message found under code ...
The reason is that your class name has been truncated in the message_xx.properties files.
Everything before the underscore is dropped and thus, no message is found in order to display that your record has been successfully saved.
It would be nice that the ROO shell raise an error when the jpa entity is created and not at runtime ...

Resources