I have a working Java EE application that is hosted on JBoss. It uses EclipseLink to manage data in a Postgres database. I am now moving the entity classes to a separate jar so that they can be shared by other components. After doing this, Postgres is giving the following error:
ERROR: operator does not exist: uuid = bytea at character 209
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
It looks like my converter class is not being called. Here is what my converter class looks like:
package model.util;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.converters.Converter;
import org.eclipse.persistence.sessions.Session;
import java.sql.Types;
import java.util.UUID;
public class PgUuidConverter
implements Converter
{
#Override
public boolean isMutable ()
{
return false;
}
#Override
public Object convertDataValueToObjectValue (Object value, Session session)
{
return (UUID) value;
}
#Override
public Object convertObjectValueToDataValue (Object value, Session session)
{
return (UUID) value;
}
#Override
public void initialize (DatabaseMapping mapping, Session session)
{
mapping.getField ().setSqlType (Types.OTHER);
}
}
And here is how I'm using it in my entities:
package model;
import model.util.PgUuidConverter;
import org.eclipse.persistence.annotations.Convert;
import org.eclipse.persistence.annotations.Converter;
import java.util.UUID;
import javax.persistence.*;
#Entity
#Table (
name = "item",
schema = "main"
)
#Converter (
name = "uuid",
converterClass = PgUuidConverter.class
)
public class Item
{
public Item () {}
#Id
#Column (
name = "item_id",
unique = true,
nullable = false
)
private UUID itemId;
#Column (name = "layer_id")
#Convert ("uuid")
private UUID layerId;
public UUID getItemId ()
{
return this.itemId;
}
public UUID getLayerId ()
{
return this.layerId;
}
public void setItemId (UUID itemId)
{
this.itemId = itemId;
}
public void setLayerId (UUID layerId)
{
this.layerId = layerId;
}
}
Is there some kind of configuration that I'm missing?
It looks like the problem is one that appears obvious now. I need to apply the Convert annotation to all UUID fields.
Previously I had only applied the Convert annotation to nullable UUID fields since Postgres was only giving me type errors when UUID fields were set to NULL. Now Postgres is throwing exceptions for all of the UUID fields that don't have Convert annotations.
Related
I am using Oracle database and have to define a UUID column. I followed other posts and created the table using regex pattern for primary key:
CREATE TABLE TEST_UUID (
ID_UUID VARCHAR(255)
DEFAULT REGEXP_REPLACE(RAWTOHEX(SYS_GUID()), '([A-F0-9]{8})([A-F0-9]{4})([A-F0-9]{4})([A-F0-9]{4})([A-F0-9]{12})', '\1-\2-\3-\4-\5'),
NAME VARCHAR2(40) NOT NULL
);
In my entity class I have to define the ID_UUID column of type UUID (cannot define it as String). The entity class looks like this:
import java.util.UUID;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "test_uuid")
public class TestUuid {
#Id
#Column(name = "id_uuid")
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID idUuid;
#Column(name = "name")
private String name;
/**
* #return the idUuid
*/
public UUID getIdUuid() {
return idUuid;
}
/**
* #param idUuid the idUuid to set
*/
public void setIdUuid(UUID idUuid) {
this.idUuid = idUuid;
}
/**
* #return the name
*/
public String getName() {
return name;
}
/**
* #param name the name to set
*/
public void setName(String name) {
this.name = name;
}
}
When I retrieve the table values I don't get the same value for ID_UUID whats stored in the database because of UUID data type (if I change it to a String I am able to pull the correct value). My limitation is to use the UUID type as I am migrating from Postgre to Oracle database and cannot change my entity class. Is it possible to retrieve the correct value using UUID data type?
You need to define custom type that stores and retrieves UUID type as VARCHAR.
See Hibernate UUID type implementation.
https://github.com/hibernate/hibernate-orm/blob/main/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/UUIDJavaType.java
In case if you have Hibernate 5 or higher you can use it.
Create own user type
public class UUIDType extends AbstractSingleColumnStandardBasicType<UUID> {
public static final UUIDType INSTANCE = new UUIDType();
public static final String NAME = "UUIDType";
public UUIDType() {
super(VarcharTypeDescriptor.INSTANCE, UUIDTypeDescriptor.INSTANCE);
}
#Override
public String getName() {
return NAME;
}
}
Implement a UUIDTypeDescriptor for storing and retrieving UUID as VARCHAR
public class UUIDTypeDescriptor extends AbstractTypeDescriptor<UUID> {
public static final UUIDTypeDescriptor INSTANCE = new UUIDTypeDescriptor();
public UUIDTypeDescriptor() {
super(UUID.class, ImmutableMutabilityPlan.INSTANCE);
}
#Override
public String toString(UUID uuid) {
return uuid.toString();
}
#Override
public UUID fromString(String s) {
return UUID.fromString(s);
}
#Override
public <T> T unwrap(UUID uuid, Class<T> type, WrapperOptions wrapperOptions) {
if (uuid == null) return null;
if (String.class.isAssignableFrom(type)) {
return (T) uuid.toString();
}
throw unknownUnwrap(type);
}
#Override
public <T> UUID wrap(T value, WrapperOptions wrapperOptions) {
if (value == null)
return null;
if(value instanceof String) {
return UUID.fromString((String) value);
}
throw unknownWrap(value.getClass());
}
}
Apply custom type to your entity
Specific for entity:
#TypeDef(name = UUIDType.NAME,
typeClass = UUIDType.class,
defaultForType = UUID.class)
#Entity
#Table(name = "test_uuid")
public class TestUUID {
}
or on package lavel in package-info.java file:
#TypeDef(
name = UUIDType.NAME,
typeClass = UUIDType.class,
defaultForType = UUID.class
)
package com.model;
I have the following two entity classes.
The first class is SampleApiEntity:
package my.company.rest;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.hibernate.annotations.Type;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.UUID;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
#ApiModel (
value = "SampleApiEntity",
description = "This is a sample entity from the Api package."
)
#Entity
public class SampleApiEntity
implements Serializable
{
public SampleApiEntity () {}
private static final long serialVersionUID = 1L;
#Column (nullable = false)
#ApiModelProperty (
value = "level",
required = true
)
private Integer level;
#Column (length = 255)
#ApiModelProperty (
value = "description"
)
private String description;
#Column (nullable = false)
#ApiModelProperty (
value = "time",
required = true
)
private Timestamp time;
#Id
#Column (
unique = true,
nullable = false
)
#Type (type = "pg-uuid")
#ApiModelProperty (
value = "id",
readOnly = true,
dataType = "uuid",
example = "123e4567-e89b-12d3-a456-426655440000"
)
private UUID sampleApiEntityId;
public String getDescription ()
{
return this.description;
}
public Integer getLevel ()
{
return this.level;
}
public UUID getSampleApiEntityId ()
{
return this.sampleApiEntityId;
}
public Timestamp getTime ()
{
return this.time;
}
public void setDescription (String description)
{
this.description = description;
}
public void setLevel (Integer level)
{
this.level = level;
}
public void setSampleApiEntityId (UUID sampleApiEntityId)
{
this.sampleApiEntityId = sampleApiEntityId;
}
public void setTime (Timestamp time)
{
this.time = time;
}
}
The second class is SampleModelEntity:
package my.company.model;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.hibernate.annotations.Type;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.UUID;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
#ApiModel (
value = "SampleModelEntity",
description = "This is a sample entity from the Model package."
)
#Entity
public class SampleModelEntity
implements Serializable
{
public SampleModelEntity () {}
private static final long serialVersionUID = 1L;
#Column (nullable = false)
#ApiModelProperty (
value = "level",
required = true
)
private Integer level;
#Column (length = 255)
#ApiModelProperty (
value = "description"
)
private String description;
#Column (nullable = false)
#ApiModelProperty (
value = "time",
required = true
)
private Timestamp time;
#Id
#Column (
unique = true,
nullable = false
)
#Type (type = "pg-uuid")
#ApiModelProperty (
value = "id",
readOnly = true,
example = "123e4567-e89b-12d3-a456-426655440000"
)
private UUID sampleModelEntityId;
public String getDescription ()
{
return this.description;
}
public Integer getLevel ()
{
return this.level;
}
public UUID getSampleModelEntityId ()
{
return this.sampleModelEntityId;
}
public Timestamp getTime ()
{
return this.time;
}
public void setDescription (String description)
{
this.description = description;
}
public void setLevel (Integer level)
{
this.level = level;
}
public void setSampleModelEntityId (UUID sampleModelEntityId)
{
this.sampleModelEntityId = sampleModelEntityId;
}
public void setTime (Timestamp time)
{
this.time = time;
}
}
The only difference between the two classes is that they are defined in separate JAR files. SampleApiEntity is packaged in the same JAR as the REST resource class. SampleModelEntity is packaged in a separate JAR with other entity classes. The swagger.yaml file that is generated contains both classes, but lacks information provided by the ApiModel and ApiModelProperty annotations for the SampleModelEntity class.
Here is what I am seeing in the generated swagger.yaml file:
SampleApiEntity:
type: "object"
required:
- "level"
- "time"
properties:
level:
type: "integer"
format: "int32"
description: "level"
description:
type: "string"
description: "description"
time:
type: "string"
format: "date-time"
description: "time"
sampleApiEntityId:
type: "string"
format: "uuid"
example: "123e4567-e89b-12d3-a456-426655440000"
description: "id"
readOnly: true
description: "This is a sample entity from the Api package."
SampleModelEntity:
type: "object"
properties:
level:
type: "integer"
format: "int32"
description:
type: "string"
time:
type: "string"
format: "date-time"
sampleModelEntityId:
type: "string"
format: "uuid"
Can someone help me understand why the ApiModel and ApiModelProperty annotations are not generating output in swagger.yaml?
I found the solution. It turned out to be a class loading issue. I had an ear library that I was using to deploy a war library. Both the war and the ear contained a copy of the swagger-annotations artifact. This resulted in loaded annotation classes being different from what was packaged in the ear.
I solved the problem by modifying my pom.xml file for the war library. I added the swagger-annotations as an explicit dependency, and set its scope as provided:
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.5.10</version>
<scope>provided</scope>
</dependency>
You can find more information here: https://github.com/swagger-api/swagger-core/issues/2582
A little hint if you have a multi module maven project and use the maven swagger plugin. The same problem can appear in this constelation.
|-Module1 ( Rest paths )
|--> pom.xml
|-Module2 ( Models )
|--> pom.xml
|--> pom.xml ( Parent pom )
Add the plugin to the parent pom and start it from here.
This prevents the class loading problem and the annotations will be recognized and processed in the right way by the swagger "scanner".
I am trying to access Tuple data structure I have stored in Cassandra with Mapper. But, I am unable to. I haven't found any example online.
This is the table and data I have created.
cqlsh:test> CREATE TABLE test.test_nested (id varchar PRIMARY KEY, address_mapping list<frozen<tuple<text,text>>>);
cqlsh:test> INSERT INTO test.test_nested (id, address_mapping) VALUES ('12345', [('Adress 1', 'pin1'), ('Adress 2', 'pin2')]);
cqlsh:test>
cqlsh:test> select * from test.test_nested;
id | address_mapping
-------+----------------------------------------------
12345 | [('Adress 1', 'pin1'), ('Adress 2', 'pin2')]
(1 rows)
My mapped class(using lombok for builder, getter, setter):
#Builder
#Table(keyspace = "test", name = "test_nested")
public class TestNested {
#PartitionKey
#Column(name = "id")
#Getter
#Setter
private String id;
#Column(name = "address_mapping")
#Frozen
#Getter
#Setter
private List<Object> address_mapping;
}
My Mapper class:
public class TestNestedStore {
private final Mapper<TestNested> mapper;
public TestNestedStore(Mapper<TestNested> mapper) {
this.mapper = mapper;
}
public void insert(TestNested userDropData) {
mapper.save(userDropData);
}
public void remove(String id) {
mapper.delete(id);
}
public TestNested findByUserId(String id) {
return mapper.get(id);
}
public ListenableFuture<TestNested> findByUserIdAsync(String id) {
return mapper.getAsync(id);
}
}
I am trying to access data in a test method as follows:
#Test
public void testConnection2(){
MappingManager manager = new MappingManager(scyllaDBConnector.getSession());
Mapper<TestNested> mapper = manager.mapper(TestNested.class);
TestNestedStore testNestedStore = new TestNestedStore(mapper);
ListenableFuture<TestNested> fut = testNestedStore.findByUserIdAsync("12345");
Futures.addCallback(fut, new FutureCallback<TestNested>() {
#Override
public void onSuccess(TestNested testNested) {
}
#Override
public void onFailure(Throwable throwable) {
System.out.println("Call failed");
}
});
}
Bit, I am unable to access the tuple. I get this error:
java.lang.IllegalArgumentException: Error while checking frozen types on field address_mapping of entity com.example.model.TestNested: expected List to be not frozen but was frozen
at com.datastax.driver.mapping.AnnotationChecks.validateAnnotations(AnnotationChecks.java:73)
at com.datastax.driver.mapping.AnnotationParser.parseEntity(AnnotationParser.java:81)
at com.datastax.driver.mapping.MappingManager.getMapper(MappingManager.java:148)
at com.datastax.driver.mapping.MappingManager.mapper(MappingManager.java:105)
I have also tried with private List<TupleValue> address_mapping;. But of no use!
How do I access Tuple values through object mapper of cassandra?
You define address_mapping as list<frozen<tuple<text,text>>>, that is, a list of frozen tuple values. To communicate this to the MappingManager, you can use the #FrozenValue attribute.
TestNested should look like:
#Builder
#Table(keyspace = "test", name = "test_nested")
public class TestNested {
...
#Column(name = "address_mapping")
#Frozen
#Getter
#Setter
#FrozenValue
private List<Object> address_mapping;
}
For defining the cassandra datatype of
map<text,frozen<tuple<text,text,int,text>>>
in java entity class mention the datatype as,
import com.datastax.driver.core.TupleValue;
#FrozenValue
private Map<String,TupleValue> event_driven;
I have a Java Entity Class "Portfolio.java", which has a composite primary key(portfolio_id, portfolio_name). I used #IdClass to create the composite primary key. and those two are the only instances in the class.
I have another class "Application.java", also with composite primary key(application_id,portfolio_id). I have again used #IdClass to create composite primary key.
Now, application_id is a instance of "Application.java" class, whereas portfolio_id is a foreign key to corresponding instance in "Portfolio.java" class.
Does anyone have any idea as how to implement this using Java?
I have tried doing the following,
PortfolioPK.java :
import java.io.Serializable;
public class PortfolioPK implements Serializable {
public int portfolio_id;
public int portfolio_name;
public PortfolioPK() {
super();
}
#Override
public boolean equals(Object obj) {
return super.equals(obj);
}
#Override
public int hashCode() {
return super.hashCode();
}
.....
}
Portfolio.java :
#Entity
#IdClass(PortfolioPK.class)
public class Portfolio {
#Id
private int portfolio_id;
#Id
private int portfolio_name;
.....
}
ApplicationPK.java
import java.io.Serializable;
public class ApplicationPK implements Serializable {
public int application_id;
public int portfolio_id;
public int portfolio_name;
public ApplicationPK() {
super();
}
#Override
public boolean equals(Object obj) {
return super.equals(obj);
}
.....
}
Application.java
#Entity
#IdClass(ApplicationPK.class)
public class Application {
#Id
private int application_id;
private String application_name;
private String app_properties;
#Id
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="portfolio_id", referencedColumnName="portfolio_id")
private Portfolio portfolio_id;
.....
}
So, now im getting an error at the #IdClass declaration in "Application.java". the error is, "The attribute matching the ID class attribute portfolio_id does not have the correct type int"
Is this a correct implementation? or should it be done in a different way?
Thanks for any help ! :)
This is a "derived identity" - the ID of Portfolio is part of the ID of the Application and there is a many-to-one relationship between them. To map that correctly, you need to have a property of type PortfolioPK in ApplicationPK with the same name as the relationship property in Application. So:
#Entity
#IdClass(ApplicationPK.class)
public class Application {
#Id
private int application_id;
// No fields referring to Portfolio ID fields
#Id
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name="portfolio_id", referencedColumnName="portfolio_id")
private Portfolio portfolio; // name it simply "portfolio"
// ...
}
public class ApplicationPK implements Serializable {
public int application_id; // name matches the field in Application
public PortfolioPK portfolio; // name matches the relationship field in Application
// ...
}
See section "2.4.1 Primary Keys Corresponding to Derived Identities" of the JPA 2.1 spec for more details and examples (Example 2 is your case).
Morning.
I need to add indexing in hibernate entity. As I know it is possible to do using #Index annotation to specify index for separate column but I need an index for several fields of entity.
I've googled and found jboss annotation #Table, that allows to do this (by specification). But (I don't know why) this functionality doesn't work. May be jboss version is lower than necessary, or maybe I don't understant how to use this annotation, but... complex index is not created.
Why index may not be created?
jboss version 4.2.3.GA
Entity example:
package somepackage;
import org.hibernate.annotations.Index;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
#Entity
#org.hibernate.annotations.Table(appliesTo = House.TABLE_NAME,
indexes = {
#Index(name = "IDX_XDN_DFN",
columnNames = {House.XDN, House.DFN}
)
}
)
public class House {
public final static String TABLE_NAME = "house";
public final static String XDN = "xdn";
public final static String DFN = "dfn";
#Id
#GeneratedValue
private long Id;
#Column(name = XDN)
private long xdn;
#Column(name = DFN)
private long dfn;
#Column
private String address;
public long getId() {
return Id;
}
public void setId(long id) {
this.Id = id;
}
public long getXdn() {
return xdn;
}
public void setXdn(long xdn) {
this.xdn = xdn;
}
public long getDfn() {
return dfn;
}
public void setDfn(long dfn) {
this.dfn = dfn;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
When jboss/hibernate tries to create table "house" it throws following exception:
Reason: org.hibernate.AnnotationException: #org.hibernate.annotations.Table references an unknown table: house
Please try the following:
#Entity
#org.hibernate.annotations.Table(appliesTo = House.TABLE_NAME,
indexes = {
#Index(name = "IDX_XDN_DFN",
columnNames = {House.XDN, House.DFN}
)
}
)
#Table(name="house")
public class House {
...
}
Note that this should also allow you to create a multi-column index (based on the index name):
#Index(name = "index1")
public String getFoo();
#Index(name = "index1")
public String getBar();
P.S.: What version of Hibernate are you using BTW? What database/dialect?
You have to have hibernate.hbm2ddl.auto set to create in persistence.xml. When set to update hibernate won't create indexes.
hibernate.hbm2ddl.auto = create
You'd better go with a composite primary key.
This article explains how to do it with JPA annotations. It uses #Embeddable and #EmbeddedId