Bypass GeneratedValue in Hibernate (merge data not in db?) - java

My problem is the same as described in [1] or [2]. I need to manually set a by default auto-generated value (why? importing old data). As described in [1] using Hibernate's entity = em.merge(entity) will do the trick.
Unfortunately for me it does not. I neither get an error nor any other warning. The entity is just not going to appear in the database. I'm using Spring and Hibernate EntityManager 3.5.3-Final.
Any ideas?

Another implementation, way simpler.
This one works with both annotation-based or xml-based configuration: it rely on hibernate meta-data to get the id value for the object. Replace SequenceGenerator by IdentityGenerator (or any other generator) depending on your configuration. (The creation of a decorator instead of subclassing, passing the decorated ID generator as a parameter to this generator, is left as an exercise to the reader).
public class UseExistingOrGenerateIdGenerator extends SequenceGenerator {
#Override
public Serializable generate(SessionImplementor session, Object object)
throws HibernateException {
Serializable id = session.getEntityPersister(null, object)
.getClassMetadata().getIdentifier(object, session);
return id != null ? id : super.generate(session, object);
}
}
Answer to the exercise (using a decorator pattern, as requested), not really tested:
public class UseExistingOrGenerateIdGenerator implements IdentifierGenerator, Configurable {
private IdentifierGenerator defaultGenerator;
#Override
public void configure(Type type, Properties params, Dialect d)
throws MappingException;
// For example: take a class name and create an instance
this.defaultGenerator = buildGeneratorFromParams(
params.getProperty("default"));
}
#Override
public Serializable generate(SessionImplementor session, Object object)
throws HibernateException {
Serializable id = session.getEntityPersister(null, object)
.getClassMetadata().getIdentifier(object, session);
return id != null ? id : defaultGenerator.generate(session, object);
}
}

