public List getAllEmployees()
{
return sessionFactory.openSession().createSQLQuery("select * from employee order by eid").list();
}
employee table has a column ipadres,type is inet in postgresql.
#Entity
#Table(name="employee")
public class Employee {
#Id
#Column(name="eid")
private int eid;
private int dept_id;
private String name;
private String address;
private String project;
private String password;
#Column(name="ipadres")
private String ipadres;
private double salary;
private Date Doj;
This is my pojo class. I have taken ipadres as string,but it gives me following exception.
org.hibernate.MappingException: No Dialect mapping for JDBC type: 1111
I was faced with similar problem when i had to create a custom Money type.
The solution is to create your own class which implements the UserType interface.
Here is a great article regarding the matter: example
In a nuthshell you are interested in implementing the following mehods:
import org.hibernate.usertype.UserType;
public InetType impelements UserType{
public Class<String> returnedClass() {
return String.class;
}
public int[] sqlTypes() {
return new int[] { Types.OTHER }; // as the db type is inet and not directly transmutable to hibernate type.
}
public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner)
throws HibernateException, SQLException {
String value = (String) Hibernate.STRING.nullSafeGet(resultSet, names[0]);
return value;
}
public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index)
throws HibernateException, SQLException {
Hibernate.STRING.nullSafeSet(preparedStatement,
(value != null) ? value.toString() : null, index);
}
}
Then in your Employee entity, you need to add type definition annotations:
#Entity
#Table(name="employee")
#TypeDefs(value={#TypeDef(name="inetType",typeClass=InetType.class)})
public class Employee {
#Column(name="ipadres")
#Type(type="inetType")
private String ipadres;
To complete the answer of Maciej, that is correct, add:
In versions of hibernate 4+ the methods nullSafeGet and nullSafeSet will be:
public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
throws HibernateException, SQLException {
final String value = (String) StandardBasicTypes.STRING.nullSafeGet(rs, names, session, owner);
return value;
}
public void nullSafeSet(PreparedStatement ps, Object value, int index,
SharedSessionContractImplementor session)
throws HibernateException, SQLException {
StandardBasicTypes.STRING.nullSafeSet(ps, (value != null) ?
value.toString(): null, index, session);
}
Also say that you can define the type in the package-info.java like
#org.hibernate.annotations.TypeDef(name = "inetType", typeClass = InetType.class)
After few days research, I find out no need to reinvent the wheel in 2022.
There is a lib vladmihalcea/hibernate-types implement Inet column type & Inet Hibernate Type.
resource
mvn repos: https://search.maven.org/search?q=g:com.vladmihalcea
tutorial: https://vladmihalcea.com/postgresql-inet-type-hibernate/
GitHub: https://github.com/vladmihalcea/hibernate-types
Example from tutorial
#Entity(name = "Event")
#Table(name = "event")
#TypeDef(
name = "ipv4",
typeClass = PostgreSQLInetType.class,
defaultForType = Inet.class
)
public class Event {
#Id
#GeneratedValue
private Long id;
#Column(
name = "ip",
columnDefinition = "inet"
)
private Inet ip;
public Long getId() {
return id;
}
public Inet getIp() {
return ip;
}
public void setIp(String address) {
this.ip = new Inet(address);
}
}
Work as expected in spring-boot 2.7.1, hibernate 5.6.9.
Found a solution by poking around:
CREATE CAST (CHARACTER VARYING as inet) WITH INOUT AS IMPLICIT;
And mark entity with
#Column(columnDefinition = "inet")
var ip: String? = null
Related
I am trying to insert data into a table having columns (NAME, VALUE) with
EntityManager.persist().
When I persist an entity like ('xx', 'value1') it inserts a new record into the table for that entity. But if I want to persist a new entity like ('xx', 'value2'), the entity is persisted in the place of already existing record.
The questions are:
Why and how is it happened?
Is there a way to insert ('xx', 'value2') too?
I found a similar question here but there is no real answer for the question.
Many thanks.
UPDATE: The first column is not a primary key. Instead, the second one is.
Here is the Entity:
#Entity
#Table(name = "TEST_DATA")
public class TestDataEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "NAME", nullable = false)
private String name;
#Id
#Column(name = "VALUE", nullable = false)
private String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
And here is the persisting code:
#Transactional
public static void storeTestData(EntityManager em, String name, String value) {
TestDataEntity entity = new TestDataEntity();
entity.setName(name);
entity.setValue(value);
em.persist(entity);
}
Also, there is another question, which is described here.
The issue is solved this way:
#Transactional
public static void storeTestData(EntityManager em, String name, String value) {
EntityTransaction transaction = em.getTransaction();
try {
transaction.begin();
TestDataEntity entity = new TestDataEntity();
entity.setName(name);
entity.setValue(value);
em.persist(entity);
transaction.commit();
} catch (RuntimeException re) {
if (transaction != null && transaction.isActive()) {
transaction.rollback();
}
throw re;
}
This means, that if no explicit transaction is provided, then the existing record is being updated if it has any value matched with the corresponding field value in entity object.
IMHO, this is really strange and not straightforward, since it would be better if it would throw a
javax.persistence.TransactionRequiredException
like it does on update/delete statements.
Check if your entity correctly implements equals() and hashCode(), usually this solves the problem
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;
This is a spring boot app using JPA, and PostgreSQL
I am trying to persist a list of Strings (or an array of Strings) in a postgres table.
The table looks something like this:
CREATE TABLE test_table (
id BIGSERIAL PRIMARY KEY NOT NULL,
test_text text NOT NULL UNIQUE,
test_array TEXT[]
);
The problem that occurs is when I try to annotate a field with my custom usertype.
#Entity
#Table(name = "test_table")
public class TestTable extends BaseEntity {
#Column(name = "test_text", nullable = false, unique = true)
public String testText;
#Type(type = "com.test.model.utils.StringListType")
#Column(name = "test_array")
public List<String> testArray;
}
The error that I get: No Dialect mapping for JDBC type: 2003
I have worked with this custom usertype before, but only on immutable entities (views).
So, this code works
#Entity
#Subselect("SELECT * FROM test_table")
#Immutable
public class TestView extends BaseEntity {
#Type(type = "com.test.model.utils.StringListType")
#Column(name = "test_array")
public List<String> testArray;
}
I presume it has something to do with the fact that with the #Subselect and #Immutable annotations do not allow for insert/update/delete actions on the entity, where with the #Table annotation I am able to do all of those actions.
Any experience or similar work with a problem like this?
Here is the custom usertype class:
public class StringListType implements UserType
{
protected static final int SQLTYPE = java.sql.Types.ARRAY;
#Override
public Object nullSafeGet(final ResultSet rs, final String[] names,
final SessionImplementor sessionImplementor, final Object owner) throws HibernateException,
SQLException
{
Array array = rs.getArray(names[0]);
List<String> stringList = Arrays.asList((String[]) array.getArray());
return stringList;
}
#Override
public void nullSafeSet(final PreparedStatement statement, final Object object, final int i,
final SessionImplementor sessionImplementor) throws HibernateException, SQLException
{
Connection connection = statement.getConnection();
#SuppressWarnings("unchecked")
List<String> castObject = (List<String>) object;
Array array = connection.createArrayOf("text", castObject.toArray());
statement.setArray(i, array);
}
#Override
public Object assemble(final Serializable cached, final Object owner) throws HibernateException
{
return cached;
}
#Override
public Object deepCopy(final Object o) throws HibernateException
{
return o == null ? null : ((String[]) o).clone();
}
#Override
public Serializable disassemble(final Object o) throws HibernateException
{
return (Serializable) o;
}
#Override
public boolean equals(final Object x, final Object y) throws HibernateException
{
return x == null ? y == null : x.equals(y);
}
#Override
public int hashCode(final Object o) throws HibernateException
{
return o == null ? 0 : o.hashCode();
}
#Override
public boolean isMutable()
{
return false;
}
#Override
public Object replace(final Object original, final Object target, final Object owner)
throws HibernateException
{
return original;
}
#SuppressWarnings("unchecked")
#Override
public Class<List<String>> returnedClass()
{
return (Class<List<String>>) Collections.<String> emptyList().getClass();
}
#Override
public int[] sqlTypes()
{
return new int[] { SQLTYPE };
}
}
Overview
I try to use postgres custom enums with Hibernate running on WildFly10 (also tested with WildFly9 with same result).
While searching I found the solution which was mentioned at several points, e.g.
http://octagen.at/2014/10/postgresql-custom-data-types-enum-in-hibernate/
Hibernate Enumeration Mapping
java.lang.verifyError on hibernate specific usertype
So basically I added a custom UserType and added a #Type annotation pointing to the specific java type / converter. It works, I can use it in a JEE app and also in a JSE app without any problem.
Problem / Question
If I enable validation
<property name="hibernate.hbm2ddl.auto" value="validate"/>
I get the following error
Schema-validation: wrong column type encountered
in column [gender] in table [test];
found [gender_type (Types#VARCHAR)], but expecting [uuid (Types#OTHER)]
Why expecting UUID when gender_type is correct?
Details
The PostgreSQL type:
CREATE TYPE gender_type as ENUM ('MALE','FEMALE');
The PostgreSQL table
create table test (
name varchar(200) NOT NULL PRIMARY KEY,
gender gender_type NOT NULL
);
The Java enum
public enum GenderEnum {
MALE ("MALE"),
FEMAIL ("FEMALE");
private String value;
private GenderEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
The Java EnumType
public class GenderEnumType extends GenericEnumType<String, GenderEnum> {
public GenderEnumType() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
super(GenderEnum.class, GenderEnum.values(), "getValue", Types.OTHER);
}
#Override
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException {
return nullSafeGet(rs, names, owner);
}
#Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException {
nullSafeSet(st, value, index);
}
}
The GenericEnumType is basically the one from http://octagen.at/2014/10/postgresql-custom-data-types-enum-in-hibernate/.
Test entity: neither
#Basic(optional = false)
#Enumerated (EnumType.STRING)
#Type (type = "full.path.to.GenderEnumType")
#Column(name = "gender")
private GenderEnum gender;
nor
#Basic(optional = false)
#Type (type = "full.path.to.GenderEnumType")
#Column(name = "gender")
private GenderEnum gender;
works.
I am trying to map a PostgreSQL custom type,named transmission_result, to a Hibernate/JPA POJO. The PostgreSQL custom type is more or less an enum type of string values.
I have created a custom EnumUserType called PGEnumUserType as well as an enum class representing the PostgreSQL enumerated values. When I run this against a real database, I receive the following error:
'ERROR: column "status" is of type transmission_result but expression is of type
character varying
Hint: You will need to rewrite or cast the expression.
Position: 135 '
Upon seeing this, I figured I needed to change my SqlTypes to Types.OTHER. But doing so breaks my integration tests (using HyperSQL in memory DB) with the message:
'Caused by: java.sql.SQLException: Table not found in statement
[select enrollment0_."id" as id1_47_0_,
enrollment0_."tpa_approval_id" as tpa2_47_0_,
enrollment0_."tpa_status_code" as tpa3_47_0_,
enrollment0_."status_message" as status4_47_0_,
enrollment0_."approval_id" as approval5_47_0_,
enrollment0_."transmission_date" as transmis6_47_0_,
enrollment0_."status" as status7_47_0_,
enrollment0_."transmitter" as transmit8_47_0_
from "transmissions" enrollment0_ where enrollment0_."id"=?]'
I'm not sure why changing the sqlType results in this error. Any help is appreciated.
JPA/Hibernate Entity:
#Entity
#Access(javax.persistence.AccessType.PROPERTY)
#Table(name="transmissions")
public class EnrollmentCycleTransmission {
// elements of enum status column
private static final String ACCEPTED_TRANSMISSION = "accepted";
private static final String REJECTED_TRANSMISSION = "rejected";
private static final String DUPLICATE_TRANSMISSION = "duplicate";
private static final String EXCEPTION_TRANSMISSION = "exception";
private static final String RETRY_TRANSMISSION = "retry";
private Long transmissionID;
private Long approvalID;
private Long transmitterID;
private TransmissionStatusType transmissionStatus;
private Date transmissionDate;
private String TPAApprovalID;
private String TPAStatusCode;
private String TPAStatusMessage;
#Column(name = "id")
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
public Long getTransmissionID() {
return transmissionID;
}
public void setTransmissionID(Long transmissionID) {
this.transmissionID = transmissionID;
}
#Column(name = "approval_id")
public Long getApprovalID() {
return approvalID;
}
public void setApprovalID(Long approvalID) {
this.approvalID = approvalID;
}
#Column(name = "transmitter")
public Long getTransmitterID() {
return transmitterID;
}
public void setTransmitterID(Long transmitterID) {
this.transmitterID = transmitterID;
}
#Column(name = "status")
#Type(type = "org.fuwt.model.PGEnumUserType" , parameters ={#org.hibernate.annotations.Parameter(name = "enumClassName",value = "org.fuwt.model.enrollment.TransmissionStatusType")} )
public TransmissionStatusType getTransmissionStatus() {
return this.transmissionStatus ;
}
public void setTransmissionStatus(TransmissionStatusType transmissionStatus) {
this.transmissionStatus = transmissionStatus;
}
#Column(name = "transmission_date")
public Date getTransmissionDate() {
return transmissionDate;
}
public void setTransmissionDate(Date transmissionDate) {
this.transmissionDate = transmissionDate;
}
#Column(name = "tpa_approval_id")
public String getTPAApprovalID() {
return TPAApprovalID;
}
public void setTPAApprovalID(String TPAApprovalID) {
this.TPAApprovalID = TPAApprovalID;
}
#Column(name = "tpa_status_code")
public String getTPAStatusCode() {
return TPAStatusCode;
}
public void setTPAStatusCode(String TPAStatusCode) {
this.TPAStatusCode = TPAStatusCode;
}
#Column(name = "status_message")
public String getTPAStatusMessage() {
return TPAStatusMessage;
}
public void setTPAStatusMessage(String TPAStatusMessage) {
this.TPAStatusMessage = TPAStatusMessage;
}
}
Custom EnumUserType:
public class PGEnumUserType implements UserType, ParameterizedType {
private Class<Enum> enumClass;
public PGEnumUserType(){
super();
}
public void setParameterValues(Properties parameters) {
String enumClassName = parameters.getProperty("enumClassName");
try {
enumClass = (Class<Enum>) Class.forName(enumClassName);
} catch (ClassNotFoundException e) {
throw new HibernateException("Enum class not found ", e);
}
}
public int[] sqlTypes() {
return new int[] {Types.VARCHAR};
}
public Class returnedClass() {
return enumClass;
}
public boolean equals(Object x, Object y) throws HibernateException {
return x==y;
}
public int hashCode(Object x) throws HibernateException {
return x.hashCode();
}
public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
String name = rs.getString(names[0]);
return rs.wasNull() ? null: Enum.valueOf(enumClass,name);
}
public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
if (value == null) {
st.setNull(index, Types.VARCHAR);
}
else {
st.setString(index,((Enum) value).name());
}
}
public Object deepCopy(Object value) throws HibernateException {
return value;
}
public boolean isMutable() {
return false; //To change body of implemented methods use File | Settings | File Templates.
}
public Serializable disassemble(Object value) throws HibernateException {
return (Enum) value;
}
public Object assemble(Serializable cached, Object owner) throws HibernateException {
return cached;
}
public Object replace(Object original, Object target, Object owner) throws HibernateException {
return original;
}
public Object fromXMLString(String xmlValue) {
return Enum.valueOf(enumClass, xmlValue);
}
public String objectToSQLString(Object value) {
return '\'' + ( (Enum) value ).name() + '\'';
}
public String toXMLString(Object value) {
return ( (Enum) value ).name();
}
}
Enum class:
public enum TransmissionStatusType {
accepted,
rejected,
duplicate,
exception,
retry}
If you have following post_status_info enum type in PostgreSQL:
CREATE TYPE post_status_info AS ENUM (
'PENDING',
'APPROVED',
'SPAM'
)
You can easily map Java Enum to a PostgreSQL Enum column type using the following custom Hibernate Type:
public class PostgreSQLEnumType extends org.hibernate.type.EnumType {
public void nullSafeSet(
PreparedStatement st,
Object value,
int index,
SharedSessionContractImplementor session)
throws HibernateException, SQLException {
if(value == null) {
st.setNull( index, Types.OTHER );
}
else {
st.setObject(
index,
value.toString(),
Types.OTHER
);
}
}
}
To use it, you need to annotate the field with the Hibernate #Type annotation as illustrated in the following example:
#Entity(name = "Post")
#Table(name = "post")
#TypeDef(
name = "pgsql_enum",
typeClass = PostgreSQLEnumType.class
)
public static class Post {
#Id
private Long id;
private String title;
#Enumerated(EnumType.STRING)
#Column(columnDefinition = "post_status_info")
#Type( type = "pgsql_enum" )
private PostStatus status;
//Getters and setters omitted for brevity
}
That's it, it works like a charm. Here's a test on GitHub that proves it.
I figured it out. I needed to use setObject instead of setString in the nullSafeSet function and pass in the Types.OTHER as the java.sql.type to let jdbc know that it was a postgres type.
public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
if (value == null) {
st.setNull(index, Types.VARCHAR);
}
else {
// previously used setString, but this causes postgresql to bark about incompatible types.
// now using setObject passing in the java type for the postgres enum object
// st.setString(index,((Enum) value).name());
st.setObject(index,((Enum) value), Types.OTHER);
}
}
The following might also help to have Postgres convert strings silently to your SQL enum type so you can use #Enumerated(STRING) and don't need #Type.
CREATE CAST (character varying as post_status_type) WITH INOUT AS IMPLICIT;
A quick solution will be
jdbc:postgresql://localhost:5432/postgres?stringtype=unspecified
?stringtype=unspecified is the answer
build.gradle.kts
dependencies {
api("javax.persistence", "javax.persistence-api", "2.2")
api("org.hibernate", "hibernate-core", "5.4.21.Final")
}
In Kotlin it is important to make a generic extension with EnumType<Enum<*>>()
PostgreSQLEnumType.kt
import org.hibernate.type.EnumType
import java.sql.Types
class PostgreSQLEnumType : EnumType<Enum<*>>() {
#Throws(HibernateException::class, SQLException::class)
override fun nullSafeSet(
st: PreparedStatement,
value: Any,
index: Int,
session: SharedSessionContractImplementor) {
st.setObject(
index,
value.toString(),
Types.OTHER
)
}
}
Custom.kt
import org.hibernate.annotations.Type
import org.hibernate.annotations.TypeDef
import javax.persistence.*
#Entity
#Table(name = "custom")
#TypeDef(name = "pgsql_enum", typeClass = PostgreSQLEnumType::class)
data class Custom(
#Id #GeneratedValue #Column(name = "id")
val id: Int,
#Enumerated(EnumType.STRING) #Column(name = "status_custom") #Type(type = "pgsql_enum")
val statusCustom: StatusCustom
)
enum class StatusCustom {
FIRST, SECOND
}
A simpler option that I don't recommend is the first option in Arthur's answer which adds a parameter in the connection URL to the db so that the enum data type is not lost. I believe that the responsibility of mapping the data type between the backend server and the database is precisely the backend.
<property name="connection.url">jdbc:postgresql://localhost:5432/yourdatabase?stringtype=unspecified</property>
Source
As TypeDef has disappeared in Hibernate 6, and we thus need to annotate each affected property anyway, I've found that using
#ColumnTransformer(write="?::transmission_result ")
to force a type cast works, without any Hibernate usertype classes needed.