Why my table is not insertable? - sql

Well, I've got two tables: SaveBatch and SaveBatchLocal. They have equal fields. First table is insertable in Hibernate and second is not. This is my code:
#Override
#Transactional(readOnly = false)
public void add(LinkedList<SmsEntity> smsEntityList) {
try {
Session session = null;
session = this.sessionFactory.getCurrentSession();
Iterator<SmsEntity> iterator = smsEntityList.iterator();
while (iterator.hasNext()) {
session.save(iterator.next());
}
} catch (Exception e) {
e.printStackTrace();
}
}
The only thing I change is table name in entity class:
#Entity
#Table(name = "smsc.SaveBatch")
public class SmsEntity {
#Id
#Column(name = "id")
private int id;
#Column(name = "nextid")
private Integer nextId;
...
If #Table(name = "smsc.SaveBatch")
everything is fine, if
#Table(name = "smsc.SaveBatchLocal")
no new rows. What is wrong? Also, this is my Oracle SQL Developer pic:
They have got different icons with unknown meanings.

SaveBatchLocal is a global temporary table, which means that its contents are only visible to the connection that inserted them, and are automatically deleted when that session ends.
See http://www.oracle-base.com/articles/misc/temporary-tables.php

Related

Cant get hibernate to update object without EntityExistException being thrown

I am trying to do a bidirectional one to one relation and when updating the AccountExtrasModel on the first save it works fine but when updating I get either an error or the sql statements adds an insert and then a delete instead of an update.
import lombok.*;
import javax.persistence.*;
#ToString
#NoArgsConstructor
#Getter
#Setter
#Entity
#Table(name = "Account")
public class AccountModel {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long Account_ID;
#Column(nullable = false, updatable = false)
private String name;
#Column(nullable = false, unique = true, updatable = false)
private String email;
#Column(nullable = false, updatable = false)
private String password;
#OneToOne(mappedBy = "accountModel", cascade = CascadeType.ALL, orphanRemoval = true)
private AccountExtrasModel accountExtras;
public AccountModel addExtras(AccountExtrasModel accountExtrasModel) {
accountExtrasModel.setAccountModel(this);
this.setAccountExtras(accountExtrasModel);
return this;
}
}
import lombok.*;
import javax.persistence.*;
#Setter
#Getter
#NoArgsConstructor
#ToString
#Entity
#Table(name = "AccountExtras")
public class AccountExtrasModel {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long ID;
#OneToOne(fetch = FetchType.LAZY)
#MapsId
private AccountModel accountModel;
#Lob
private String description;
private String[] myVideos;
private String[] likedVideos;
private String imageReference;
}
If i change the #MapsId to #JoinColumn in AccountExtrasModel then i get the desired result but what its doing is inserting a new row and linking it to acccount and then deleting the old row instead of doing an update.
This is the error im getting:
{
"timestamp": "2018-04-26T18:19:01.657+0000",
"status": 500,
"error": "Internal Server Error",
"message": "A different object with the same identifier value was already associated with the session : [com.alttube.account.models.AccountExtrasModel#1]; nested exception is javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [com.alttube.account.models.AccountExtrasModel#1]",
"path": "/update_account"
}
What can i do to get the desired result which is to simply perform an update on the accountExtrasModel to the corresponding account to which it belongs?
If you update an AccountModel instance by setting a new instance of AccountExtrasModel, as you do in addExtras(), then it is normal to have a delete+insert. By updating the id you would end with orphaned records.
When you set new values on AccountExtrasModel instance, check if accountExtras is initialized. If not, do the addExtras() stuff. If it is, do not replace it, just change it, so hibernate will generate an update on the extras table (but keep the record id unchanged).
Instead of use the method save try use the method merge, because in case of object exist in database the framework will update this object.
Case the object don't exist, the framework will insert as normal.
Regards!

JPA query to retrieve data using list of matching tuples as arguments [duplicate]

I have an Entity Class like this:
#Entity
#Table(name = "CUSTOMER")
class Customer{
#Id
#Column(name = "Id")
Long id;
#Column(name = "EMAIL_ID")
String emailId;
#Column(name = "MOBILE")
String mobile;
}
How to write findBy method for the below query using crudrepository spring data jpa?
select * from customer where (email, mobile) IN (("a#b.c","8971"), ("e#f.g", "8888"))
I'm expecting something like
List<Customer> findByEmailMobileIn(List<Tuple> tuples);
I want to get the list of customers from given pairs
I think this can be done with org.springframework.data.jpa.domain.Specification. You can pass a list of your tuples and proceed them this way (don't care that Tuple is not an entity, but you need to define this class):
public class CustomerSpecification implements Specification<Customer> {
// names of the fields in your Customer entity
private static final String CONST_EMAIL_ID = "emailId";
private static final String CONST_MOBILE = "mobile";
private List<MyTuple> tuples;
public ClaimSpecification(List<MyTuple> tuples) {
this.tuples = tuples;
}
#Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// will be connected with logical OR
List<Predicate> predicates = new ArrayList<>();
tuples.forEach(tuple -> {
List<Predicate> innerPredicates = new ArrayList<>();
if (tuple.getEmail() != null) {
innerPredicates.add(cb.equal(root
.<String>get(CONST_EMAIL_ID), tuple.getEmail()));
}
if (tuple.getMobile() != null) {
innerPredicates.add(cb.equal(root
.<String>get(CONST_MOBILE), tuple.getMobile()));
}
// these predicates match a tuple, hence joined with AND
predicates.add(andTogether(innerPredicates, cb));
});
return orTogether(predicates, cb);
}
private Predicate orTogether(List<Predicate> predicates, CriteriaBuilder cb) {
return cb.or(predicates.toArray(new Predicate[0]));
}
private Predicate andTogether(List<Predicate> predicates, CriteriaBuilder cb) {
return cb.and(predicates.toArray(new Predicate[0]));
}
}
Your repo is supposed to extend interface JpaSpecificationExecutor<Customer>.
Then construct a specification with a list of tuples and pass it to the method customerRepo.findAll(Specification<Customer>) - it returns a list of customers.
It is maybe cleaner using a projection :
#Entity
#Table(name = "CUSTOMER")
class CustomerQueryData {
#Id
#Column(name = "Id")
Long id;
#OneToOne
#JoinColumns(#JoinColumn(name = "emailId"), #JoinColumn(name = "mobile"))
Contact contact;
}
The Contact Entity :
#Entity
#Table(name = "CUSTOMER")
class Contact{
#Column(name = "EMAIL_ID")
String emailId;
#Column(name = "MOBILE")
String mobile;
}
After specifying the entities, the repo :
CustomerJpaProjection extends Repository<CustomerQueryData, Long>, QueryDslPredicateExecutor<CustomerQueryData> {
#Override
List<CustomerQueryData> findAll(Predicate predicate);
}
And the repo call :
ArrayList<Contact> contacts = new ArrayList<>();
contacts.add(new Contact("a#b.c","8971"));
contacts.add(new Contact("e#f.g", "8888"));
customerJpaProjection.findAll(QCustomerQueryData.customerQueryData.contact.in(contacts));
Not tested code.

OrmLite Foreign Collection to List

I try to use foreign collections in ORMLite. However, I dont know how to convert it into list. I try to do something like this :
public class Car implements Serializable {
#DatabaseField(columnName = "carId" , generatedId = true, id=true)
private int id;
#DatabaseField(columnName = "carNumber")
private String mNumber;
#DatabaseField(columnName = "carName")
private String mName;
#ForeignCollectionField(eager = true,columnName = "carParts")
private Collection<Part> mParts;
ArrayList<Part> parts = new ArrayList<>(mParts);
public ArrayList<Part> getParts() {
return parts;
}
public void setParts(ArrayList<Part> parts) {
this.parts = parts;
}
but when I try to use it I get exception :
java.lang.NullPointerException: collection == null
at this line :
ArrayList<Part> parts = new ArrayList<>(mParts);
please, help.
The reason is simple - you have to wait until mParts will be initialized by ORMLite library, then you can create ArrayList from it.
public ArrayList<Part> getParts() {
return new ArrayList<>( mParts );
}

JPA #ElementCollection generates strange unique key

I have an entity class PositionOrdering which contains an element collection:
#ElementCollection(targetClass = Position.class, fetch = FetchType.EAGER)
#CollectionTable(name = "POSITION_ORDERING_POSITION",
joinColumns = #JoinColumn(name = "position_ordering_id"))
#OrderColumn
List<Position> positions = new ArrayList<>();
When hibernate generates the database structure, it looks like this:
CREATE TABLE wls.position_ordering_position
(
position_ordering_id bigint NOT NULL,
positions_id bigint NOT NULL,
positions_order integer NOT NULL,
...
}
It's ok and exactly what I was expected. But it also generate a unique contsraint on positions_id column. It is strange, because the position id should be unique only per ordering, so any of the following unique keys would be ok:
position_ordering_id + positions_order
position_ordering_id + positions_id
But not on the single column of positions_id.
Because the constraint is generated automatically, I can't ignore or remove it simply.
Can I configure my collection to create correct unique constraint or at least not to create any?
UPDATE:
As for request, here is the skeleton of the Position entity:
#Entity
#SequenceGenerator(name = EntityBase.SEQUENCE_NAME,
sequenceName = "POSITION_ID_SEQ")
#Table(name = "position")
public class Position extends EntityBase {
// Lots of fields, like row, column number, and type, etc.
}
Where EntityBase is a simple class with some utility function and with Id:
#MappedSuperclass
public abstract class EntityBase implements Serializable, Cloneable {
public static final String SEQUENCE_NAME = "SEQUENCE_GENERATOR";
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = SEQUENCE_NAME)
protected Long id;
//..
}
#ElementCollection is used for mapping basic types or #Embedded classes, not entities. From the documentation
An ElementCollection can be used to define a one-to-many relationship to an Embeddable object, or a Basic value (such as a collection of Strings).
Since Position is an #Entity, you should map it as #OneToMany or #ManyToMany. I don't know the exact reason why are you getting that unique key generated, but I guess you can expect unpredictable results if you use the annottion in a was that it was not intended for.
As Predrag Maric described it in the accepted answer, the problem was that Position was not an `Embeddable'. My solution was:
I created a support class which wraps the Position into an #Embeddable entity:
#Embeddable
//#Table(name = "position_ordering_position")
public class PositionOrderingPosition {
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "position_id", nullable = false)
private Position position;
public PositionOrderingPosition() {
}
public PositionOrderingPosition(Position position) {
this.position = position;
}
public Position getPosition() {
return position;
}
}
Also I changed the Element collection to this:
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "POSITION_ORDERING_POSITION",
joinColumns = #JoinColumn(name = "position_ordering_id"))
#OrderColumn
List<PositionOrderingPosition> positions = new ArrayList<>();
Now it creates the same table, but with the right constraints.

get next sequence value from database using hibernate

I have an entity that has an NON-ID field that must be set from a sequence.
Currently, I fetch for the first value of the sequence, store it on the client's side, and compute from that value.
However, I'm looking for a "better" way of doing this. I have implemented a way to fetch the next sequence value:
public Long getNextKey()
{
Query query = session.createSQLQuery( "select nextval('mySequence')" );
Long key = ((BigInteger) query.uniqueResult()).longValue();
return key;
}
However, this way reduces the performance significantly (creation of ~5000 objects gets slowed down by a factor of 3 - from 5740ms to 13648ms ).
I have tried to add a "fake" entity:
#Entity
#SequenceGenerator(name = "sequence", sequenceName = "mySequence")
public class SequenceFetcher
{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
private long id;
public long getId() {
return id;
}
}
However this approach didn't work either (all the Ids returned were 0).
Can someone advise me how to fetch the next sequence value using Hibernate efficiently?
Edit: Upon investigation, I have discovered that calling Query query = session.createSQLQuery( "select nextval('mySequence')" ); is by far more inefficient than using the #GeneratedValue- because of Hibernate somehow manages to reduce the number of fetches when accessing the sequence described by #GeneratedValue.
For example, when I create 70,000 entities, (thus with 70,000 primary keys fetched from the same sequence), I get everything I need.
HOWEVER , Hibernate only issues 1404 select nextval ('local_key_sequence') commands. NOTE: On the database side, the caching is set to 1.
If I try to fetch all the data manually, it will take me 70,000 selects, thus a huge difference in performance. Does anyone know the internal functioning of Hibernate, and how to reproduce it manually?
You can use Hibernate Dialect API for Database independence as follow
class SequenceValueGetter {
private SessionFactory sessionFactory;
// For Hibernate 3
public Long getId(final String sequenceName) {
final List<Long> ids = new ArrayList<Long>(1);
sessionFactory.getCurrentSession().doWork(new Work() {
public void execute(Connection connection) throws SQLException {
DialectResolver dialectResolver = new StandardDialectResolver();
Dialect dialect = dialectResolver.resolveDialect(connection.getMetaData());
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
preparedStatement = connection.prepareStatement( dialect.getSequenceNextValString(sequenceName));
resultSet = preparedStatement.executeQuery();
resultSet.next();
ids.add(resultSet.getLong(1));
}catch (SQLException e) {
throw e;
} finally {
if(preparedStatement != null) {
preparedStatement.close();
}
if(resultSet != null) {
resultSet.close();
}
}
}
});
return ids.get(0);
}
// For Hibernate 4
public Long getID(final String sequenceName) {
ReturningWork<Long> maxReturningWork = new ReturningWork<Long>() {
#Override
public Long execute(Connection connection) throws SQLException {
DialectResolver dialectResolver = new StandardDialectResolver();
Dialect dialect = dialectResolver.resolveDialect(connection.getMetaData());
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
preparedStatement = connection.prepareStatement( dialect.getSequenceNextValString(sequenceName));
resultSet = preparedStatement.executeQuery();
resultSet.next();
return resultSet.getLong(1);
}catch (SQLException e) {
throw e;
} finally {
if(preparedStatement != null) {
preparedStatement.close();
}
if(resultSet != null) {
resultSet.close();
}
}
}
};
Long maxRecord = sessionFactory.getCurrentSession().doReturningWork(maxReturningWork);
return maxRecord;
}
}
Here is what worked for me (specific to Oracle, but using scalar seems to be the key)
Long getNext() {
Query query =
session.createSQLQuery("select MYSEQ.nextval as num from dual")
.addScalar("num", StandardBasicTypes.BIG_INTEGER);
return ((BigInteger) query.uniqueResult()).longValue();
}
Thanks to the posters here: springsource_forum
I found the solution:
public class DefaultPostgresKeyServer
{
private Session session;
private Iterator<BigInteger> iter;
private long batchSize;
public DefaultPostgresKeyServer (Session sess, long batchFetchSize)
{
this.session=sess;
batchSize = batchFetchSize;
iter = Collections.<BigInteger>emptyList().iterator();
}
#SuppressWarnings("unchecked")
public Long getNextKey()
{
if ( ! iter.hasNext() )
{
Query query = session.createSQLQuery( "SELECT nextval( 'mySchema.mySequence' ) FROM generate_series( 1, " + batchSize + " )" );
iter = (Iterator<BigInteger>) query.list().iterator();
}
return iter.next().longValue() ;
}
}
If you are using Oracle, consider specifying cache size for the sequence. If you are routinely create objects in batches of 5K, you can just set it to a 1000 or 5000. We did it for the sequence used for the surrogate primary key and were amazed that execution times for an ETL process hand-written in Java dropped in half.
I could not paste formatted code into comment. Here's the sequence DDL:
create sequence seq_mytable_sid
minvalue 1
maxvalue 999999999999999999999999999
increment by 1
start with 1
cache 1000
order
nocycle;
To get the new id, all you have to do is flush the entity manager. See getNext() method below:
#Entity
#SequenceGenerator(name = "sequence", sequenceName = "mySequence")
public class SequenceFetcher
{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequence")
private long id;
public long getId() {
return id;
}
public static long getNext(EntityManager em) {
SequenceFetcher sf = new SequenceFetcher();
em.persist(sf);
em.flush();
return sf.getId();
}
}
POSTGRESQL
String psqlAutoincrementQuery = "SELECT NEXTVAL(CONCAT(:psqlTableName, '_id_seq')) as id";
Long psqlAutoincrement = (Long) YOUR_SESSION_OBJ.createSQLQuery(psqlAutoincrementQuery)
.addScalar("id", Hibernate.LONG)
.setParameter("psqlTableName", psqlTableName)
.uniqueResult();
MYSQL
String mysqlAutoincrementQuery = "SELECT AUTO_INCREMENT as id FROM information_schema.tables WHERE table_name = :mysqlTableName AND table_schema = DATABASE()";
Long mysqlAutoincrement = (Long) YOUR_SESSION_OBJ.createSQLQuery(mysqlAutoincrementQuery)
.addScalar("id", Hibernate.LONG)
.setParameter("mysqlTableName", mysqlTableName)
.uniqueResult();
Interesting it works for you. When I tried your solution an error came up, saying that "Type mismatch: cannot convert from SQLQuery to Query". --> Therefore my solution looks like:
SQLQuery query = session.createSQLQuery("select nextval('SEQUENCE_NAME')");
Long nextValue = ((BigInteger)query.uniqueResult()).longValue();
With that solution I didn't run into performance problems.
And don't forget to reset your value, if you just wanted to know for information purposes.
--nextValue;
query = session.createSQLQuery("select setval('SEQUENCE_NAME'," + nextValue + ")");
Spring 5 has some builtin helper classes for that:
org/springframework/jdbc/support/incrementer
Here is the way I do it:
#Entity
public class ServerInstanceSeq
{
#Id //mysql bigint(20)
#SequenceGenerator(name="ServerInstanceIdSeqName", sequenceName="ServerInstanceIdSeq", allocationSize=20)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="ServerInstanceIdSeqName")
public Long id;
}
ServerInstanceSeq sis = new ServerInstanceSeq();
session.beginTransaction();
session.save(sis);
session.getTransaction().commit();
System.out.println("sis.id after save: "+sis.id);
Your idea with the SequenceGenerator fake entity is good.
#Id
#GenericGenerator(name = "my_seq", strategy = "sequence", parameters = {
#org.hibernate.annotations.Parameter(name = "sequence_name", value = "MY_CUSTOM_NAMED_SQN"),
})
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "my_seq")
It is important to use the parameter with the key name "sequence_name". Run a debugging session on the hibernate class SequenceStyleGenerator, the configure(...) method at the line final QualifiedName sequenceName = determineSequenceName( params, dialect, jdbcEnvironment ); to see more details about how the sequence name is computed by Hibernate. There are some defaults in there you could also use.
After the fake entity, I created a CrudRepository:
public interface SequenceRepository extends CrudRepository<SequenceGenerator, Long> {}
In the Junit, I call the save method of the SequenceRepository.
SequenceGenerator sequenceObject = new SequenceGenerator();
SequenceGenerator result = sequenceRepository.save(sequenceObject);
If there is a better way to do this (maybe support for a generator on any type of field instead of just Id), I would be more than happy to use it instead of this "trick".