Using
Hibernate 5.3, Spring Boot 2.1, MySQL, that runs in Windows 10 OS.
What I know
I have verified on my phpmyadmin the case of the attributes. And as long the case were the same as the attributes of my Entity class you don't have to explicitly define the column name in that Entity. And using #Column(name="tableattribute") is not required.
Problem
Once I executes the query, the number of row has been retrieved correctly. For example, my database contains 5 record, the List contains 5 employee objects, but all its attributes of the Entity always returns as null value.
I want to remove explicitly declaring the column name on each attribute and ensure that it will work in the actual server which might cause problem with the case of column name and the attribute name.
Tried
I tried to add #Column('column Name all lowercase') on each attributes and It retrieve the value.
Upon learning this, I verified the table column if it is lower case, but is not in lower case. Instead, It still follow the camel-case that is in my SQL command to create the table.
MySQL Table
CREATE TABLE `personal` (
`empID` int(11) NOT NULL,
`empNumber` varchar(15) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
`surname` varchar(50) NOT NULL DEFAULT '',
`firstname` varchar(50) NOT NULL DEFAULT '',
`middlename` varchar(50) NOT NULL DEFAULT '',
`middleInitial` varchar(10) DEFAULT NULL,
`nameExtension` varchar(10) DEFAULT '',
`salutation` varchar(15) NOT NULL DEFAULT ''
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
Entity
This Entity is now working fine as long as #Column and specify the column name in lowercase. But it is annoying to add each attribute with #Column and specify the name of the field in all lower case.
#Entity
#Table(name="personal")
public class Employee implements Serializable {
#Id
#Column(name="empID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#NotNull
#Column(name="empnumber")
private String empNumber;
private String surname;
private String firstname;
private String middlename;
#Column(name="middleinitial")
private String middleInitial;
#Column(name="nameextension")
private String nameExtension;
}
Actual Query Snippet on DAO
#Autowired
private EntityManager entityManager;
#Override
public List<Employee> findAll() {
Session currentSession = entityManager.unwrap(Session.class);
Query<Employee> query = currentSession.createQuery("from Employee", Employee.class);
return query.getResultList();
}
You can change that behavior by setting
spring.jpa.hibernate.naming.physical-strategy
spring.jpa.hibernate.naming.implicit-strategy
properties in your application.properties. On how to implement the strategies yourself, you can check out official docs.
Edit 1:
spring.jpa.hibernate.naming.physical-strategy = org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.hibernate.naming.implicit-strategy = org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
Related
I'm using this hibernate-types that allows hibernate to translate SQL layer data types into java classes in my springboot application, here I'm trying to add a text array field called user array.
#Entity
#Table(name = "user_update")
#Getter
#Setter
#NoArgsConstructor
#RequiredArgsConstructor
#TypeDef(name = "list-array", typeClass = ListArrayType.class)
public class UserUpdate {
#Id #NonNull private String userKey;
#Column #NonNull private String userName;
#Column #NonNull private Instant updatedAt;
#Type(type = "list-array")
#Column(columnDefinition = "text[]")
#NonNull
private List<String> userArray;
}
I can insert data into the table, but now I want to add a test and I see the following error message from the table.sql
CREATE TABLE IF NOT EXISTS user_update (
user_key VARCHAR(255) NOT NULL,
user_name VARCHAR(255) NOT NULL,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
user_array TEXT ARRAY NOT NULL,
PRIMARY KEY(org_key)
);
maybe it is because the test I'm running uses #DataJpaTest and for some reason it can not recognize the new text[] field ?
Error executing DDL "create table user_update (user_key varchar(255) not null, user_name varchar(255), user_array text[], updated_at timestamp, primary key (user_key))" via JDBC Statement
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement "create table user_update (user_key varchar(255) not null, user_name varchar(255), user_array text[*][], updated_at timestamp, primary key (user_key))"; expected "(, ARRAY, INVISIBLE, VISIBLE, NOT, NULL, AS, DEFAULT, GENERATED, ON, NOT, NULL, DEFAULT, NULL_TO_DEFAULT, SEQUENCE, SELECTIVITY, COMMENT, CONSTRAINT, COMMENT, PRIMARY, UNIQUE, NOT, NULL, CHECK, REFERENCES, ,, )"; SQL statement:
With #DataJpaTest Spring will instruct Hibernate to create the schema and that will use the information that you provided in your annotations, but it seems this is not legal for H2.
Try using the following instead:
#Column(columnDefinition = "text array")
if a java object has a auto-increment ID field, should the Database table field should be auto-increment as well?
for example
public class Company {
private static int countID = 0;
private int companyID;
private String name;
private String email;
private String password;
private List<Coupon> couponsList;
public Company(int companyID, String name, String email, String password, List<Coupon> couponsList) {
this.companyID = companyID+=1;
this.name = name;
this.email = email;
this.password = password;
this.couponsList = couponsList;
}
you can see that the companyID field is generated automatically.
i have created this table in my SQL server
CREATE TABLE `companies` (
`ID` int NOT NULL AUTO_INCREMENT,
`NAME` varchar(200) DEFAULT NULL,
`EMAIL` varchar(200) DEFAULT NULL,
`PASSWORD` varchar(200) DEFAULT NULL,
PRIMARY KEY (`ID`)
how should I connect the Java field (commpanID) to the sql field (ID)?
No. They are different things. Auto-incremented columns in MySQL are not guaranteed to be gapless. Gaps can occur for multiple reasons. The most common are:
Concurrent transactions.
Deletion.
It sounds like you have a unique identifier in Java which is either redundant or an item of data. If the latter, then add it as an additional column.
More likely, though, you might want to reconsider your design, so there is only one auto-incremented value for a given record. I would recommend using the one in the database, because that would apply regardless of how inserts are made into the database.
It isn't compulsory to create and unique id field in the database . You can instead change the table like-->
CREATE TABLE companies (
'COMPANYID' int NOT NULL,
`NAME` varchar(200) DEFAULT NULL,
`EMAIL` varchar(200) DEFAULT NULL,
`PASSWORD` varchar(200) DEFAULT NULL,
PRIMARY KEY (`ID`)
since you are auto incrementing the same the same value twice , it will create some problems.
your ID column will be like this-->
Id|
---
2 |
---
4 |
--
6 |
--
8 |
it will increment the values twice
Query:
INSERT INTO PERSON
(email, mobile, party_id, affiliate_id, eligibility, member_start_date, created_by, created_dt, first_name, last_name, google_connected)
values
('xxx#yyy.org', NULL, 123, '123', '1', NULL, NULL, '2018-8-30 21:45:56.859000 -6:0:0', 'xxx', 'yyy', '0')
ON CONFLICT (email)
DO UPDATE SET create_dt = '2018-8-30 21:45:56.859000 -6:0:0' where email = ?
When the LocalDate value is not null, it works fine. Facing this issue only when LocalDate value is given as null.
Even after PostgreSQL casting, it does the same.
Exception stacktrace:
2018-08-30 21:10:48,372 -- [ERROR]-- There was an unexpected problem
with your request org.postgresql.util.PSQLException: ERROR: column
"member_start_date" is of type date but expression is of type bytea
Hint: You will need to rewrite or cast the expression. Position: 185
at
org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2182)
at
org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1911)
at
org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:173)
at
org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:645)
at
org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:495)
at
org.postgresql.jdbc2.AbstractJdbc2Statement.executeQuery(AbstractJdbc2Statement.java:380) at sun.reflect.GeneratedMethodAccessor98.invoke(Unknown Source) at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498) at
org.apache.tomcat.jdbc.pool.StatementFacade$StatementProxy.invoke(StatementFacade.java:114)
at com.sun.proxy.$Proxy185.executeQuery(Unknown Source) at at
org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:70)
... 149 common frames omitted
Entity:
#Entity(name = "person")
#EqualsAndHashCode(callSuper = false)
public class PersonEntity extends Audit {
#Id
#GeneratedValue
#Column(name = "person_id", columnDefinition = "uuid", updatable = false)
private UUID id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "email")
#NotNull
private String email;
#Column(name = "mobile")
private String mobile;
#Column(name = "party_id")
private Long partyId;
#Column(name = "affiliate_id")
private String affiliateId;
#Column(name = "eligibility")
#NotNull
private Boolean eligibility;
#Column(name = "member_start_date")
private LocalDate memberStartDate;
#Column(name = "google_connected")
private Boolean googleAccountConnected;
}
PostgreSQL table definition; it's missing google_connected column which is not important:
CREATE TABLE person
(
person_id UUID NOT NULL,
email VARCHAR(128) NOT NULL,
mobile VARCHAR(20),
party_id INTEGER,
affiliate_id VARCHAR(20),
eligibility BOOLEAN NOT NULL,
member_start_date DATE,
created_by VARCHAR(128) NOT NULL,
created_dt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_by VARCHAR(128) DEFAULT NULL,
updated_dt TIMESTAMP NULL,
CONSTRAINT person_pk PRIMARY KEY ( person_id )
);
Because the query is native, Hibernate doesn't know the data types which to expect, so when you pass a null it defaults to the generic Serializable type handler. Changing this behaviour breaks compatibility with other databases.
Postgres, however parses the query immediately and determines what types are acceptable, and it always checks for type before it checks for null. They are the only ones who can fix this, but refuse to do so and say it works as intended.
The only solutions for you are:
use JPQL
use managed entities
use hard-coded nulls in the query string where you need to
Fortunately for the third option, with Hibernate you can use named parameters in native queries, so you don't have to do positional calculations for when something is available and when it isn't.
edit: 4th solution that I've discovered since.
You have your query:
Query q = em.createNativeQuery("UPDATE...");
Have some static final LocalDate object somewhere:
public static final LocalDate EPOCH_DATE = LocalDate.of(1970, 1, 1);
then call the query like this:
q.setParameter("start_date", EPOCH_DATE);
q.setParameter("start_date", nullableDateParam);
The first time you call setParameter for a parameter, Hibernate uses the class to resolve the type. The second time you call it, the type is already resolved, so a null will work.
It's an old question, but there is a more useful way:
your query...
.setParameter("transaction_id", null, LongType.INSTANCE)
It works.
Found from https://forum.hibernate.org/viewtopic.php?p=2493645
Going to newer versions of hibernate 5.1.17 and above + postgres seems to have exhibited this behavior. Looking into the code, when it binds a type that has no value, the old hibernate code attempted to resolve the type through a typeresolver. The newer versions of hibernate's implementation state that it will not guess.
public Type resolveParameterBindType(Object bindValue) {
if ( bindValue == null ) {
// we can't guess
return null;
}
We ended up just setting a default value based on the type first, and then the real null value.
I'm having problems where two Date fields are updated to the exact same date when only one should be. I'm trying to figure out why this is happening and how I can update only the one date field I want updated, and leave the other at its original value.
I'm using Hibernate with JPA on a MySQL database, in case that is part of the reason.
I have a persistence entity that looks something like this:
#NamedQueries({
#NamedQuery(name="MyObject.updateItem", query="UPDATE MyObject m SET m.item = :item, m.lastUpdate = :updated WHERE m.id = :id")
})
#Entity
#Table(name="entries")
public class MyObject implements Serializable
{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
private String item;
#Column(columnDefinition = "TIMESTAMP", nullable = false)
private Date dateCreated = new Date();
#Column(columnDefinition = "TIMESTAMP", nullable = false)
private Date lastUpdate = new Date();
// after here standard constructors, getters, setters, etc.
}
When from my DAO I call the NamedQuery and provide the correct paramters, I find that both lastUpdate and dateCreated are changed. Is there any reason for this and how can I prevent this from happening? Is this caused because I initialize the to date fields in the entity class?
I'm using the TIMESTAMP column definition because I want to be able to perform queries with < or >.
lastUpdate and dataCreated, aftare update have the same value?
I don't know if this will be a solution for you but this is what I commonly do for all of the entities I regularly implement. Add a PrePersist and PreUpdate function to your entity in order to set the created and last modified times. Also try adding #Temporal(TemporalType.TIMESTAMP) to each of your date fields.
#PrePersist
public void prePersist() {
this.dateCreated = new Date();
this.lastUpdated = this.dateCreated;
}
#PreUpdate
public void preUpdate() {
this.lastUpdated = new Date();
}
Beyond that, I'm a little stumped...
So I figured out the problem wasn't to do with my query or how I used persistence but how I built the database itself.
When I created the table to contain the data for the object I didn't specify a specific default for a NOT NULL field.
My original SQL CREATE statement looked something like this.
CREATE TABLE IF NOT EXISTS `entries` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`item` VARCHAR(255) NOT NULL,
`dateCreated` TIMESTAMP NOT NULL,
`lastUpdate` TIMESTAMP NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
When the MySQL Server executed this statement it deferred the first TIMESTAMP field (in this case dateCreated) with the default to fill it with the CURRENT_TIMESTAMP and the attribute on update CURRENT_TIMESTAMP which was unexpected by me.
I corrected this problem by changing default for the field to DEFAULT '0000-00-00 00:00:00' and by changing my CREATE TABLE statement to force this default so my new statement looks like
CREATE TABLE IF NOT EXISTS `entries` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`item` VARCHAR(255) NOT NULL,
`dateCreated` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
`lastUpdate` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
This apparently allows me to update the fields that I want without causing the other to update automatically.
I'm still not sure why MySQL assumed the defaults that it did. I guess it's probably somewhere in the documentation.
Im getting this error from hibernate when trying to do a criteria search
ERROR org.hibernate.property.access.spi.GetterMethodImpl - HHH000122:
IllegalArgumentException in class: packagename.domain.User, getter method of
property: id
For this criteria search
#Override
public List<Story> findStoryByAuthor(Long userId) throws Exception {
Criteria criteria = currentSession().createCriteria(Story.class);
criteria.add(Restrictions.eq("author", userId));
criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
List<?> stories = criteria.list();
return (List<Story>) stories;
}
We are finding stories via the user that created it so here are the property definitions. Trying not to clutter this so if you guys want to see more code let me know.
Story has a Many to One relationship with User
#ManyToOne(fetch = FetchType.LAZY, targetEntity = User.class)
#JoinColumn(name = "author_user_id", referencedColumnName = "id")
public User getAuthor() {
return author;
}
Users have no reference to story but its id property is correctly annotated
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getId() {
return id;
}
And lastly the database structure
Stories Table
id bigint NOT NULL DEFAULT nextval('stories_seq'::regclass),
parent_id bigint,
author_user_id bigint NOT NULL,
title character varying(100) NOT NULL,
dt_created timestamp without time zone NOT NULL,
dt_last_updated timestamp without time zone NOT NULL DEFAULT now_utc(),
thumbs_up bigint DEFAULT 0,
thumbs_down bigint DEFAULT 0,
CONSTRAINT pk_stories PRIMARY KEY (id),
CONSTRAINT fk_stories_author_user_id_users FOREIGN KEY (author_user_id)
REFERENCES public.users (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
Users Table
id bigint NOT NULL DEFAULT nextval('users_seq'::regclass),
first_name character varying(255),
last_name character varying(255),
email character varying(255) NOT NULL,
user_name character varying(255),
password character varying(255),
phone character varying(255),
address character varying(500),
city character varying(255),
state character varying(255),
zip integer,
preferred_language character varying(255),
note character varying(32000),
active boolean DEFAULT true,
active_paid boolean DEFAULT false,
CONSTRAINT pk_user_id PRIMARY KEY (id),
CONSTRAINT users_unique_fields UNIQUE (email, user_name)
FYI
all basic dao functions (add, update and delete) work (full unit test coverage). I have another criteria search, almost identical but searches on the email field and works fine. so this error is something with the user reference
Please try with
criteria.add(Restrictions.eq("author.id", userId));
author property is of class User so you need to reference its id property for the equality.