I have created a trigger so that my entities ids are autogenerated with a sequence each time they're inserted into my Oracle database.
The problem comes with annotating these entities for Hibernate/JPA: I need to define a #GeneratedValue annotation but I don't want to specify the sequence name -- doing that will make Hibernate query the sequence first, then insert, which is a work that is already done by the trigger.
Is there any way to skip this sequence in the #GeneratedValue with the scenario I've proposed?
Exception I get if id is not provided:
org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save(): Pattern
Pattern class:
#Entity
#Table(name = "PATTERN")
public class Patron extends HistoricoAbstractEntity {
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ID_PATTERN")
private Integer idPattern;
#Column
private String description;
#Column(name = "NEGATIVE")
private Boolean isNegative;
#Column(name = "type")
private Integer type;
#Column(name = "N_DAYS")
private Integer numDays;
... (getters & setters)
}
From what your code,
What I can tell you is that its not related to #GeneratedValue, it specifies that the hibernate takes responsibility to generate and idetifier for your entity. In your case your are generating id your self, so you have to manually set the id for that particular entity. Then you won't get this error any more, the other thing that you can try is use of #PrePersist annotate a method with this and try assigning a value to id in it. I haven't tried this but this should work according to this answer on SO.
Assign Custom Identifier
If your id is being generated by the database then you should use #GeneratedValue(strategy=GenerationType.AUTO) on your id field along with your #Id annotation.
Related
I am new in Spring Data, and I need to establish the impossibility of creating a new entity in DB if an entity already exists with the same field values.
Comparison condition: if "closeType" field and "id" agreement field of a new entity equal to database entity fields, I can't add this entity to DB. How do it?
My entity:
#Entity
#Table(name = "contract")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Contract implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "contractGenerator")
#SequenceGenerator(name = "contractGenerator", sequenceName = "contract_sequence")
private Long id;
#Column(name = "start_date")
private LocalDate startDate;
#Column(name = "end_date")
private LocalDate endDate;
#Column(name = "first_pay_date")
private LocalDate firstPayDate;
#Column(name = "next_pay_date")
private LocalDate nextPayDate;
//Here is thу first field for comparison
#Enumerated(EnumType.STRING)
#Column(name = "close_type")
private CloseType closeType;
#ManyToOne
#JsonIgnoreProperties("")
private Mentor mentor;
//Here is second ID agreement field for comparison
#ManyToOne
#JsonIgnoreProperties("")
private Agreement agreement;
...............
//getters and setters
I have to block possibility to create several active contracts("closeType") in one agreement ("id")
I have to block possibility to create several active
contracts("closeType") in one agreement ("id")
you could use UniqueConstraint How to introduce multi-column constraint with JPA annotations?
...
#Table(uniqueConstraints={
#UniqueConstraint(columnNames = {"close_type", "agreement"})
})
Contract implements Serializable {
...
}
Thanks, maybe you could hint me how to set constraint for "closeType"
if, for example, only closeType fields with Null value will be uniqe?
But other values of closeType wont be uniqe
How to annotate unique constraint with WHERE clause in JPA says:
creating partial indexes (CREATE INDEX ... ON ... WHERE) using JPA
aren't specified by JPA. You cannot use a unique constraint for this
purpose because unique partial indexes are not unique constraints.
Some JPA providers offer extension annotations specific to that JPA
provider that add features for running native DDL scripts, defining
indexes with annoations, etc. Since you haven't mentioned which JPA
provider you are using I can't tell you more. Here's the documentation
for EclipseLink index DDL;
I suggest you to have a look at the
How to annotate unique constraint with WHERE clause in JPA
I created a field annotated with the #ReadOnlyProperty so I can track a computed field value. However, when I make a normal findAll query, I receive a SQL Error warning that this column (hasChildren) does not exist. I already tried removing the GET method, but it does not solve the problem at all. I also tried using the #Transient annotation but I could not save the value on my property.
#Entity
#Table
public class Parent extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToMany( fetch = FetchType.LAZY)
private List<Child> childs;
// Computed column that come from #Query annotation
#ReadOnlyProperty
private boolean hasChildren;
//Getters
//Setters
}
Try annotating the field with JPAs #Transient. This should tell it not to persist it in the database.
I am absolutly new in Hibernate development and I have the following problem.
I have 2 entity classes that maps 2 DB tables:
1) The first entity class (the main one) is named KM_ProjectInfo and map a DB table named KM_PROJECT.
2) The second entity class is named KM_ProjectInfoStatus and map a DB table named KM_PROJECT_INFO_STATUS.
So the second one represent a specific field of the first one (a status of the row representd by an instance of the KM_ProjectInfo class). Infact I have something like this:
1) KM_ProjectInfo class:
#Entity
#Table(name = "KM_PROJECT")
public class KM_ProjectInfo implements Serializable {
#Id
#GeneratedValue
private Long idProjectInfo;
#Column(name = "name")
private String name;
#Column(name = "technology")
private String technology;
#ManyToOne
#JoinColumn(name = "idCountry")
private KMCountry country;
#Column(name = "power")
private long power;
#Column(name = "cod")
private String cod;
#ManyToOne
#JoinColumn(name = "idProjectInfoStatus")
private KM_ProjectInfoStatus status;
// GETTERS & SETTERS
}
2) KM_ProjectInfoStatus:
#Entity
#Table(name = "KM_PROJECT_INFO_STATUS")
public class KM_ProjectInfoStatus implements Serializable {
#Id
#GeneratedValue
private Long idProjectInfoStatus;
#Column(name = "foldertech")
private Long foldertech;
#Column(name = "folderproject")
private Long folderproject;
// GETTERS & SETTERS
}
So, as you can see in the previous snippet, the KM_ProjectInfoStatuss is a field of the KM_ProjectInfo because I want that it contains the primary key of this table as foreign key.
In the logic of my application I want that at one row of the KM_PROJECT table (so at one instance of the KM_ProjectInfo entity class) is associated a single row of the KM_PROJECT_INFO_STATUS (one instance of the KM_ProjectInfoStatus entity class) because it represent a specific status for the KM_PROJECT row.
In my code I have:
#ManyToOne
#JoinColumn(name = "idProjectInfoStatus")
private KM_ProjectInfoStatus status;
but I think that is wrong because at one row of my first table it is associated a specific single row of the second table. But maybe I am missing something about how Hibernate work.
Can you help me to understand what I am missing? What it work? Why I have #ManyToOne instead #OneToOne?
Tnx
It all depends on how you want to model things. In terms of Database structure, OneToOne and ManyToOne are implemented in the same way:
One or more JoinColumns which makes a foreign key pointing to the primary key of the other table.
So both solutions correctly map to your database, but it depends if you want to allow several KM_ProjectInfo to point to the same KM_ProjectInfoStatus, or only allow a single one.
Note that, even though you would declare a OneToOne, you could still end up with multiple KM_ProjectInfo pointing to the same KM_ProjectInfoStatus if you don't manipulate Hibernate properly.
Here you did not declare the reverse relationship, but if you did, the declaration would have to be different:
In case of a OneToOne, you would have a KM_ProjectInfo member
In case of a OneToMany (reverse of ManyToOne), you would have a Collection<KM_ProjectInfo> member
From the description it seems you want to have one-to-one relationship. That is the project entity should have its very own status not shared by any other project. You could achieve this by using #OneToOne as below.
#Entity
#Table(name = "KM_PROJECT")
public class KM_ProjectInfo implements Serializable {
#Id
#GeneratedValue
private Long idProjectInfo;
#OneToOne
#JoinColumn(name = "idProjectInfoStatus")
private KM_ProjectInfoStatus status;
}
#Entity
#Table(name = "KM_PROJECT_INFO_STATUS")
public class KM_ProjectInfoStatus implements Serializable {
#Id
#GeneratedValue
private Long idProjectInfoStatus;
#OneToOne(mappedBy="idProjectInfoStatus")
private KM_ProjectInfo project;
}
This way you can have specific status for the KM_PROJECT.
Coming back to #ManyToOne, you will want to have this if you want to share the same status with multiple projects, but that's not what you want in your case. I have tried to explain mappings in simple way here One-to-One mapping.
In my Hibernate Application i'm using create a ValueObject class
#Entity
#Table(name="user")
public class UserVO{
#Id
#Column(name="S_ID")
private String s_id;
#Column(name="FIRSTNAME")
private String firstName;
#Column(name="LASTNAME")
private String lastName;
}
and in my Service class i'm writing like this
public void createOrUpdateUser(UserVO userVO) {
userDAO.createOrUpdateUser(userVO);
}
and in my DAO class i'm writing like this
private EntityManager entityManager;
public void createOrUpdateUser(UserVO userVO) throws DataAccessException {
entityManager.persist(userVO);
}
now i'm calling createOrUpdateUser(userVO) but it give error
Caused by: org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save()
Actually my data base i have created one trigger for user table to generate unique id for s_id column is their any problem for trigger..please suggest me..
If you are using a trigger, the intended generation strategy is org.hibernate.id.SelectGenerator. However, in order to use this strategy, Hibernate must be able to locate the inserted row after insertion to see what value the trigger assigned there are 2 ways to do this.
First is to specifically configure the generator to tell it a column that define a unique key (at least logically) within the table:
#Id
#Column(name="S_ID")
#GeneratedValue( strategy = "trigger" )
#GenericGenerator(
name="trigger", strategy="org.hibernate.id.SelectGenerator",
parameters = {
#Parameter( name="keys", value="userName" )
}
)
private String s_id;
private String userName;
The other is via Hibernate's natural-id support:
#Id
#Column(name="S_ID")
#GeneratedValue( strategy = "trigger" )
#GenericGenerator( name="trigger", strategy="org.hibernate.id.SelectGenerator" ) )
private String s_id;
#NaturalId
private String userName;
GenerationType.IDENTITY may work for you. It will really come down to the JDBC driver and how (if) it implements getGeneratedKeys
ID column must not be null, whatever you do in database driver, will only be trigger before/after insert or any other operation, But according to error it is giving error before any error.
Set something to ID values or try something
#GeneratedValue(strategy = GenerationType.IDENTITY)
as #vinit said :
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
#Column(name="S_ID")
private String s_id;
is the best choice ...
This question already has answers here:
Bypass GeneratedValue in Hibernate (merge data not in db?)
(8 answers)
Closed 5 years ago.
I have following definition in Employee class
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "employee_id")
private Integer employeeId;
Now I want to import existing employees with existing employee IDs. Even if I set the employee ID before saving, the assigned ID is ignored and auto incremented ID is stored. How do we override this?
I had similar problem for composite key which has been explained here
I wrote my own generator to solve the issue.
public class UseExistingIdOtherwiseGenerateUsingIdentity extends IdentityGenerator {
#Override
public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
Serializable id = session.getEntityPersister(null, object).getClassMetadata().getIdentifier(object, session);
return id != null ? id : super.generate(session, object);
}
}
and use it like this: (replace the package name)
#Id
#GenericGenerator(name = "UseExistingIdOtherwiseGenerateUsingIdentity", strategy = "{package}.UseExistingIdOtherwiseGenerateUsingIdentity")
#GeneratedValue(generator = "UseExistingIdOtherwiseGenerateUsingIdentity")
#Column(unique = true, nullable = false)
protected Integer id;
You cannot override it. First of all JPA do not provide way to do it. Second problem is that GenerationType.AUTO can produce such a column type that will not accept user assigned value via SQL statement.
Perform import of data manually and do not try to use application itself as an import tool.
you must override getEmployeeId() method and use Annotation on this method
in this case JPA uses the getter methods for all fields in class and you must move other Annotaions on getter methods
In this case, it might be best to have a #MappedSuperclass
#MappedSuperclass
public AbstractEmployee {
// declare all properties, getters, setters except for the id
}
#Entity
#Table(name="EMPLOYEE")
public class Employee extends AbstractEmployee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "employee_id")
private Integer employeeId;
...
}
#Entity
#Table(name="EMPLOYEE")
public class EmployeeWithAssignedId extends AbstractEmployee {
#Id
#Column(name = "employee_id")
private Integer employeeId;
...
}
So when you want to assign the ID manually, use the second class. I have not tested this, but I believe it should work for your situation. I don't believe there's any rule in JPA that says two classes can't have the same table name :)