it works on my project with the following code:
#XmlAttribute
#Id
#Basic(optional = false)
#GeneratedValue(strategy=GenerationType.IDENTITY, generator="IdOrGenerated")
#GenericGenerator(name="IdOrGenerated",
strategy="....UseIdOrGenerate"
)
#Column(name = "ID", nullable = false)
private Integer id;
and
import org.hibernate.id.IdentityGenerator;
...
public class UseIdOrGenerate extends IdentityGenerator {
private static final Logger log = Logger.getLogger(UseIdOrGenerate.class.getName());
#Override
public Serializable generate(SessionImplementor session, Object obj) throws HibernateException {
if (obj == null) throw new HibernateException(new NullPointerException()) ;
if ((((EntityWithId) obj).getId()) == null) {
Serializable id = super.generate(session, obj) ;
return id;
} else {
return ((EntityWithId) obj).getId();
}
}
where you basically define your own ID generator (based on the Identity strategy), and if the ID is not set, you delegate the generation to the default generator.
The main drawback is that it bounds you to Hibernate as JPA provider ... but it works perfectly with my MySQL project

Updating Laurent Grégoire's answer for hibernate 5.2 because it seems to have changed a bit.
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;

I`m giving a solution here that worked for me:
create your own identifiergenerator/sequencegenerator
public class FilterIdentifierGenerator extends IdentityGenerator implements IdentifierGenerator{
#Override
public Serializable generate(SessionImplementor session, Object object)
throws HibernateException {
// TODO Auto-generated method stub
Serializable id = session.getEntityPersister(null, object)
.getClassMetadata().getIdentifier(object, session);
return id != null ? id : super.generate(session, object);
}
}
modify your entity as:
#Id
#GeneratedValue(generator="myGenerator")
#GenericGenerator(name="myGenerator", strategy="package.FilterIdentifierGenerator")
#Column(unique=true, nullable=false)
private int id;
...
and while saving instead of using persist() use merge() or update()

If you are using hibernate's org.hibernate.id.UUIDGenerator to generate a String id I suggest you use:
public class UseIdOrGenerate extends UUIDGenerator {
#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);
}
}

According to the Selectively disable generation of a new ID thread on the Hibernate forums, merge() might not be the solution (at least not alone) and you might have to use a custom generator (that's the second link you posted).
I didn't test this myself so I can't confirm but I recommend reading the thread of the Hibernate's forums.

For anyone else looking to do this, above does work nicely. Just a recommendation to getting the identifier from the object rather than having inheritance for each Entity class (Just for the Id), you could do something like:
import org.hibernate.id.IdentityGenerator;
public class UseIdOrGenerate extends IdentityGenerator {
private static final Logger log = Logger.getLogger(UseIdOrGenerate.class
.getName());
#Override
public Serializable generate(SessionImplementor session, Object object)
throws HibernateException {
if (object == null)
throw new HibernateException(new NullPointerException());
for (Field field : object.getClass().getDeclaredFields()) {
if (field.isAnnotationPresent(Id.class)
&& field.isAnnotationPresent(GeneratedValue.class)) {
boolean isAccessible = field.isAccessible();
try {
field.setAccessible(true);
Object obj = field.get(object);
field.setAccessible(isAccessible);
if (obj != null) {
if (Integer.class.isAssignableFrom(obj.getClass())) {
if (((Integer) obj) > 0) {
return (Serializable) obj;
}
}
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return super.generate(session, object);
}
}

You need a running transaction.
In case your transaction are manually-managed:
entityManager.getTransaction().begin();
(of course don't forget to commit)
If you are using declarative transactions, use the appropriate declaration (via annotations, most likely)
Also, set the hibernate logging level to debug (log4j.logger.org.hibernate=debug) in your log4j.properties in order to trace what is happening in more details.

Related

Hibernate/SpringData : Incorrect dirty check on field with AttributeConverter

I have below entity with custom AttributeConverter which saves field in the DB as binary data.
TaskEntity.java
#Entity
#Table(name = "task")
public class TaskEntity {
#Id
#GeneratedValue
#Column(name = "id", nullable = false)
private UUID id;
#Column(name = "state_machine_context")
#Convert(converter = StateMachineContextConverter.class)
private StateMachineContext<State, Event> stateMachineContext;
}
StateMachineContextConverter.java
#Converter
public class StateMachineContextConverter
implements AttributeConverter<StateMachineContext, byte[]> {
private static final ThreadLocal<Kryo> kryoThreadLocal = ThreadLocal.withInitial(() -> {
Kryo kryo = new Kryo();
kryo.addDefaultSerializer(StateMachineContext.class, new StateMachineContextSerializer());
kryo.addDefaultSerializer(MessageHeaders.class, new MessageHeadersSerializer());
kryo.addDefaultSerializer(UUID.class, new UUIDSerializer());
return kryo;
});
private static final int BUFFER_SIZE = 4096;
private static final int MAX_BUFFERED_SIZE = 10240;
#Override
public byte[] convertToDatabaseColumn(final StateMachineContext attribute) {
return serialize(attribute);
}
#Override
public StateMachineContext convertToEntityAttribute(final byte[] dbData) {
return deserialize(dbData);
}
private byte[] serialize(final StateMachineContext context) {
if (context == null) {
return null;
}
try (Output output = new Output(BUFFER_SIZE, MAX_BUFFERED_SIZE)) {
final Kryo kryo = kryoThreadLocal.get();
kryo.writeObject(output, context);
return output.toBytes();
}
}
private StateMachineContext deserialize(final byte[] data) {
if (data == null || data.length == 0) {
return null;
}
final Kryo kryo = kryoThreadLocal.get();
try (Input input = new Input(data)) {
return kryo.readObject(input, StateMachineContext.class);
}
}
}
So after TaskEntity is selected with SpringData nativeQuery within method with #Transactional annotation UPDATE queries are fired for all retrieved entities.
After investigation I suppose it is happened because of dirty checking of hibernate, as context field is converted from byte[] and for some reasons it is considered dirty by hibernate.
The interesting thing is that making #Transactional(readOnly=true) does not help as Postgres is throwing exception "Could not UPDATE in readOnly transaction" but if I remove #Transactional annotation completely everything works fine and UPDATE queries are not fired after select.
What is the best solution to fix this issue? Maybe it is possible to disable dirty checking for readOnly transactions? Is it possible to rewrite hibernate dirty check for one field? I found that it is possible to overwrite dirty checking completely but I would prefer not to do this.
I faced the same issue when I was using a convertor to convert my JSON field from DB to my custom class.
The Dirty checking policy of Hibernate calls the .equals method on the entity from the Persistent Context (which is saved as soon as you fetch an object from DB) and your current Entity.
So overriding the .equals method of StateMachineContext should do it for you. It actually worked for me.
For reference : https://medium.com/#paul.klingelhuber/hibernate-dirty-checking-with-converted-attributes-1b6d1cd27f68

Migrate from Hibernate 4 to 5

I have some problems with migration from hibernate 4.3 to hibernate 5.
I'm working on a major project where we use SequenceHiLoGenerator to generate id which is deprecated now. How update this without major changes?
This is my code with hibernate 4.3.
public class KSequenceGenerator extends SequenceHiLoGenerator {
#Override
public Serializable generate(SessionImplementor session, Object object) throws HibernateException {
Serializable id = session.getEntityPersister(null, object).getClassMetadata().getIdentifier(object, session);
return id != null ? id : super.generate(session, object);
}
In EJB:
#GenericGenerator(name="id_gen", strategy="com.or.nk.ob.base.KSequenceGenerator", parameters = {#org.hibernate.annotations.Parameter(name = KSequenceGenerator.SEQUENCE, value = "building_id_seq")})
now i have:
public class KSequenceGenerator extends SequenceStyleGenerator {
public static final String SEQUENCE = "sequence";
#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);
}
}
id:
#Id
#Column(name = "ID")
#GeneratedValue(generator = "id_gen", strategy = GenerationType.SEQUENCE)
public Long getId() {
return id;
}

Send String second Argument IdentifierGenerator - Hibernate

I have implemented a custom generator, for my application and I want to send a string as the second argument to the IdentifierGenerator interface but I am not getting any clue how to do this. unfortunately because fo the below code, it is setting null2 as the key generated. please help.
I want to send a String which is the "date" from the client as the second argument.
Thanks.
public class CourierTransImpl implements IdentifierGenerator{
private String appendString;
#Override
public Serializable generate(SessionImplementor session, Object arg1)
throws HibernateException {
Connection connection = session.connection();
int id=0;
try {
PreparedStatement ps = connection
.prepareStatement("SELECT MAX(TRANS_ID) as value from SecurePass.COURIER_TRANSACTIONS_SER_TABLE");
ResultSet rs = ps.executeQuery();
if (rs.next()) {
id = rs.getInt("value");
id++;
}
ps = connection
.prepareStatement("INSERT INTO SecurePass.COURIER_TRANSACTIONS_SER_TABLE VALUES("+id+")");
ps.execute();
} catch (SQLException e) {
e.printStackTrace();
}
return appendString+id;
}
public String getAppendString() {
return appendString;
}
public void setAppendString(String appendString) {
this.appendString = appendString;
}
}
You can implement the Configurable interface and override the configure for your requirement. By doing this you can only pass a static value as a parameter to CourierTransImpl class
If you want to pass some dynamic values then you can have a #Transient property defined in your entity and then access that property in your CourierTransImpl class.
Detailed explanation:
For example, lets says there is an entity called Employee and it has a transient property called empType then you can define the entity like this.
#Entity
public class Employee {
#Id
#GeneratedValue(generator = "UniqueIdGenerator")
#GenericGenerator(name = "UniqueIdGenerator", strategy = "com.CourierTransImpl",
parameters = { #Parameter(name = "appendString", value = "Emp") })
private String id;
private String name;
#Transient
private String empType;
// Getters & Setters
}
In above code you can see that we set the parameter appendString and this is a static value that we are setting here as "Emp".
Now the CourierTransImpl class that implements Configurable interface:
public class CourierTransImpl implements IdentifierGenerator, Configurable {
private String appendString;
#Override
public Serializable generate(SessionImplementor session, Object object)
throws HibernateException {
Connection connection = session.connection();
int id = 0;
try {
Employee emp = (Employee) object;
id = ..; // your logic to get the id from database
// Now you can use the parameter appendString which is static value set to "Emp"
// You can also access any of the employee properties here, so in your code you can set the required value dynamically.
return appendString + emp.getEmpType()+id;
} catch (Exception e) {
e.printStackTrace();
}
return appendString + id;
}
#Override
public void configure(Type type, Properties params, Dialect d)
throws MappingException {
setAppendString(params.getProperty("appendString")); // Here we are setting the parameters.
}
// Setters & Getters
}
In this example if I create an object of Employee and set the empType to some value say "Manager", then the hibernate generates and id like "Emp1Manager".
Your question is not clear but the reason it is showing null2 is that your appendString is null and not initialized.I guess you need to set the appendString to the date.

Using UUID with EclipseLink and PostgreSQL

I want to use the PostgreSQL uuid type for objects' primary keys.
For that I've created a converter (implementing the Converter interface).
Bellow is the relevant code:
#Override
public void initialize(DatabaseMapping mapping, Session session) {
final DatabaseField field;
if (mapping instanceof DirectCollectionMapping) {
field = ((DirectCollectionMapping) mapping).getDirectField();
} else {
field = mapping.getField();
}
field.setSqlType(Types.OTHER);
field.setTypeName("uuid");
field.setColumnDefinition("UUID");
}
Then I've annotated the relevant entity X with the bellow annotations:
#Converter(name="uuidConverter",converterCalss=UUIDConverter.class)
#Convert("uuidConverter")
#Id
public UUID getId()
{
return id;
}
The problem is that I have another class (Y) which has the following definition:
#ManyToOne(targetEntity = X.class)
#JoinColumn(name = "x_id")
public X getX();
Although EclipseLink created the tables as expected it sends a string to the database when trying to insert objects of type Y.
Postgres returns the following error message:
column "id" is of type uuid but expression is of type character varying at character
Any solutions / work around will be appreciated.
I had the same issue with EclipseLink JPA + Postgresql + UUID as primary key.
To solve it, I've merged codes from Github and below link:
https://forums.oracle.com/forums/thread.jspa?messageID=4584157
The below code for UUIDConverter worked for me, though the code surely isn't the best.
public void initialize(DatabaseMapping ARGMapping, Session ARGSession)
{
final DatabaseField Field;
if (ARGMapping instanceof DirectCollectionMapping)
{
Field = ((DirectCollectionMapping) ARGMapping).getDirectField();
}
else
{
Field = ARGMapping.getField();
}
Field.setSqlType(Types.OTHER);
Field.setTypeName("uuid");
Field.setColumnDefinition("UUID");
for (DatabaseMapping m : ARGMapping.getDescriptor().getMappings())
{
assert OneToOneMapping.class.isAssignableFrom(ManyToOneMapping.class);
if (m instanceof OneToOneMapping)
{
for (DatabaseField field : ((OneToOneMapping) m).getForeignKeyFields())
{
field.setSqlType(Types.OTHER);
field.setColumnDefinition("UUID");
field.setTypeName("uuid");
}
}
}
}
I had some issues with EclipseLink JPA 2.1 + Postgresql + UUID as primary key but I find out different solution. I adopted AttributeConverter but I faced a problem with EclipseLink implementation that I resolved with this code:
#javax.persistence.Converter(autoApply = true)
public class PostgresUuidConverter implements AttributeConverter<UUID, Object> {
#Override
public Object convertToDatabaseColumn(UUID uuid) {
PostgresUuid object = new PostgresUuid();
object.setType("uuid");
try {
if (uuid == null) {
object.setValue(null);
} else {
object.setValue(uuid.toString());
}
} catch (SQLException e) {
throw new IllegalArgumentException("Error when creating Postgres uuid", e);
}
return object;
}
#Override
public UUID convertToEntityAttribute(Object dbData) {
if (dbData instanceof String) {
return UUID.fromString(dbData.toString());
} else {
return (UUID) dbData;
}
}
}
public class PostgresUuid extends PGobject implements Comparable<Object> {
private static final long serialVersionUID = 1L;
#Override
public int compareTo(Object arg0) {
return 0;
}
}
As I exaplined in detail in this post http://blog-ungarida.rhcloud.com/persisting-uuid-in-postgresql-using-jpa-eclipselink/
Try checking what the fieldClassification of the mapping is in the initialize method. It might be getting String.class somehow, try setting it to Object.class.
or, field.setType(Object.class)
It seems there is a bug/incompatibility between EclipseLink and PostgresQL. If you just use UUID for primary keys you should be okay. But if you have a nullable UUID column, and you try to store null in it, you will get the reported error:
column "whatever" is of type uuid but expression is of type character varying
See: https://bugs.eclipse.org/bugs/show_bug.cgi?id=538138 (log in and vote for it if you have the time!)
That bug report proved very useful to me. Specifically the link to the forum thread at:
https://www.eclipse.org/forums/index.php?t=msg&th=1073632&goto=1719530&#msg_1719530
I tried all sorts of solutions from here on SO, and elsewhere on the web. The only one that seemed to work for me was the one posted by David Wheeler there. Specifically, creating a cast from character varying to uuid in the database.
Note that you have to be user postgres to create the cast:
$ sudo su - postgres
$ psql <your database name>
# drop cast if exists (character varying as uuid);
# create or replace function uuid(_text character varying) returns uuid language sql as 'select uuid_in(_text::cstring)';
# create cast (character varying as uuid) with function uuid(character varying) as assignment;
For completeness here is the rest of what I use (in case it helps)
All my entities (that have a UUID primary key) extend a base class called EntityBase:
package com.example.entity;
import java.io.Serializable;
import java.util.UUID;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import org.eclipse.persistence.annotations.Convert;
import org.eclipse.persistence.annotations.Converter;
import com.example.converter.UUIDTypeConverter;
#MappedSuperclass
#Converter(name="uuidConverter", converterClass=UUIDTypeConverter.class)
public class EntityBase implements Serializable, Cloneable
{
private static final long serialVersionUID = 1L;
#Id
#Convert("uuidConverter")
private UUID id;
public EntityBase() {
this.id = UUID.randomUUID();
}
#Override
public int hashCode() {
return id.hashCode();
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof EntityBase)) {
return false;
}
EntityBase other = (EntityBase) obj;
return getId().equals(other.getId());
}
public UUID getId()
{
return this.id;
}
public void setId(UUID id)
{
this.id = id;
}
}
The UUID converter class looks like this:
package com.example.converter;
import java.sql.Types;
import java.util.UUID;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.converters.Converter;
import org.eclipse.persistence.sessions.Session;
public class UUIDTypeConverter implements Converter
{
#Override
public UUID convertObjectValueToDataValue(Object objectValue, Session session)
{
return (UUID) objectValue;
}
#Override
public UUID convertDataValueToObjectValue(Object dataValue, Session session)
{
return (UUID) dataValue;
}
#Override
public boolean isMutable()
{
return true;
}
#Override
public void initialize(DatabaseMapping mapping, Session session)
{
DatabaseField field = mapping.getField();
field.setSqlType(Types.OTHER);
field.setTypeName("java.util.UUID");
field.setColumnDefinition("UUID");
}
}
If you have entities that have UUID columns that are not primary keys, you can annotate them as follows:
import org.eclipse.persistence.annotations.Convert
import org.eclipse.persistence.annotations.Converter;
#Entity
#Converter(name="uuidConverter", converterClass=UUIDTypeConverter.class)
public class BillingEvent extends EntityBase
{
#Convert("uuidConverter")
private UUID entityId;
}
Note that if that entity has other columns that use the standard javax.persistence.convert annotation, you'll need to differentiate the two Convert annotations to avoid a compile error.
For example:
import javax.persistence.Convert;
import org.eclipse.persistence.annotations.Converter;
#Entity
#Converter(name="uuidConverter", converterClass=UUIDTypeConverter.class)
public class BillingEvent extends EntityBase
{
#org.eclipse.persistence.annotations.Convert("uuidConverter")
private UUID entityId;
#Convert(converter = JSR310InstantTypeConverter.class)
private Instant createdOn;
}
I hope this saves others some time. Good luck!
Universal UUIDConverter for EclipseLink (not only PostgreSQL)
Code:
import java.nio.ByteBuffer;
import java.util.UUID;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.DirectCollectionMapping;
import org.eclipse.persistence.mappings.converters.Converter;
import org.eclipse.persistence.sessions.Session;
public class UUIDConverter implements Converter {
private Boolean isUUIDasByteArray = true;
#Override
public Object convertObjectValueToDataValue(Object objectValue,
Session session) {
if (isUUIDasByteArray) {
UUID uuid = (UUID)objectValue;
if (uuid == null) return null;
byte[] buffer = new byte[16];
ByteBuffer bb = ByteBuffer.wrap(buffer);
bb.putLong(uuid.getMostSignificantBits());
bb.putLong(uuid.getLeastSignificantBits());
return buffer;
}
return objectValue;
}
#Override
public UUID convertDataValueToObjectValue(Object dataValue,
Session session) {
if (isUUIDasByteArray) {
byte[] bytes = (byte[])dataValue;
if (bytes == null) return null;
ByteBuffer bb = ByteBuffer.wrap(bytes);
long high = bb.getLong();
long low = bb.getLong();
return new UUID(high, low);
}
return (UUID) dataValue;
}
#Override
public boolean isMutable() {
return true;
}
#Override
public void initialize(DatabaseMapping mapping, Session session) {
final DatabaseField field;
if (mapping instanceof DirectCollectionMapping) {
// handle #ElementCollection...
field = ((DirectCollectionMapping) mapping).getDirectField();
} else {
field = mapping.getField();
}
if (session != null && session.getLogin()!= null && session.getLogin().getPlatform() != null) {
String platform = session.getLogin().getPlatform().getClass().getSimpleName();
if (platform.equals("PostgreSQLPlatform")) {
field.setSqlType(java.sql.Types.OTHER);
field.setTypeName("java.util.UUID");
field.setColumnDefinition("UUID");
isUUIDasByteArray = false;
} else if (platform.equals("H2Platform")) {
field.setColumnDefinition("UUID");
} else if (platform.equals("OraclePlatform")) {
field.setColumnDefinition("RAW(16)");
} else if (platform.equals("MySQLPlatform")) {
field.setColumnDefinition("BINARY(16)");
} else if (platform.equals("SQLServerPlatform")) {
field.setColumnDefinition("UNIQUEIDENTIFIER");
}
}
}
}
You don't need a converted. Use this column definition in the entity. You need to register the uuid extension first. This works with Postgres 10 and Wildfly 10.1
#Column(name = "UUID", nullable=false, insertable = false, columnDefinition="uuid DEFAULT uuid_generate_v4()")
private String uuid;

Java EE 6, EJB 3.1, JPA 2.0 - error in inserting record in database

I am trying to insert a record in the database (using Java EE 6, EJB 3.1, JPA 2.0). I am getting an error that accountTypeId field is null, but i have set it up as autogenerate. Can anyone please suggest what am I doing wrong?
Following is the create table query:
create table example.account_type(
account_type_id INT NOT null PRIMARY KEY GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
account_type_desc varchar(20)
);
Following is the entity class:
EDIT: Updated the entity class as generated by NetBeans which didn't work. I also added #GeneratedValue annotation but still it didn't work.
#Entity
#Table(name = "ACCOUNT_TYPE")
#NamedQueries({
#NamedQuery(name = "AccountType.findAll", query = "SELECT a FROM AccountType a"),
#NamedQuery(name = "AccountType.findByAccountTypeId", query = "SELECT a FROM AccountType a WHERE a.accountTypeId = :accountTypeId"),
#NamedQuery(name = "AccountType.findByAccountTypeDesc", query = "SELECT a FROM AccountType a WHERE a.accountTypeDesc = :accountTypeDesc")})
public class AccountType implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY) // ADDED THIS LINE
#Basic(optional = false)
#NotNull
#Column(name = "ACCOUNT_TYPE_ID")
private Integer accountTypeId;
#Size(max = 50)
#Column(name = "ACCOUNT_TYPE_DESC")
private String accountTypeDesc;
public AccountType() {
}
public AccountType(Integer accountTypeId) {
this.accountTypeId = accountTypeId;
}
public Integer getAccountTypeId() {
return accountTypeId;
}
public void setAccountTypeId(Integer accountTypeId) {
this.accountTypeId = accountTypeId;
}
public String getAccountTypeDesc() {
return accountTypeDesc;
}
public void setAccountTypeDesc(String accountTypeDesc) {
this.accountTypeDesc = accountTypeDesc;
}
#Override
public int hashCode() {
int hash = 0;
hash += (accountTypeId != null ? accountTypeId.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof AccountType)) {
return false;
}
AccountType other = (AccountType) object;
if ((this.accountTypeId == null && other.accountTypeId != null) || (this.accountTypeId != null && !this.accountTypeId.equals(other.accountTypeId))) {
return false;
}
return true;
}
#Override
public String toString() {
return "Entities.AccountType[ accountTypeId=" + accountTypeId + " ]";
}
}
Following is the session bean interface:
#Remote
public interface AccountTypeSessionBeanRemote {
public void createAccountType();
public void createAccountType(String accDesc);
}
Following is the session bean implementation class:
#Stateless
public class AccountTypeSessionBean implements AccountTypeSessionBeanRemote {
#PersistenceContext(unitName="ExamplePU")
private EntityManager em;
#Override
public void createAccountType(String accDesc) {
AccountType emp = new AccountType(accDsc);
try {
this.em.persist(emp);
System.out.println("after persist");
} catch(Exception ex) {
System.out.println("ex: " + ex.getMessage());
}
}
}
Following is the Main class:
public class Main {
#EJB
private static AccountTypeSessionBeanRemote accountTypeSessionBean;
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
accountTypeSessionBean.createAccountType("test");
}
}
Following is the error:
INFO: ex: Object: Entities.AccountType[ accountTypeId=null ] is not a known entity type.
You are not getting an error because of "accountTypeId field is null". As the error message says, the error occurs because "Entities.AccountType[ accountTypeId=null ] is not a known entity type".
The most likely reason is that AccountType is not annotated with #Entity. This problem is likely solved by adding it. Additionally it makes sense to use
#GeneratedValue(strategy = GenerationType.IDENTITY)
instead of AUTO. Auto means that the provider chooses a strategy based on the capabilities of the target database. According to the table definition it seems clear that the preferred strategy is IDENTITY.
I changed my create table query as following:
create table example.account_type(
account_type_id INT NOT null PRIMARY KEY,
account_type_desc varchar(20)
);
Then had to add the following line to the entity class (Netbeans doesn't add that):
#GeneratedValue(strategy = GenerationType.AUTO)

Categories