Im trying to build a simple web service, right now I only have 3 classes, an entity class, a DAO class and a tester class.
My entity class:
#Entity
#Table(name="sales")
public class Sale implements Serializable{
#Id
#Column(name = "idsale_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int idsale_id;
#Column(name = "grand_total", nullable = false)
private double grand_total;
public Sale() {
}
public Sale(double grand_total) {
this.grand_total = grand_total;
}
My Database Operations class
#ApplicationScoped
public class DatabaseOperations {
#PersistenceContext(unitName = "owlJPA")
EntityManager em;
#Transactional
public String createSale(double grand_total) {
Sale sale = new Sale(grand_total);
em.persist(sale);
em.flush();
return "Successfully added new entry in DB";
}
}
REST handling code
#RequestScoped
#Path("/hello-world")
public class HelloResource {
#Inject
DatabaseOperations databaseOperations;
#POST
#Produces("text/plain")
#Consumes(MediaType.APPLICATION_JSON)
public String POSTrecieved(JsonObject jsonRaw) {
DatabaseOperations databaseOperations = new DatabaseOperations();
try {
String tempStr = jsonRaw.getJsonObject("newSale").getString("grand_total");
double grand_total = Double.parseDouble(tempStr);
String x = databaseOperations.createSale(grand_total);
return "SUCESSFULLY ADDED NEW SALE, with grand total of: "+x;
}
catch(Exception error){
return error.toString();
}
}
Whenever I try and run a transaction by calling the createSale method, the sale object gets created just fine, but i get a nullPointerException error as my entityManager em is null. But shouldn't my entityManager em already be instantialized as i did #ApplicationScoped?
Related
I'm using Spring #Scope(value = "session", proxyMode=ScopedProxyMode.TARGET_CLASS) beans for objects that should be shared across a single Http-Session. This will provide for example one "Project" object for each User who is using my application.
To get this working I had to implement an interceptor for Hibernate that is returning the name of the class:
public class EntityProxySupportHibernateInterceptor extends EmptyInterceptor {
private static final long serialVersionUID = 7470168733867103334L;
#Override
public String getEntityName(Object object) {
return AopUtils.getTargetClass(object).getName();
}
}
With this interceptor I can use a Spring CrudRepository to save a Project-entity in the database:
#Repository
public interface ProjectRepository extends CrudRepository<Project, Integer> {
Project findByProjectId(int projectId);
}
Project-entity:
#Component
#Entity
#Table(name = "xxx.projects")
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class Project implements Serializable {
private static final long serialVersionUID = -8071542032564334337L;
private int projectId;
private int projectType;
#Id
#Column(name = "project_id")
public int getProjectId() {
return projectId;
}
public void setProjectId(int projectId) {
this.projectId = projectId;
}
#Column(name = "project_type")
public int getProjectType() {
return projectType;
}
public void setProjectType(int projectType) {
this.projectType = projectType;
}
}
Storing the Project in the database works as expected. I can have a look at the database and the correct values are inserted. Now I have a different entity that I'm creating the same way as the project and that I want to save in the database via a CrudRepository.
Here the problem begins. Hibernate is not inserting the values that I have set. Hibernate always only inserts null into the database. Reading the values in my Spring application is working as expected. I think that Hibernate is not using the proxy of the entity but the underlying blueprint of the object. How can I force Hibernate to use the proxy with the correct values?
Repository:
#Repository("DataInput001Repository")
public interface DataInputRepository extends CrudRepository<DataInput, DataInputId> {}
Entity:
#Component("DataInput001")
#Entity
#Table(name = "xx.data_input_001")
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
#IdClass(DatanputId.class)
public class DataInput implements Serializable {
private static final long serialVersionUID = -6941087210396795612L;
#Id
#Column(name = "project_id")
private int projectId;
#Column(name = "income")
private String income;
#Column(name = "income_increase")
private String incomeIncrease;
/* Getter + Setter */
}
Service:
#Service("DataInputService001")
public class DataInputServiceImpl implements DataInputService {
#Resource(name = "DataInputMapper001")
DataInputMapperImpl dataInputMapper;
#Resource(name = "DataInput001Repository")
DataInputRepository dataInputRepository;
#Resource(name = "DataInput001")
DataInput datanInput;
#Transactional
public void createDataInput(String json) throws Exception {
dataInputMapper.mapDataInput(json);
dataInputRepository.save(dataInput);
}
public DataInput getDataInput() {
return dataInput;
}
public void setDataInput(DataInput dataInput) {
this.dataInput = dataInput;
}
}
Hello, everybody!
Some time ago I run into a trouble: if save method of repository fails, identifier, injected to a bean by Hibernate, remains in the bean. That behaviour may led us to a situation, when we will think about our not persistent bean as about persistent one. I would be pleased to know what practice is common to avoid this situation.
Example test(spring boot + hibernate + oracle database):
#Entity
#SequenceGenerator(name = "TEST_ENTITY_GENERATOR", allocationSize = 1, sequenceName = "TEST_ENTITY_SEQ")
public class TestEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TEST_ENTITY_GENERATOR")
private Long id;
#Column(nullable = false)
private String name;
public Long getId() {
return id;
}
}
#Repository
public interface TestEntityRepository extends JpaRepository<TestEntity, Long> {
}
#RunWith(SpringRunner.class)
#SpringBootTest
public class RemainingIdTest {
#Autowired
private TestEntityRepository testEntityRepository;
#Test
public void test() {
TestEntity entity = new TestEntity();
try {
Assertions.assertThat(entity.getId()).isNull();
testEntityRepository.save(entity);
Assertions.fail("Save must fail");
} catch (DataIntegrityViolationException e) {
Assertions.assertThat(entity.getId()).isNotNull();
}
}
}
A possible solution is to use org.hibernate.event.spi.PreInsertEventListener where we can bind the transaction with a processor that will clear your entity if transaction is failed.
Example:
#Component
public class IdentifierCleaner implements PreInsertEventListener {
#Autowired
private EntityManagerFactory entityManagerFactory;
#PostConstruct
private void init() {
SessionFactoryImpl sessionFactory = entityManagerFactory.unwrap(SessionFactoryImpl.class);
EventListenerRegistry registry = sessionFactory.getServiceRegistry().getService(EventListenerRegistry.class);
registry.getEventListenerGroup(EventType.PRE_INSERT).appendListener(this);
}
#Override
public boolean onPreInsert(PreInsertEvent event) {
Object entity = event.getEntity();
event.getSession().getActionQueue().registerProcess(((success, session) -> {
if (!success) {
event.getPersister().resetIdentifier(
entity,
event.getId(),
event.getPersister().getVersion(entity),
event.getSession()
);
}
}));
return false;
}
}
Below is the DAO. I am getting the first UppeningUsers object. Note that here for this function I do not want to return peopleWhoBlockedMe set which is located inside the UppeningUsers..
But in different functions I would like to return that information. Note that Both of them are LAZY fetching. With evict I tried to detach the object but still it did not work.
First of all RESTcontroller is below. Then the DAO code is below. Then two entity descriptions are below.
Question is: I see that until
return new ResponseEntity(returned, HttpStatus.OK);
There is only one query which is the typical select. I do not want hibernate to go and take also UserBlock information of that specific UppeningUser. Because it is not needed for this service response. However even though it is lazy loading for some reason
return new ResponseEntity(returned, HttpStatus.OK);
calls the hibernate. I dont know why in restcontroller still it is connected to the database. I tried evict but didnt work.
The json response is
{"id":7,"peopleWhoBlockedMe":[{"blockedId":7}]}
But I do not want for this function to return this peopleWhoBlockedMe. It can be empty.
PLEASE NOTE that in other service for example I will explictly request this peopleWhoBlockedMe but just for this business logic I do not need this information. So what I can do to prevent this so whenever I actually want to call peopleWhoBlockedMe I can get it. Not automaticly.
#RestController
public class TempController {
#Autowired
UppeningUsersService uppeningUsersService;
#RequestMapping(value = "/testing", method = RequestMethod.GET)
public ResponseEntity<UppeningUsers> getPhotos() {
try {
UppeningUsers returned = uppeningUsersService.getUsersDetailsPartial();
return new ResponseEntity<UppeningUsers>(returned, HttpStatus.OK);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
This part is the DAO.
#Repository
public class UppeningUsersDAO {
#Autowired
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sf) {
this.sessionFactory = sf;
}
/**
* Get Existing user. Return error if there is not.
* #param incomingUser user who requested access.
* #return returns the guy information. All information.
*/
#Transactional
public UppeningUsers getUserDetails() throws Exception {
Session session = this.sessionFactory.getCurrentSession();
Query query = session.createQuery("from UppeningUsers ");
UppeningUsers returning = (UppeningUsers) query.list().get(0);
session.evict(returning);
return returning;
}
}
The main table is this one..
#Entity
#Table(name = "uppening_users")
#Proxy(lazy = true)
public class UppeningUsers {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private
int id;
#OneToMany(mappedBy = "blockedId",cascade =CascadeType.ALL, fetch = FetchType.LAZY)
private Set<UserBlocks> peopleWhoBlockedMe;
public UppeningUsers() {
super();
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Set<UserBlocks> getPeopleWhoBlockedMe() {
return peopleWhoBlockedMe;
}
public void setPeopleWhoBlockedMe(Set<UserBlocks> peopleWhoBlockedMes) {
this.peopleWhoBlockedMe = peopleWhoBlockedMes;
}
}
Now here is the other table.
#Entity
#Table(name="user_blocks")
#Proxy(lazy = true)
public class UserBlocks {
#Id
#Column(name="id")
#GeneratedValue(strategy= GenerationType.IDENTITY)
int id;
#Column(name = "blocked_id",insertable = false,updatable = false)
private int blockedId;
public int getBlockedId() {
return blockedId;
}
public void setBlockedId(int blockedId) {
this.blockedId = blockedId;
}
}
UPDATE: 2 forgot to add the service
#Service("uppeningUserService")
public class UppeningUsersService {
#Autowired
UppeningUsersDAO uppeningUsersDAO;
public UppeningUsers getUsersDetailsPartial( ) throws Exception {
return uppeningUsersDAO.getUserDetails();
}
}
Jens is right about her sentence. The layer methodology and writing business objects fix the issue. Thank you.
I am running a RESTful web service on Glassfish 3.1.2 server. I use Jersey as JAX-RS implementation, Jackson as it's JSON provider, and (JPA) EclipseLink 2.5 as persistence provider for MySQL.
I'll introduce some context and then ask my question.
Let's assume we have the following hierarchy: Item entity and CraftableItem entity that inherits it (see code samples below). They both do have corresponding EAO and Resource classes: ItemEAO, CraftableItemEAO + ItemResource, CraftableItemResource (see code samples below).
Let's have two records in items table:
id | name
1 | "craftable"
2 | "non craftable"
and one corresponding record for "craftable" in crafting_items_joined:
id | crafting_time
1 | 120000
I want to get as more specific information about each entity from GET /item request as possible. I want it to return [{"id":1,"name":"craftable", "crafting_time":120000}, {"id":2,"name":"non craftable"}]. At the momment, GET /item/1 returns {"id":1,"name":"craftable", "crafting_time":120000} and GET /item/2 returns just {"id":2,"name":"non craftable"} which is the behavior i want, but for GET /item. Getting all information about entity present.
Code samples (setters are omitted for sanity's sake).
#MappedSuperclass
public abstract class BaseEntity {
protected Long id;
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getId() {
return id;
}
}
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#Table(name = "items")
public class Item extends BaseEntity implements Serializable {
private String name;
#Column(name = "name", nullable = false, unique = true)
public String getName() {
return name;
}
}
#Entity
#DiscriminatorValue("craftable")
#Table(name = "craftable_items_joined")
public class CraftableItem extends Item {
protected long craftingTime;
#Column(name = "crafting_time", nullable = false)
public long getCraftingTime() {
return craftingTime;
}
}
EAO then:
public abstract class AbstractEAO<T> {
private Class<T> entityClass;
public AbstractEAO(Class<T> entityClass) {
this.entityClass = entityClass;
}
public Class<T> getEntityClass() {
return entityClass;
}
protected abstract EntityManager getEntityManager();
// create, edit, remove
public T find(Object id) {
return getEntityManager().find(entityClass, id);
}
public List<T> findAll() {
CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
cq.select(cq.from(entityClass));
return getEntityManager().createQuery(cq).getResultList();
}
}
#Stateless
#LocalBean
public class ItemEAO extends AbstractEAO<Item> {
#PersistenceContext(unitName = "primary")
private EntityManager entityManager;
public ItemEAO() {
super(Item.class);
}
#Override
protected EntityManager getEntityManager() {
return entityManager;
}
}
#Stateless
#LocalBean
public class CraftableItemEAO extends AbstractEAO<CraftableItem> {
#PersistenceContext(unitName = "primary")
private EntityManager entityManager;
public CraftableItemEAO() {
super(CraftableItem.class);
}
#Override
protected EntityManager getEntityManager() {
return entityManager;
}
}
Resource finally:
public abstract class AbstractResource<T extends BaseEntity> {
protected abstract AbstractEAO<T> getEAO();
#GET
public List<T> findAll() {
return getEAO().findAll();
}
#GET
#Path("{id}")
public T find(#PathParam("id") Long id) {
return getEAO().find(id);
}
// create, edit, remove
}
#Stateless
#Path("item")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class ItemResource extends AbstractResource<Item> {
#EJB private ItemEAO itemEAO;
#Override
protected AbstractEAO<Item> getEAO() {
return itemEAO;
}
}
#Stateless
#Path("item/craftable")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class CraftableItemResource extends AbstractResource<CraftableItem> {
#EJB private CraftableItemEAO craftableItemEAO;
#Override
protected AbstractEAO<CraftableItem> getEAO() {
return craftableItemEAO;
}
}
I want to log changes of an account. Therefore, I have created an entity class that shall log the changes.
Each time, an account entity is saved or updated a logging object is created.
When the object is updated with a new balance the old balance shall be retrieved from the database.
As the object is bound to the session retrieving its old balance is not trivial, because one always gets the new balance.
To circumvent, I detached the object from the session. Yet, this seems to be a workaround that should be avoided.
The following code snippets shall illustrate the scenario.
Any suggestion is highly appreciated!
The test:
public class AccountServiceTest
{
#Autowired
AccountService accountService;
#Autowired
ChangeAccountService changeAccountService;
#Test
public void shouldHaveChangeLog()
{
Account account = this.accountService.updateAccount(new Account(0, 10.0));
assertThat(account.getId(), is(not(0L)));
account.setBalance(20.0);
account = this.accountService.updateAccount(account);
final List<ChangeAccountLog> changeResultLogs = this.changeAccountService.findAll();
assertThat(changeResultLogs.get(1).getNewBalance(), is(not(changeResultLogs.get(1).getOldBalance())));
}
}
The service of the domain class to be logged:
#Service
public class AccountService
{
#Autowired
AccountRepository accountRepository;
#Autowired
ChangeAccountService changeAccountService;
public Account findById(final long id)
{
return this.accountRepository.findOne(id);
}
public Account updateAccount(final Account account)
{
this.changeAccountService.saveLog(account);
return this.accountRepository.save(account);
}
}
The service of the logging class:
#Service
public class ChangeAccountService
{
#Autowired
AccountService accountService;
#Autowired
ChangeAccountLogRepository repository;
public ChangeAccountLog save(final ChangeAccountLog changeAccountLog)
{
return this.repository.save(changeAccountLog);
}
public List<ChangeAccountLog> findAll()
{
return this.repository.findAll();
}
public ChangeAccountLog saveLog(final Account account)
{
final Double oldAccountBalance = oldAccountBalance(account);
final Double newAccountBalance = account.getBalance();
final ChangeAccountLog changeAccountLog = new ChangeAccountLog(0, oldAccountBalance, newAccountBalance);
return this.repository.save(changeAccountLog);
}
#PersistenceContext
EntityManager em;
private Double oldAccountBalance(final Account account)
{
this.em.detach(account);
final Account existingAccount = this.accountService.findById(account.getId());
if (existingAccount != null)
{
return existingAccount.getBalance();
}
return null;
}
}
The class of which objects are to be logged:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
public class Account
{
#Id
#GeneratedBalance
protected long id;
Double balance;
}
The logging class:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
public class ChangeAccountLog
{
#Id
#GeneratedBalance
private long id;
private Double oldBalance;
private Double newBalance;
}
You might want to use Hibernate Envers to create a versioning table instead of creating separate log objects.