I am trying Roo database reverse engineering and I had a problem with my first real database. For this question, I created a minimal example showing the problem. In the DB there is a sl_person table a sl_group table and a bridge table sl_person_group, since a person can belong to 0 or more groups.
CREATE DATABASE `rooperson` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE TABLE IF NOT EXISTS `sl_group` (
`id_group` int(11) NOT NULL DEFAULT '0',
`name` varchar(80) NOT NULL,
`description` text,
UNIQUE KEY `id_group_idx` (`id_group`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `sl_person` (
`id_person` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(64) DEFAULT NULL,
`surname` varchar(64) DEFAULT NULL,
PRIMARY KEY (`id_person`),
KEY `name` (`name`),
KEY `surname` (`surname`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=5727 ;
CREATE TABLE IF NOT EXISTS `sl_person_group` (
`id_person` int(11) NOT NULL DEFAULT '0',
`id_group` int(11) NOT NULL DEFAULT '0',
UNIQUE KEY `id_person_group_idx` (`id_person`,`id_group`),
KEY `id_group` (`id_group`)
) ENGINE=InnoDB DEFAULT CHARSET;
ALTER TABLE `sl_person_group`
ADD CONSTRAINT `sl_person_group_ibfk_2` FOREIGN KEY (`id_group`) REFERENCES `sl_group` (`id_group`),
ADD CONSTRAINT `sl_person_group_ibfk_1` FOREIGN KEY (`id_person`) REFERENCES `sl_person` (`id_person`);
In Eclipse I create a new Spring Roo project with Spring Source Tool Suite: File -> New -> Project -> Spring Roo Project
Project Name: rooperson
Top level package name: org.obliquid.rooperson
Next -> Finish
Then in roo shell I type:
persistence setup --database MYSQL --provider HIBERNATE
database properties set --key database.password --value xxxxx
database properties set --key database.username --value rooperson
database properties set –key database.url –value jdbc:mysql://localhost/rooperson?zeroDateTimeBehavior=convertToNull&characterEncoding=UTF-8
database reverse engineer --schema PUBLIC --package org.obliquid.rooperson.domain
web mvc setup
Then I fixed a small problem with stylesheets.
However, when I try to start GlasshFish 3.1.1, the application doesn't start with the following error.
INFO: 2011-09-11 20:42:59,562 [admin-thread-pool-4848(3)] ERROR org.springframework.web.context.ContextLoader - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'transactionManager'
defined in file [/usr/local/glassfish3/glassfish/domains/domain1/eclipseApps/rooperson/WEB-INF/classes/META-INF/spring/applicationContext.xml]:
Cannot resolve reference to bean 'entityManagerFactory' while setting bean property 'entityManagerFactory';
nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'entityManagerFactory' defined in file
[/usr/local/glassfish3/glassfish/domains/domain1/eclipseApps/rooperson/WEB-INF/classes/META-INF/spring/applicationContext.xml]:
Invocation of init method failed; nested exception is org.hibernate.AnnotationException: referencedColumnNames(id_group)
of org.obliquid.rooperson.domain.SlPersonGroup.idGroup referencing org.obliquid.rooperson.domain.SlGroup not mapped to a single property
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328)
The generated file SlPersonGroup_Roo_DbManaged.aj starts with:
privileged aspect SlPersonGroup_Roo_DbManaged {
#ManyToOne
#JoinColumn(name = "id_group", referencedColumnName = "id_group", nullable = false, insertable = false, updatable = false)
private SlGroup SlPersonGroup.idGroup;
#ManyToOne
#JoinColumn(name = "id_person", referencedColumnName = "id_person", nullable = false, insertable = false, updatable = false)
private SlPerson SlPersonGroup.idPerson;
How can I solve the problem? If I should provide more information let me know.
Similar problem here. It seems this version of Roo has a problem with composite primary keys (as in your sl_person_group table).
While making the key refer to a single column will stop this from happening, unfortunately it's probably not what your data model calls for.
Related
I would like to define a constraint in JPA on an entity to make sure that either one of two properties "text" or "title" is set to a non-null value.
For this example, suppose the following Question entity class:
#Entity
#Table
public class Question {
#Id
private Long id;
#Column(nullable = true)
private String text;
#Column(nullable = true)
private String title;
}
For this class, JPA will generate the following SQL statement (we need to use the oracle dialect):
create table question (
id number(19,0) not null,
text varchar2(50 char) null,
title varchar2(10,0) null,
primary key (id)
);
In order to check that either one of the properties is set, I could add a check constraint:
#Entity
#Table
#Check(constraints = "TEXT IS NOT NULL OR TITLE IS NOT NULL")
public class Question {
...
}
Now JPA will generate this:
create table question (
id number(19,0) not null,
text varchar2(50 char) null,
title varchar2(10,0) null,
primary key (id),
check (TEXT IS NOT NULL OR TITLE IS NOT NULL)
);
On the database side, this will generate a check constraint with a random name like "SYS_C00127157".
In order to assign a meaningful name (for example: check_title) to the constraint, I could use this DDL:
create table question (
id number(19,0) not null,
text varchar2(50 char) null,
title varchar2(10,0) null,
primary key (id),
constraint check_title check(TEXT IS NOT NULL OR TITLE IS NOT NULL)
);
What I am looking for is something like this:
#Entity
#Table
#Check(name = "check_title" constraints = "TEXT IS NOT NULL OR TITLE IS NOT NULL")
public class Question {
...
}
Unfortunately, this is not possible. The #Check annotation in Java does not offer such a name property for the constraint.
Is there any other way to set a name so that the generated DDL will match the expected result?
It's not possible, unfortunately. If you take a look at how the table generation script is built:
https://github.com/hibernate/hibernate-orm/blob/master/hibernate-core/src/main/java/org/hibernate/mapping/Table.java#L604
you'll see that the string, which you specify in #Check annotation is just wrapped like this:
buf.append( ", check (" )
.append( checkConstraint )
.append( ')' );
and the generation of a constraint name is given to the database.
In contrast, a few lines above, you can find that it's possible to influence the name of the unique constraints. Here you can find an example of it.
That's not possible.
In my opinion it's also not a good idea to generate the production database from JPA annotations.
The datamodel usually lives longer than the application and therefore it's good to have the DDL under control.
You should consider to use FlyWay or Liquibase for your database migrations.
I have an entity Property which has city field. Something like this:
#Entity
class Property {
...
#ManyToOne(fetch = FetchType.LAZY)
private City city;
...
}
So each property (e.g house or apartment) belongs to only one city.
Now here is a thing, if I try to log generated SQL by Hibernate, it is generating the foreign key in a right way:
Hibernate:
alter table property
add constraint FKdn1hnohufcwdr4a401xabcjn
foreign key (city_id_city)
references city (id_city)
However, if I check my Mysql database, there is no foreign key there, only index is generated:
show create table property;
leads to:
| property | CREATE TABLE `property` (
`id_property` int(11) NOT NULL AUTO_INCREMENT,
`created_at` datetime NOT NULL,
`deal_type` varchar(15) NOT NULL,
`publisher_type` varchar(15) NOT NULL,
`type` varchar(15) NOT NULL,
`updated_at` datetime NOT NULL,
`city_id_city` int(11) DEFAULT NULL,
PRIMARY KEY (`id_property`),
KEY `FKdn1hnohufcwdr4a401xabcjn` (`city_id_city`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 |
So the question is why there is no CONSTRAINT FOREIGN KEY () REFERENCES definition in my database?
The problem ended up to be the database engine after I switched to InnoDB engine it started working, as MyISAM does not implement foreign keys.
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
The annotation #ManyToOne is used to map two tables that have a relationship instance by a foreign key, it could not automatically create a foreign key in your database
You need to define the list Property in the City Entity and add the relationship there as well
Good day to you. I have 2 tables.
CREATE TABLE A(
id NUMBER(10) NOT NULL,
name VARCHAR2(255) NULL,
category VARCHAR2(255) NULL,
is_hidder NUMBER(1) NULL,
is_enabled NUMBER(1) NULL,
b_id NUMBER(10) NULL,
CONSTRAINT pk_a PRIMARY KEY (ID),
CONSTRAINT fk_to_b
FOREIGN KEY (vj_users_preferences_id)
REFERENCES VJ_USER_PREFERENCES(ID)
);
CREATE TABLE B(
ID NUMBER(10) NOT NULL,
EXT_ID NUMBER(10) NOT NULL,
KEY VARCHAR2(1024) NOT NULL,
VALUE VARCHAR2(1024) NOT NULL,
CONSTRAINT PK_B PRIMARY KEY(ID)
);
For both tables I have entities and I want to map them #OneToOne.
class Entity A{
.......
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="pk_b", referencedColumnName = "id")
private B b;
}
Entity B is Audited and haven't got A field so connection is unidirectional. When I try to persist the A entity I got the error:
Caused By: org.hibernate.HibernateException: Could not determine type of dynamic map entity
This exception is being thrown from:
org.hibernate.tuple.entity.DynamicMapEntityTuplizer.BasicEntityNameResolver#resolveEntityName
Because the input is Map and it has not got: DynamicMapInstantiator.KEY in it.
Can somebody help me with this issue? I cannot figure out why Hibernate is creating that map without necessary value. Is there something wrong with my mapping?
It appeared that my UI form was receiving over 50 symbols, but database had:
EXT_ID NUMBER(10) NOT NULL
When I altered table to reflect UI possibilities (e.g. EXT_ID NUMBER(50) NOT NULL) then error was gone.
That exception, as per my understanding, is misleading in this kind of situation. I was expecting to see: SQLGrammarException of kind of it.
Hope this will help oneday.
I have the following definition for an id field in an entity that is mapped to a table in HSQLDB.
...
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "ID")
private Integer id;
...
But this does not seem to generate the an unique id; instead an attempt is made to insert null into the column which results in failure. If, I manually create a sequence and generation strategy to use that sequence then the data is persisted as expected.
Doesn't a generation strategy of auto imply that the provider (hibernate in this case) will automatically choose the correct approach and do all the heavy lifting as needed (create sequence, use a native approach or whatever works for that particular platform)? Is my understanding incorrect?
Doesn't a generation strategy of auto imply that the provider (hibernate in this case) will automatically choose the correct approach and do all the heavy lifting as needed (create sequence, use a native approach or whatever works for that particular platform)? Is my understanding incorrect?
It does in theory (it defaults to IDENTITY with HSQLDB) and it works for me. This begs the following questions:
What dialect are you using (just in case)?
How did you create the table?
Can you show the DDL (activate the logging of org.hibernate.tool.hbm2ddl if required)?
How do you insert (through Hibernate's API, right?)?
Here is a sample DDL for an entity Foo when using HSQLDB:
create table Foo (
id bigint generated by default as identity (start with 1),
bar varchar(100),
primary key (id)
)
I created the table using the HSQL DB manager. Just normal create table address... I had not set the id column as identity in my case - just set it as primary key.
Then you have your answer, use an IDENTITY column.
While Hibernate does choose the right strategy and does generate the appropriate INSERT statements (passing null into the id which is expected to be persisted into an IDENTITY column), it won't create or alter your physical model if you don't use the DDL generation and export capabilities.
I had the same issue when using a JpaSchemaGenerator utility class that I wrote.
When generating the schema for a org.hibernate.dialect.HSQLDialect (where I use a SEQUENCE to generate my unique IDs), I use the following Hibernate property:
hibernate.id.new_generator_mappings=true
This results in the following CREATE statement:
CREATE TABLE BATCH (
BAT_ID NUMBER(19,0) NOT NULL,
BAT_EXPIRY_DATE TIMESTAMP,
BAT_NUMBER VARCHAR2(255 CHAR),
BAT_MAT_ID NUMBER(19,0),
PRIMARY KEY (BAT_ID)
);
But when I use this same property in my utility class to generate a schema using the org.hibernate.dialect.HSQLDialect, I get the following CREATE statement:
CREATE TABLE BATCH (
BAT_ID BIGINT NOT NULL,
BAT_EXPIRY_DATE TIMESTAMP,
BAT_NUMBER VARCHAR(255),
BAT_MAT_ID BIGINT,
PRIMARY KEY (BAT_ID)
);
This would mean that if I created a Batch without an ID, it would not generate it for me and the NOT NULL constraint would cause an exception.
If I change the Hibernate property to the following:
hibernate.id.new_generator_mappings=false
Then it would generate the following CREATE statement:
CREATE TABLE BATCH (
BAT_ID BIGINT GENERATED BY DEFAULT AS IDENTITY (START WITH 1),
BAT_EXPIRY_DATE TIMESTAMP,
BAT_NUMBER VARCHAR(255),
BAT_MAT_ID BIGINT,
PRIMARY KEY (BAT_ID)
);
Which works perfectly when creating JPA entities with Hibernate.
How can I order JPA to set a MySQL database column with text content as case sensitive by default upon creation?
The #Column annotation on your field can specify a columnDefinition attribute which may allow you to specify a case-sensitive collation for the column.
public abstract String columnDefinition
(Optional) The SQL fragment that is used when generating the DDL for the column.
Defaults to the generated SQL to create a column of the inferred type.
Default:
""
In your case, for example, using the #Column annotation, you would use
#Column(name = "NAME_COL", columnDefinition = "VARCHAR(250) COLLATE latin1_general_cs")
private String name;
MySQL varchar type is not case-sensitive. What you want is varbinary.
CREATE TABLE IF NOT EXISTS `stuff` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` varbinary(255) NOT NULL -- varbinary to be case sensitive
) ENGINE=InnoDB DEFAULT CHARSET=utf8;