I have a Product entity and it's name and category combination should be unique for every product.
In my case name is required, but category may not be present.
Product is defined like this:
#Entity
#Table(
uniqueConstraints=
#UniqueConstraint(columnNames={"name", "category")
)
public class Product {
// ...
#NotNull
#Size(min = 1, max = 64)
private String name;
#ManyToOne(fetch = FetchType.EAGER)
private ProductCategory category;
// ...
}
... but the constraint works only when category is not NULL.
In other words I cannot persist (and this is ok) entities with:
name="example", category=1
name="example", category=1
... at the same time I can persist (and this is not what I want) entities wihh:
name="example", category=null
name="example", category=null
So, my question is - how do I implement unique constraint for combination of fields, one of which can be nullable (with NULL treated as any other value)?
PS: I use Hibernate as JPA provider and MySQL database.
The #UniqueConstraints annotation should do the work. Check the documentation with an example. Also if the table is autogenerated, you might consider dropping the table, depending on how your autogeneration of the schema is set in your persistence.xml file.
UPDATE
Of course, you must specify both columns (not fields as you did):
#UniqueConstraint(columnNames={"name", "category_id")//or check the name of the column in the db for category
UPDATE 2
Because the problem is actually in mysql, you could insert other two persistent fields to your entity:
#Column(columnName="category", insertable=false, updatable=false)//map to the same column used for your category
private Long categoryId;//I suppose it is a Long.
#Column(columnName="categoryIdConstraint")//
private long categoryIdConstraint;//this is not nullable. If categoryId is null, then simply put 0 or -1
//and now synchronize the categoryIdConstraint field with entity listeners
//and add the #UniqueConstraint over "name" and "categoryIdConstraint" (which now cannot be null.)
Managed to avoid solving this this issue relatively easy (by appying null object pattern).
For anyone who cannot avoid this issue easily and is wondering how to solve this with as less pain as possible - have a look on implementing mysql trigger that will do unique constraint validation taking nullable columns into account. Here is a nice starting point.
Related
In Grails, Gorm, I have this entity:
class MyEntity implements Serializable {
Long bankTransactionId
int version
BigDecimal someValue
static constraints = {
bankTransactionId(nullable: false)
version(nullable: true)
someValue(nullable: true)
}
}
Doing MyEntity.findByBankTransactionId(Long.valueOf("3")) throws this exception:
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown
column 'this_.id' in 'field list'
I am suspecting the fact that my column has the name id in it. Could it be this?
How to fix it then ?
Thanks.
Everything you have provided here looks fine. In particular, there are no restrictions about having the letters "id" in a column name.
Take a look at your generated MySQL table. I'm guessing that the id column isn't there for some reason. Maybe something prevented generating it due to some earlier error that you have now corrected, or you have your datasource set to "none" instead of "update" (or similar) and the whole table is missing!
If this is just a development environment with no real data (and no foreign key constraints), drop the whole MyEntity table and let it be automatically recreated. If not, move to a different temporary datasource, let a new table be created, and compare the two. If the new one still doesn't have an id column, you have something going wrong during your startup that is preventing your tables from being created correctly. You could just add the column in manually, but if you don't figure out what happened to it in the first place, it will probably just happen again.
For reference, in my test environment, my MySQL table for "MyEntity" copied from your example looks like:
desc my_entity;
'id','bigint(20)','NO','PRI',NULL,'auto_increment'
'version','int(11)','YES','',NULL,''
'bank_transaction_id','bigint(20)','NO','',NULL,''
'some_value','decimal(19,2)','YES','',NULL,''
I am using Entity Framework 4.2 Code First and MySQL. I need to be able to do a full text search across several fields of my table. However, I am having a number of issues with this:
1) Full text search under MySQL requires the MyISAM table type. However, when I run the DropCreateDatabaseAlways initializer, the table type I get is InnoDB. Here is an example of my table definition:
[Table("Patient")]
[Serializable]
public class Patient
{
public Patient()
{
}
[Key]
public int PatientID { get; set; }
[MaxLength(50)]
public string FirstName { get; set; }
[MaxLength(50)]
public string LastName { get; set; }
}
Is it possible to specify MyISAM table type? If so, how?
2) I need to specify the full text index:
ALTER TABLE `patient` ADD FULLTEXT INDEX `Name`(`FirstName`, `LastName`);
One possibility I can think of is:
public class MyDbInitializer : DropCreateDatabaseAlways<MyDbContext>
{
protected override void Seed(MyDbContext context)
{
context.Database.ExecuteSqlCommand(
"ALTER TABLE `patient` ADD FULLTEXT INDEX `Name`(`FirstName`, `LastName`)");
}
}
Is there a better way?
3) I need to be able to search using the full text index. Ideally in a way that works with my existing linq to SQL.
One less-then-desirable possiblity I can think of is to create a stored procedure (again, using ExecuteSqlCommand) that takes a string and returns either a list of matching PatientIDs or a list of Patient rows. For example:
IEnumerable<int> patientIDs = [...call stored proc to get matching ids...]
var patients = from p in patients
where patientIDs.Any(pid => pid == p.PatientID)
select new {...}
Or:
var patients = from p in [...call stored proc to get matching patient records...]
select new {...}
How would I do this? Is there a better way?
4) Bonus question: Even though I specify the [MaxLength(50)] attribute, my strings are being stored as MEDIUMTEXT instead of VARCHAR(50) as I would expect. Without the [MaxLength] attribute I get LONGTEXT. How do I specify VARCHAR(n) for strings types?
Any ideas?
Thanks in advance,
Dan
It is provider specific so unless your provider for MySQL offers some way to change it you will not be able to do that from code first. I think you should be able to run ALTER TABLE in custom DB initializer to change the engine.
If you use migration feature (EF 4.3 and newer) you can script the index in the migration otherwise you need to use your initializer.
You need to query the database with SQL. EF has no support for fulltext search queries. To call a stored procedure you must use SqlQuery and populate a type with same properties as the result set.
Try to use [Column(TypeName="VARCHAR(50)] annotation.
If you are a dotConnect for MySQL user, we advise you to take a look at this article. If you use some other provider, we advise you to contact its vendor.
I'm using ActiveRecord by CastleProject with MySQL in my solution. I wanted to retrieve the last insert ID, so I did something like this:
[ActiveRecord]
public class UserDb : ActiveRecordBase<UserDb>
{
[PrimaryKey]
public uint Id { get; set; }
[Property]
public int OtherStuff { get; set; }
}
public void Blablabla()
{
UserDb user = new UserDb();
user.OtherStuff = 123;
user.CreateAndFlush();
Console.WriteLine( user.Id );
}
Sometimes, it outputs the correct ID, but sometimes, it just outputs 0.
Did I miss anything?
Note: The reason I did this is because we had several server connected to the same database at the same time, and I wanted a unique id generator. For unique, I mean unique among those servers. If there're other solutions to maintain a unique id generator among several servers, I will be very appreciated.
Thanks guys!
If you want to have a unique ID among several servers, you might consider using a GUID instead of an int. If, however, you're just trying to get the ID of the item you just inserted, you could just refresh it with user.Refresh()
Castel.ActiveRecord (or better NHibernate) gives you multiple options to generate primary keys, you should have a look at the PrimaryKeyType-Enum.
GUID is the easiest one, needing nothing more then to make the primary-key-Property of the type System.Guid and adjust the column in the database. (I am not sure wich type you need in the database, I allways use VARCHAR/CHAR/etc.)
If you want to keep the uint you are using at the moment, then I would suggest to use HiLo.
[PrimaryKey(Generator=PrimaryKeyType.HiLo)]
public virtual uint Id{get;set;}
In the database:
CREATE TABLE hibernate_unique_key( next_hi NUMERIC(18, 0) );
INSERT INTO hibernate_unique_key VALUES(1);
-- script from ms-sql-express, I am not sure if there is a difference for mysql
Greetings
Juy Juka
I've been bitten by the same issue. However, my issue grew and I found what seems to be the real issue; nHibernate is not thread-aware. I'm assuming your code is multi-threaded, if so you need to make use of 'using(new SessionScope())' blocks for database access in other threads.
In my case Castle would return IDs from other tables after saving, that really drove me crazy.
I'm creating a DB table using hbm2ddl with Java code similar to the following:
#Entity
public class Filter {
public enum Type {
TypeA, TypeB;
}
#Enumerated(EnumType.STRING)
private Type type;
}
It works fine, but for "type" a VARCHAR column is created, i.e. the DDL code looks like this:
CREATE TABLE IF NOT EXISTS `filter` (`type` varchar(255) DEFAULT NULL)
But what I want to have is this:
CREATE TABLE IF NOT EXISTS `filter` (`type` enum('TypeA','TypeB') NOT NULL)
Is this possible to declare in Hibernate, preferred with annotations?
Or is there a way to extend SchemaUpdate and overwrite the method that renders the alter script part for enumerated field the way I like it?
Background: The same database is used in a PHP part of the project and I want to prevent that invalid values are inserted.
Although it seems there is no way to handle MySQL enums 100% automatically, as pointed out by Lucas on his answer, there is actually a simple way to contour it. You may use columnDefinition attribute on #Column annotation, which seems to be specifically designed to generate custom DDL code.
See the documentation excerpt describing the attribute:
(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.
The NOT NULL restriction is quite standard, and is supported by another attribute nullable.
Thus, your property definition would look like this:
#Enumerated(EnumType.STRING)
#Column(columnDefinition = "enum ('TypeA', 'TypeB')", nullable = false)
private Type type;
I believe that's going to be complicated, since the java.sql.Types, which define the sql types treated by java, does not have enum type (since it's not a standardized type according to SQL-92).
If that was the case you could create a hibernate custom UserType extending the EnumType and setting the sqlType accordingly, but since java.sql.Types doesn't handle it I don't see how to use native sql enum.
best regards!
We have the following entity relationships where a User belongs to a particular Organization. My queries either look like "select * from User where org=:org" or "select * from User where org=:org and type=:type"
I have separate indexes on the User class. The first query will be fine, because of the Index on the foreign key element. Does the second query mandate a multi columnindex on org and type columns. If so how should I annotate to create one such index.
#Entity
class User {
...
#ManyToOne
#ForeignKey
#Index
Organization org;
#Index
Type type;
...
}
This is doable using the Hibernate specific #Table annotation. From the documentation:
2.4.1 Entity
...
#Table(appliesTo="tableName", indexes = { #Index( name="index1", columnNames={"column1", "column2"} ) } ) creates the defined indexes on the columns of table tableName. This can be applied on the primary table or any secondary table. The #Tables annotation allows your to apply indexes on different tables. This annotation is expected where #javax.persistence.Table or #javax.persistence.SecondaryTable(s) occurs.
Reference
Hibernate Annotations Reference Guide
2.4. Hibernate Annotation Extensions
As you can read in JSR-000338 Java Persistence 2.1 Proposed Final Draft Specification:
11.1.23 Index Annotation
The Index annotation is used in schema generation. Note that it is not necessary to specify an index for a primary key, as the primary key index will be created automatically, however, the Index annotation may be used to specify the ordering of the columns in the index for the primary key.
#Target({}) #Retention(RUNTIME)
public #interface Index {
String name() default "";
String columnList();
boolean unique() default false;
}
The syntax of the columnList element is a column_list, as follows:
column::= index_column [,index_column]*
index_column::= column_name [ASC | DESC]
The persistence provider must observe the specified ordering of the
columns.
If ASC or DESC is not specified, ASC (ascending order) is
assumed.
Usage example:
#Table(indexes = {
#Index(columnList = "org,type"),
#Index(columnList = "another_column")})
Yes, it is possible using JPA 2.1 as seen in the specification here:
http://download.oracle.com/otndocs/jcp/persistence-2_1-pfd-spec/index.html
on page 445 it states that
The Index annotation is used in schema generation
columnList (Required) The names of the columns to be included in the index.
An example of usage can be seen here:
http://java-persistence-performance.blogspot.co.uk/2013/03/but-what-if-im-not-querying-by-id.html
It seems that the syntax is the same or very similar to Hibernate.