We using MYSQL and Hibernate for our project.
JPA is used to persist object in DB.
We have Multiple class with similar code
#Entity
#Table(name = "users")
class Users implement Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
.
.
.
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
Now we want to give support for oracle too. How should we do it ?
strategy=GenerationType.AUTO is not supported by oracle.
One soln is we can define sequence in each POJO which we do not want to do?
please provide us some input so that we can move ahead.
The AUTO strategy should work on Oracle as well. The difference with MySQL is that it will use a sequence instead of relying on an auto_increment ID.
You can even control the sequence name per entity if desired: see Hibernate sequence on oracle, #GeneratedValue(strategy = GenerationType.AUTO).
#Id
#SequenceGenerator(name="admin_seq", sequenceName="unique_id")
#GeneratedValue(strategy=GenerationType.AUTO, generator="admin_seq")
private Long id
worked for me , thanks for all your answers
Related
I'm learning Spring and few days ago i started learning Hibernate. I have my studying project where i need to create a shop with products. Here is entity class
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
#Entity
#Table(name = "cart")
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Cart {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "quantity")
private int quantity;
#Column(name = "mask")
private String mask;
#Column(name = "price")
private int price;
So, i create interface
import org.springframework.data.jpa.repository.JpaRepository;
public interface CartRepository extends JpaRepository<Cart, Integer> {
}
and create controllers
#Autowired
CartRepository cartList;
#RequestMapping("/add-black-mask")
public String addBlackMask() {
cartList.save(new Cart(1, 1, "black", 3));
return "masks/add-black-mask";
}
#RequestMapping("/add-build-mask")
public String addBuildMask() {
cartList.save(new Cart(2, 1, "build", 5));
return "masks/add-build-mask";
}
#RequestMapping(value = "/save_product_to_cart")
public ModelAndView saveProduct(#ModelAttribute(value = "cart")
Cart cart, BindingResult result) {
int index = Integer.parseInt(String.valueOf(cartList.count()));
if (cart.getId() == 0) {
cart.setId(index + 1);
cartList.save(cart);
} else {
Cart cart1 = cartList.getOne(cart.getId());
cart1.setMask(cart.getMask());
cart1.setQuantity(cart.getQuantity());
cart1.setPrice(cart.getPrice());
cartList.save(cart1);
}
return new ModelAndView("redirect:/");
}
Also, there are some other controllers for view, Thymeleaf etc, but its ok. My problem is - when i save my product 1 time - its ok, but when i save second - it didnt work( i think because i can't save 2 rows with similar ID) So it seems i have UNIQUE ID in my table and it can not be repeated. Question is - how can i delete unique id or change my code in any way? Thanks in advance!
p.s. i read some other topics here but it didnt help me.
when you use GENERATIONTYPE.IDENTITY you are asking hibernate to let the database handle Ids for you, you should not set it yourself. you are changing the value of the id, just create a new product, set all the fields and inside a transactional context, save your product. Also always use wrapped versions of primitives for serialization purposes. (Long is an object but long is a primitive.) you can google boxing and unboxing and learn more about this.
Let me answer this question:
First of all, using annotations as our configure method is just a convenient method instead of coping the endless XML configuration file.
The #Idannotation is inherited from javax.persistence.Id, indicating the member field below is the primary key of current entity. Hence your Hibernate and spring framework as well as you can do some reflect works based on this annotation. for details please check javadoc for Id
The #GeneratedValue annotation is to configure the way of increment of the specified column(field). For example when using Mysql, you may specify auto_increment in the definition of table to make it self-incremental, and then use
#GeneratedValue(strategy = GenerationType.IDENTITY)
in the Java code to denote that you also acknowledged to use this database server side strategy. Also, you may change the value in this annotation to fit different requirements.
1. Define Sequence in database
For instance, Oracle has to use sequence as increment method, say we create a sequence in Oracle:
create sequence oracle_seq;
2. Refer the database sequence
Now that we have the sequence in database, but we need to establish the relation between Java and DB, by using #SequenceGenerator:
#SequenceGenerator(name="seq",sequenceName="oracle_seq")
sequenceName is the real name of a sequence in Oracle, name is what you want to call it in Java. You need to specify sequenceName if it is different from name, otherwise just use name. I usually ignore sequenceName to save my time.
3. Use sequence in Java
Finally, it is time to make use this sequence in Java. Just add #GeneratedValue:
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq")
The generator field refers to which sequence generator you want to use. Notice it is not the real sequence name in DB, but the name you specified in name field of SequenceGenerator.
4. Complete
So the complete version should be like this:
public class Cart
{
#Id
#SequenceGenerator(name="seq",sequenceName="oracle_seq")
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq")
private Integer id;
}
Now start using these annotations to make your JavaWeb development easier.
On top of that I would like you to understand all 4 ways of ID generation in hibernate. You can think of reading in your free time
GenerationType.AUTO
GenerationType.IDENTITY (your case)
GenerationType.SEQUENCE
GenerationType.TABLE {Rarely used nowdays}
I have this entity:
#Entity
#Table
public class Terminals implements Serializable {
private static final long serialVersionUID = 5288308199642977991L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", updatable = false, nullable = false)
private Long id;
#Column
private int merchant_id;
#Column
private String terminalToken;
.....
}
I tried to use this query:
public Terminals getTerminalToken(String terminalToken) throws Exception {
return entityManager.find(Terminals.class, terminalToken);
}
Looks like it's selecting only the table key.
How I can select the table column terminalToken?
You better use Spring data to build your queries along with JPA Repositories. You will just need to extend the JpaRepository interface, and follow the naming conventions to name your methods.
Your method will look like this:
public List<Terminal> findByTeminalToken(String TerminalToken);
Otherwise you will need to use entityManager.createQuery() method instead of entityManager.find() because the latter one is only used with the id column.
If you are looking for pure java (i am more in favour of Philipp solution) perhaps you wish to check out this solution. https://www.objectdb.com/java/jpa/query/criteria. Sorry for not posting a direct solution but i think it woths more to give you the source.
By the way, why not using spring data? Much easier
Good day Guys,
I want to create Models that don't use the default #Id auto generation for Playframework and Ebeans. I have seen online that there are options for Using GenericModel, however that class doesn't seem to be included in version 2.3x. I have done this in order to workaround it but i still fall short my aim
public class ProductVariants extends Model
{
#Id
String id;
public String getId() {
return (this.id == null) ? UUID.randomUUID().toString() : this.id;
}
public void setId(String id) {
this.id = id;
}
}
The issue with this is that i have to manually set the ID before i can save the object e.g.
productVariant.setId(productVariant.getId());
productVariant.save();
Both for a primary model and all it related models with a OneToMany relationship, and it is currently giving me issues when i bind from the view to the Model object with error ERROR executing DML bindLog[] error[Field 'id' doesn't have a default value]]].
Please any help will be appreciated.
Good day Guys,
I finally fixed this by using the UUID class that ships with the JDK. So when you are creating your Models you create them with the
#Id
public java.util.UUID id
Also in the routes file if you need to map to a record by the ID you can do that by doing something like this
GET /:pid/edit controllers.Application.edit(pid: java.util.UUID)
I have two entities:
#Entity
Article {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
#JoinColumn(name="embed_id", referencedColumnName="id")
#MapKeyColumn(name = "language")
#MapKeyEnumerated(EnumType.ORDINAL)
private Map<Language, Locale> locales;
Article() {
locales.put(Language.CS, new Locale());
locales.put(Language.EN, new Locale());
}
}
#Entity
Locale {
#Id
private Long embed_id;
#Id
private Language language;
#Column(length = 256)
private String name;
}
Thanks to the constructor, I can make sure, that once an Article is instantiated, two Locales (with CascadeType.ALL) are associated with it.
The problem comes when I try to persist such entity - I am getting:
javax.persistence.EntityExistsException:
a different object with the same identifier value was already associated
with the session: org...Locale#org...Locale#2bfdsc64
The problem is that neither embed_id, nor language have assigned values from the Article when persisting and Hibernate does not associate them after persisting Article. How can this be done?
Edit 1:
I checked that when the embed_id and language are set manually, everything works correctly. I just need to tell Hibernate, to set the value of #JoinColumn and #MapKeyColumn according to the #OneToMany relation.
Edit 2:
The problem with MapKeyColumn has an easy solution:
public void addLocale(Language l, Locale locale) {
locale.setLanguage(l);
this.locales.put(l);
}
But I am still unable to tell hibernate to associate the Locale.embed_id from Article.id.
Edit 3:
I can see that the ID of article is properly generated, but then it is not put in the locale:
DEBUG org.hibernate.event.internal.AbstractSaveEventListener -
Generated identifier: 87, using strategy: org.hibernate.id.SequenceGenerator
DEBUG org.hibernate.event.internal.AbstractSaveEventListener -
Generated identifier: component[language,embedId]{language=0, embedId=null}, using strategy: org.hibernate.id.CompositeNestedGeneratedValueGenerator
DEBUG org.hibernate.event.internal.AbstractSaveEventListener -
Generated identifier: component[language,embedId]{language=1, embedId=null}, using strategy: org.hibernate.id.CompositeNestedGeneratedValueGenerator
I guess the problem is, that you want to persist two empty locales. And because you don't use a generator for the id-fields, the locales have the same (empty) primary key and therefore can't be persisted.
I finally found the anwswer! The trick was to create Setter on Article and add Access to id as follows:
#Id
#Getter
#Access(AccessType.PROPERTY)
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
public void setId(Long id) {
this.id = id;
this.getLocale(Language.CS).setEmbedId(id);
this.getLocale(Language.EN).setEmbedId(id);
}
I have a post class and it kind of works, but there's one problem: the primary key doesn't increase.
#Entity
#Table(name="posts")
public class Post extends GenericModel{
#Id
#Column(name="post_id")
public int id;
#Column(name="post_situation")
public String situation;
#Column(name="post_date")
public Date date;
#Column(name="post_userid")
public int userid;
#OneToMany(mappedBy="post", cascade=CascadeType.ALL)
public List<Block> blocks;
public Post addBlock(String content, int position){
Block b = new Block(this, content, position);
b.save();
this.blocks.add(b);
this.save();
return this;
}
public Post(String situation, Date date){
this.situation = situation;
this.date = date;
this.userid = 2;
}
}
When I call it the first time on an empty table, it works fine, but the second time, I'm getting PersistenceException occured : org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
The post_id column always has 0. Any idea how to fix this? I have the #Id annotation in palce..
This is how I have in my controller:
Post p = new Post("Midden in het middenoosten.", new Date()).save();
Any ideas what's causing this problem?
It seems that you want the primary key values to be auto-generated. If that is the case, if you'll need to add the #GeneratedValue annotation to the id attribute, in addition to the #Id annotation. Your code should therefore be:
#Id
#Column(name="post_id")
#GeneratedValue
public int id;
There are several strategies available to generate the Ids. You would have to read up on those to decide if you want to choose the TABLE -based, SEQUENCE -based or the IDENTITY -based strategy (which depends on what your database supports). If you choose a strategy explicitly, the define strategy will be used, instead of the default AUTO strategy. Explicit strategy decisions, are communicated in code as:
#Id
#Column(name="post_id")
#GeneratedValue(strategy=SEQUENCE, generator="POST_SEQ")
public int id;
Without generated values, the default value for integers in Java, i.e. 0 will be persisted for the post_id column. Due to the primary key constraint, you cannot have a second row with the same key, resulting in the described failure.
There are several strategies available to generate id:
GenerationType.AUTO
GenerationType.SEQUENCE
GenerationType.IDENTITY
GenerationType.TABLE
If you want the primary key values to be auto-generated, use GenerationType.AUTO, it works with MySQL.