Hello!
I'm using Hibernate 3.2.1 in JBoss 4.0.5GA on MySQL 5.0.27, and this problem may have overlap with JIRA 492/499.
While trying to implement a hierarchical permissions model where the permissions entities have composite primary keys that are spread over both the entities themselves and several MappedSuperclass base classes, the primary keys don't seem to be specified correctly in the generated SQL.
There are several permissions entities, all have owners (UserGroup entities), a target (the thing the owners have permissions for), and a bit field of the different boolean permission values.
I wanted each permissions entity to have a composite primary key composed of the target entity id, the project id (the root container entity, containing the relevant subset of all owners and all targets) and the permissions bit-field.
First, here's an example that DOES work, a ProjectPermissions class whose compound primary key is the project id and the integer bit-field. Because the target is the same as the project, there is no additional target id in the primary key class. Here are the ProjectPermissions fields--note that it doesn't specificy an @Id field itself, the two are inherited:
Code:
@Entity
@IdClass(ProjectPermissionsKey.class)
public class ProjectPermissions extends OwnedPermissions<Project> implements Serializable, Cloneable {
private static final long serialVersionUID = 1L;
@ManyToMany
@JoinTable(
inverseJoinColumns = @JoinColumn(referencedColumnName = "id"),
joinColumns = { @JoinColumn(referencedColumnName = "projectId"),
@JoinColumn(referencedColumnName = "permissions")})
protected Set<UserGroup> owners = new HashSet<UserGroup>();
.
.
The OwnedPermissions class has these fields, which adds a project id field to the composite primary key.
Code:
@MappedSuperclass
public abstract class OwnedPermissions<TargetType extends Serializable> extends BitFieldPermissions {
@Transient Class<? extends TargetType> targetType;
private static final long serialVersionUID = 1L;
@Id @Column(nullable=false) Integer projectId;
@ManyToOne @JoinColumn(name="projectId", insertable=false, updatable=false) protected Project project;
public abstract Set<UserGroup> getOwners();
public abstract void setOwners(Set<UserGroup> owningUserGroups);
.
.
.
BitFieldPermissions has these fields:
Code:
@MappedSuperclass
public class BitFieldPermissions extends NamedCpkEntity {
@Id protected int permissions = 0;
.
.
and here are the fields for ProjectPermissionsKey
Code:
public class ProjectPermissionsKey implements Serializable {
private static final long serialVersionUID = 1L;
protected Integer projectId;
protected int permissions;
.
.
(NamedCpkEntity is another MappedSuperclass that adds a String name field but no pk fields)
Here is what hibernate produces for ProjectPermissions (as a reverse engineered create table script), which looks correct to me:
Code:
CREATE TABLE `ProjectPermissions` (
`projectId` int(11) NOT NULL,
`permissions` int(11) NOT NULL,
`name` varchar(255) character set utf8 collate utf8_bin NOT NULL,
PRIMARY KEY (`projectId`,`permissions`),
KEY `name` (`name`),
KEY `FKD8FC53CB2E24AC76` (`projectId`),
CONSTRAINT `FKD8FC53CB2E24AC76` FOREIGN KEY (`projectId`) REFERENCES `Project` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Now for one that doesn't work. I have an AssetGroupPermissions @Entity, which has UserGroup 'owners' like ProjectPermissions and adds an AssetGroup 'target' relationship. The target asset id is supposed to form a 3 member primary key with the project id and the permissions value.
Code:
@Entity
@IdClass(AssetGroupPermissionsKey.class)
public class AssetGroupPermissions extends OwnedPermissions<AssetGroup>
implements Serializable, Cloneable, Comparable {
private static final long serialVersionUID = 1L;
@Id private Integer assetId;
@ManyToOne @JoinColumn(name="assetId", insertable=false, updatable=false) protected AssetGroup target;
@ManyToMany
@JoinTable(
inverseJoinColumns = @JoinColumn(referencedColumnName = "id"),
joinColumns = { @JoinColumn(referencedColumnName = "assetId"),
@JoinColumn(referencedColumnName = "projectId"),
@JoinColumn(referencedColumnName = "permissions")})
protected Set<UserGroup> owners = new HashSet<UserGroup>();
.
.
and here is the primary key class
Code:
public class AssetGroupPermissionsKey extends ProjectPermissionsKey {
private static final long serialVersionUID = 1L;
private Integer assetId;
.
.
Here is what hibernate produces (reverse engineered):
Code:
CREATE TABLE `AssetGroupPermissions` (
`assetId` int(11) NOT NULL,
`projectId` int(11) NOT NULL,
`permissions` int(11) NOT NULL,
`name` varchar(255) character set utf8 collate utf8_bin NOT NULL,
PRIMARY KEY (`assetId`),
UNIQUE KEY `assetId` (`assetId`,`projectId`,`permissions`),
KEY `name` (`name`),
KEY `FK47858F752E24AC76` (`projectId`),
KEY `FK47858F754E45DD71` (`assetId`),
CONSTRAINT `FK47858F754E45DD71` FOREIGN KEY (`assetId`) REFERENCES `AssetGroup` (`id`),
CONSTRAINT `FK47858F752E24AC76` FOREIGN KEY (`projectId`) REFERENCES `Project` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Not what I expected, but is it wrong? Or some MySql idiom? Note that if I drop the AssetGroupPermissions table and create one using what I was expecting:
Code:
CREATE TABLE `AssetGroupPermissions` (
`assetId` int(11) NOT NULL,
`projectId` int(11) NOT NULL,
`permissions` int(11) NOT NULL,
`versionNumber` int(11) NOT NULL,
`lastModified` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
`name` varchar(255) character set utf8 collate utf8_bin NOT NULL,
`permsString` varchar(255) default NULL,
PRIMARY KEY (`assetId`,`projectId`,`permissions`),
KEY `name` (`name`),
KEY `FK47858F752E24AC76` (`projectId`),
KEY `FK47858F754E45DD71` (`assetId`),
CONSTRAINT `FK47858F754E45DD71` FOREIGN KEY (`assetId`) REFERENCES `AssetGroup` (`id`),
CONSTRAINT `FK47858F752E24AC76` FOREIGN KEY (`projectId`) REFERENCES `Project` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
and then deploy my app with hibernate.hbm2ddl.auto=validate, I get no errors.
Thanks for any help,
Reid