I have searched on the web before posting this here, but I'm in an important project and I don't have any more time to waste with this. Well, here's the deal:
My tables in the database(SqlServer) are created automatically by Hibernate. So, I have an Entity and this entity was mapped before with Id annotation, like this:
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private int id;
Hibernate created a table dbo.Parcela inside sqlServer, but we had to change the way we generate the id, Because sometimes we receive the id number and we want that id saved on our database. So our Entity now is like this:
#Id
private Integer id;
Things work fine when we run the program for the first time, but we have some customers that already have their databases created with old Id mapping and we cannot create a new table, with the new Id mapping. So when I'm trying to insert a new record I get this message:
SEVERE: Cannot insert explicit value for identity column
in table 'Parcela' when IDENTITY_INSERT is set to OFF.
Any help would be appreciate.
Thanks
So you want your surrogate keys generated by the database, except when they were already generated by the customer. How are you going to avoid collisions, if the database wants to set id=12345, but a customer-imported entry with that id already exists?
The short answer to your question is: don't do this. I don't want to go into the old natural key vs surrogate key debate, this has been done already for example here. And google "codd surrogate keys" to learn how to properly use them. All i want to say is: if you use surrogate keys, then have your database generate them, and treat everything from outside as additional lookup key. That's the sane way.
The long answer is: if you really want to do this, and if you really know what you're doing, you can implement your own IdGenerator class. In JPA for example, you could annotate your id:
#Id
#GenericGenerator(name = "customId", strategy = "com.example.CustomIdGenerator", parameters = { #Parameter(name = "sequence", value = "SEQ_IDGENERATOR") })
#GeneratedValue(generator = "customId")
#Column(name = "ID", unique = true, nullable = false)
private Integer id;
Your customIdGenerator would then extend SequenceGenerator:
public class CustomIdGenerator extends SequenceGenerator {
public Serializable generate(SessionImplementor session, Object obj) {
// return o.getId() if obj.getId() is not null
// newId = SEQ_IDGENERATOR.nextval
// while (newId is already in db) newId = SEQ_IDGENERATOR.nextval
// return newId
}
}
and your database would provide SEQ_IDGENERATOR. Id would no longer be an autogenerated field but simply
create table foo( id integer not null primary key, ...);
But again: you don't want to do this. You want your surrogate keys to be irrelevant to the outside world and handled by the database.
How did you have Hibernate create the schema for the DB? If you used hbm2ddl perhaps adding
<property name="hibernate.hbm2ddl.auto" value="update"/>
to your persistence.xml or setting hbm2ddl.auto to "update" may have Hibernate automatically update the db schema on redeploy, having it fix the insertion problem.
Of course it won't help you in cases when you try inserting an already existing id, but i guess you know it :)
Related
consider an entity with just an id and text field:
#lombok.Data
class Entity {
#javax.persistence.Id
UUID id;
String name;
}
consider that the table definition is as follows:
create table entity (
id uniqueidentifier not null primary key default newid(),
name varchar(max)
);
I am then curious why this doesn't work and how i could make it work:
UUID savedId = entityRepository.save(new Entity().setName("entity name")).getId();
In JPA, entity IDs can be either assigned by the application code, or generated (by the JPA provider, such as Hibernate, or by the database). In many situations, it's desirable to have the entity IDs be generated instead of applicaiton-assigned; it seems like that's what you are expecting.
If so, you need to annotate the id field with #GeneratedValue. For example:
class Entity {
#Id
#GeneratedValue
UUID id;
String name;
}
Note that there are considerations to be made regarding the generation strategy, so you'll want to educate yourself about them and make the right choice based on your situation. This is a good page that discusses the options. This SO Answer also is worth reading (the author is a well-known expert on JPA and Hibernate).
Hello StackOverflow Community, I have a problem with the annotation #GenerateValue. I want that JPA generates the values for my ID column. But I have another column where people can write some sort of tasks (todo list).
My code seems like this:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
and I have a SQL data file that write some data at the beginning in my h2 database:
INSERT INTO task VALUES(1, 'go to the gym');
INSERT INTO task VALUES(2, 'eat with tom');
INSERT INTO task VALUES(3, 'meetup');
INSERT INTO task VALUES(4, 'doing some homeworks');
INSERT INTO task VALUES(5, 'doing some exercise');
INSERT INTO task VALUES(6, 'studying with Mat');
my problem is, when I delete the integer values on my SQL data file, my compiler says always that I have to declare an id for the tasks, but I thought the #GenerateValue automatically generate the id's for me?
You have to use #GeneratedValue(strategy = GenerationType.IDENTITY) or #GeneratedValue(strategy = GenerationType.SEQUENCE) and make sure that you have index or sequence on database level. Also when using auto-generated ids, don't specify them explicitly when inserting.
Correct:
INSERT INTO task(description) VALUES('go to the gym');
but I thought the "#GenerateValue" automatically generate the id's for me?
It's a not the correct assumption, Hibernate just uses ids provided by db. To get it to work you need to create index/sequence for your primary key column.
You're mixing up sql and hibernate. #GeneratedValue is declared in your java code, so that hibernate knows, that when you're persisting an entity without an id, it should be generated by given strategy.
On the other hand you tried to make insertions using sql without passing primary key, so you're explicitly said, that it should be NULL, which is clearly against the constraint.
I have a use case of dual write where I need to save entities to both Oracle and Postgres DB. I also want to implement a custom IdentifierGenerator to create new primary keys, which have to be consistent in Oracle and Postgres.
The entity is like below (Getter and Setter are ignored).
#Entity
#Table(name = "users")
#GenericGenerator(
name = "CUSTOM_ID_GENERATOR",
strategy = "<class reference>",
)
public class User implements Serializable {
#Id
#GeneratedValue( generator = "idGenerator")
#Column(name = "id")
private Integer id;
#Column(name = "name")
private String name;
}
When saving the entity, what I'm gonna do is like:
User user = new User();
user.setName("USER_1");
long id = oracleSession.save(user);
user.setId(id);
postgresSession.save(user);
For my code, will it cause a new id to be generated in Postgres? If so, how should the implementation be to keep the same id in both Oracle and Postgres?
Is there a way to let the IdGenerator NOT generate a new value when the id is manually set?
Thanks in advance.
#GeneratedValue will cause new value each time save method is called. So you don't have guarantee that the value would be same for Oracle and PG.
You need to use assigned Id in this case.
Your application should generate the id and should set to the user object. The assigned Id will then be saved to both the DB.
To do this you have to drop the #GeneratedValue annotation refer this documentation page.
Now the next question is how to generate unique Id for your tables, the simplest way is to use a sequence from either DB and get new value and set that to user object. Go through this question for more information on this.
I've been searching for a way to generate IDs without actually persisting entities in Hibernate. The reason for this, is that I want to manually insert new database rows. The ideal situation would be to use the auto increment that is present in MySQL, but that is not possible due to the fact that I'm using InheritanceType.TABLE_PER_CLASS. (It is not possible to switch strategies for either inheritance mapping or sequence generation, since the project is already quite mature.)
The conclusion from the research I've done is (please correct me if I'm wrong):
Hibernate reserves a block of IDs by increasing next_val by 1. The range of IDs Hibernate can assign to entities without having to query and update the sequence table again, is (using the old value of next_val) next_val * allocationSize ... (next_val + 1) * allocationSize
My observations from playing with the conclusion:
Hibernate generates IDs that have a value far smaller than next_val * allocationSize (which is in conflict with the conclusion) - in fact, it seems that Hibernate uses IDs up to next_val * allocationSize (but not quite: the value of next_val is 350781 whereas the highest ID in the database is 350744, with an allocationSize of 20)
Sometimes, the value of next_val is smaller than the highest ID in the database
This leaves me with the only option of delegating the ID generation process to Hibernate, since it is unclear to me how IDs are generated. Besides the fact that it is unclear to me, I would like to have a solution that works for different ID generation strategies.
So the approach I want to take is to generate a bunch of ids from java code, which are then 'reserved' for me (the backing table containing the next_val gets updated), so I can use them in my INSERT queries.
This would look something like this:
Given the following definition:
#Entity
#Table(name="datamodel")
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class DataModel {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator="model_sequence")
#SequenceGenerator(
name="model_sequence",
sequenceName="model_sequence",
allocationSize=20
)
#Column(name="id", nullable=false, unique=true, length=11)
private int id;
}
Generate IDs in a way like this:
// This will be the ID generator, a
// long-lived object that generates ids
IDGenerator gen = new IDGenerator();
gen.initialize(DataModel.class, ... /* provide a serviceRegistry, sessionFactory, or anything else that might be needed to initialize this object */);
// This will generate the next ID based
// on the parameters passed to #SequenceGenerator
// and the values that are present in the DB.
int nextId = gen.generateNextId(/* provide a session, transaction, or anything else that might be needed for accessing the DB */);
The following posts have helped me get a bit further:
Hibernate, #SequenceGenerator and allocationSize
How to set up #Id field properly when database table is already populated JPA
https://vladmihalcea.com/from-jpa-to-hibernates-legacy-and-enhanced-identifier-generators/
And my current attempt is stuck at this point:
URL configUrl = this.getClass().getClassLoader().getResource("hibernate.main.cfg.xml");
Configuration configuration = new Configuration().configure(configUrl);
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
SessionFactorysessionFactory = configuration.buildSessionFactory(serviceRegistry);
Properties generatorProperties = new Properties();
// Properties would normally be read from annotations
// that are present on the entity class
generatorProperties.put("name", "model_sequence");
generatorProperties.put("sequence_name", "model_sequence");
generatorProperties.put("allocation_size", 20);
sequenceGenerator = new SequenceStyleGenerator();
sequenceGenerator.configure(LongType.INSTANCE, generatorProperties, serviceRegistry);
// Without the line below, the queries that Hibernate uses to read
// and alter the sequence table are not initialized (they are null)
// and Hibernate throws an exception
sequenceGenerator.registerExportables( database?? /* This requires a Database object, but how to provide it? */);
// In order to generate a new ID, do the following:
Session session = sessionFactory.getCurrentSession();
Serializable id = sequenceGenerator.generate((SessionImplementor)session, entity?? /* This requires an entity, but that is exactly what I'm trying to omit. */);
My considerations on this approach:
As I understand, Hibernate reserves a block of IDs (rather than just 1 ID). This implies that the block of IDs is maintained in-memory in the SequenceStyleGenerator instance. This would cause two blocks to be present simultaneously: one for the running web application, and one for my ID generation application.
It might be problematic when the sequence table is not locked, in which case both instances may simultaneously read the same value from the table and end up using the same block of IDs.
Do any of you have an idea on how to do this?
I am trying to establish a relationship between 2 entities which would be zero-to-one. That is, the Parent can be saved without the associated Child entity and also along with the assoicated Child.
Following are the 2 Entity classes...
Employee (Parent)
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name="EMP_NAME")
private String name;
#PrimaryKeyJoinColumn
#OneToOne(cascade = {CascadeType.ALL})
private EmployeeInfo info;
#Column(name="EMP_ENUM")
private Integer enumId;
EmployeeInfo (Child)
public class EmployeeInfo {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Column(name="EMPLOYEE_EMAIL")
private String email;
With such kind of a relation and id column of the only Parent (Employee) table set to AUTO INCREMENT in MySql DB, the problem is that while saving a Parent->Child object graph, I get the following exception
org.springframework.orm.hibernate3.HibernateJdbcException: JDBC exception on Hibernate data access: SQLException for SQL [insert into EMP_INFO
Caused by: java.sql.SQLException: Field 'id' doesn't have a default value
I tried setting the Child Table's Id property to AUTO INCREMENT in the DB , and the persistence of such a Parent->Child object graph is successful.
However, the problem described here surfaces, because I have a scenario in which I would like to save the parent (Employee) object without the associated EmpInfo object, and hence do NOT want to have AUTO INCREMENT on the Child's id column.
One solution could be not use the PrimaryKeyJoinColumn, but use a particular JoinColumn, but that adds an unnecessary column to my existing Table.
Has anyone come across such a problem? If yes, any pointers would be much helpful.
Finally, I got it working thanks to Pascal and some googling from my side. Apparently, I cannot use the Native key generator for such relationships where the parent can exist without the child (optional = true).
The thing that worked finally was the following, leaving me the downside of having to deal with Hibernate specific annotation (#GenericGenerator) and also having to make-do with bi-directional relationships instead of the unidirectional that I wanted.
Employee (Parent) class remains unchanged as above. It has AUTO INCREMENT on the Id column.
As for the child class (EmployeeInfo) it changed to the following, and again WITHOUT having the AUTO INCREMENT set on the Id column.
#Table(name="EMP_INFO")
#Entity
public class EmployeeInfo {
#Id
#GeneratedValue(generator="foreign")
#GenericGenerator(name="foreign", strategy = "foreign", parameters={
#Parameter(name="property", value="verifInfo")})
private Long id;
#OneToOne(optional=false)
#JoinColumn (name="id")
private Employee emp;
#Column(name="EMPLOYEE_EMAIL")
private String email;
This helped me achieve what I wanted but on the downside, GenericGenerator is not a JPA annotation, it is a hibernate annotation, and sadly I have to make do with that as of now because JPA does not currently support this(or any similar) annotation.
Anyway, it helps to get through such cases :-)
I have a scenario in which I would like to save the parent (Employee) object without the associated EmpInfo object.
The optional attribute of a OneToOne is true by default, which is what you want.
However, you are somehow misusing the #PrimaryKeyJoinColumn here (well, it actually depends on what you really want to achieve but your current combination of annotations is not correct).
IF you want to map a OneToOne with a shared primary-key, use the #PrimaryKeyJoinColumn. But in that case, don't use a GeneratedValue on EmployeeInfo and set the id manually or, if you don't want to set it manually, use the Hibernate specific foreign generator that I already mentioned in your previous question. Check also the related question mentioned below.
And IF you do not want to use a shared primary key (like in your current code since you're trying to get the id generated by the database), then do not use the PrimaryKeyJoinColumn.
You have to make a choice.
References
JPA 1.0 specification:
9.1.32 PrimaryKeyJoinColumn Annotation
Related question
JPA Hibernate One-to-One relationship.