I have an enterprise application set to work with a JNDI. Here's the configuration in my application.properties.
spring.datasource.jndi-name=jdbc/MYPRIMARYDATABASE
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.jpa.database-platform=org.hibernate.dialect.Oracle10gDialect
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
spring.jpa.properties.hibernate.current_session_context_class=jta
spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.EJB3NamingStrategy
spring.jpa.properties.hibernate.transaction.factory_class=org.hibernate.transaction.JTATransactionFactory
spring.jackson.serialization-inclusion=NON_NULL
and my Spring main: Application.java
#SpringBootApplication
#EnableScheduling
#EnableJpaRepositories(basePackages = { "it.mypackage.data.access.database" })
#EntityScan(basePackages = { "it.mypackage.data.access.model" })
#ComponentScan(basePackages = { "it.mypackage" })
public class Application extends SpringBootServletInitializer implements WebApplicationInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
#Bean
public SessionFactory sessionFactory(#Qualifier("entityManagerFactory") EntityManagerFactory emf) {
return emf.unwrap(SessionFactory.class);
}
}
Inside the package it.mypackage.data.access.database there is a DAO interface and a DAO facade which perform operations on the datasource and in the it.mypackage.data.access.model package where all table entities are declared.
DatabaseDAOFacade.java
#Repository
#Transactional
public class DatabaseDAOFacade implements DatabaseDAOInterface {
private final SessionFactory sf;
#SuppressWarnings("unused")
private EntityManager entityManager;
protected Session getSession() {
return sf.getCurrentSession();
}
#Autowired
public DatabaseDAOFacade(SessionFactory sf, EntityManager entityManager) {
this.sf = sf;
this.entityManager = entityManager;
}
#SuppressWarnings("unchecked")
public <T> T save(T o) {
return (T) this.getSession().save(o);
}
.
.
}
Here I have a new entity which should use an additional JNDI
Photo.java
#Entity
#Table(name = "T_PHOTO")
public class Photo {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.AUTO, generator = "PHOTO_REQUEST_SEQ_GEN")
#SequenceGenerator(name = "PHOTO_REQUEST_SEQ_GEN", sequenceName = "PHOTO_REQUESTS")
private Long id;
#Column(name = "USERID")
private String userId;
#Column(name = "UPLOAD_DATE")
private Date uploadDate;
.
.
.
}
Is there a clean way to add a second JNDI and set Photo.java to use the new one?
Related
I'm trying to fetch data from an Oracle DB and import it to Azure Sql. The databases have the same structure and therefore I was thinking if I could use the same entities and repositories with different datasources for it.
#Entity
#Table(name = "Street", indexes = {#Index(name = "street_id_index", columnList = "streetid")})
public class Street {
#Id
private String streetid;
public String streetcode;
public String streetname;
public String streetnameaddd;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "municipalitycode", referencedColumnName = "municipalitycode")
public Municipality municipalitycode;
public String is_deleted;
}
public interface Street_Repository extends JpaRepository<Street,String> {
List<Street> findAll();
}
#Configuration
public class AzureConfig {
#Bean("AzureDataSource")
#ConfigurationProperties(prefix = "spring.datasource-azure-sql")
public DataSource dataSourceAzureSql(){
return DataSourceBuilder.create().build();
}
}
#Configuration
public class OracleConfig {
#Bean
#ConfigurationProperties(prefix = "spring.datasource-oracle")
public DataSource dataSourceOracle(){
return DataSourceBuilder.create().build();
}
}
spring:
datasource-azure-sql:
jdbc-url: jdbc:sqlserver://xxxxxxx.database.windows.net:1433;database=xxxxxxxxxxx-xxx;user=xxxxx#xxxxx;password=xxxxxxxxx;encrypt=true;trustServerCertificate=false;hostNameInCertificate=xxxxxxxx;loginTimeout=30
datasource-oracle:
jdbc-url: jdbc:oracle:thin:#xxxxx.xx.xxx:xxx:ssid
#Repository
public class Stree_Repo implements Street_Repository {
private JdbcTemplate jdbcTemplate;
#Autowired
#Qualifier("AzureDataSource")
public void setDataSource(DataSource dataSource){
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
#Autowired
#Qualifier("OracleDataSource")
public void setDataSource2(DataSource dataSource){
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
#Override
public List<Street> findAll() {
return null;
}
... rest override methods...
}
So the classe Street_Repo implements the interface and because this Entity is part of both Oracle and Azure I was wondering if there was a possible of not repeating the Entity and all the other classes associated.
Thank you in advance!
I have an entity and would like to implement Audit and AuditHistory, both works but while unit testing Application context is null.
The Entity
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#EntityListeners(UserListener.class)
public class User extends BaseModel<String> {
#Column
private String username;
#Column
private String password;
#Transient
private String passwordConfirm;
#ManyToMany
private Set<Role> roles;
}
UserListener
public class UserListener {
#PrePersist
public void prePersist(User target) {
perform(target, INSERTED);
}
#PreUpdate
public void preUpdate(User target) {
perform(target, UPDATED);
}
#PreRemove
public void preRemove(User target) {
perform(target, DELETED);
}
#Transactional(MANDATORY)
void perform(User target, Action action) {
EntityManager entityManager = BeanUtil.getBean(EntityManager.class);
if(target.isActive()){
entityManager.persist(new UserAuditHistory(target, action));
}else{
entityManager.persist(new UserAuditHistory(target, DELETED));
}
}
}
UserAuditHistory
#Entity
#EntityListeners(AuditingEntityListener.class)
public class UserAuditHistory {
#Id
#GeneratedValue
private Long id;
#ManyToOne
#JoinColumn(name = "user_id", foreignKey = #ForeignKey(name = "FK_user_history"))
private User user;
#CreatedBy
private String modifiedBy;
#CreatedDate
#Temporal(TIMESTAMP)
private Date modifiedDate;
#Enumerated(STRING)
private Action action;
public UserAuditHistory() {
}
public UserAuditHistory(User user, Action action) {
this.user = user;
this.action = action;
}
}
BeanUtil for getting and setting context
#Service
public class BeanUtil implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
}
Now the Junit where I get null pointer exception on the context from the above BeanUtil class in getBean() method.
#RunWith(SpringRunner.class)
#DataJpaTest
public class UserRepositoryTest{
#Autowired
private TestEntityManager entityManager;
#Autowired
private UserRepository repository;
#Test
public void whenFindAll_theReturnListSize(){
entityManager.persist(new User("jk", "password", "password2", null));
assertEquals(repository.findAll().size(), 1);
}
}
This is how I solved the problem, in the test class
#Autowired
ApplicationContext context;
inside the test method called
BeanUtil beanUtil = new BeanUtil();
beanUtil.setApplicationContext(context);
and it worked.
The problem is, that you're not using spring's AOP, but the static context:
private static ApplicationContext context;
It's null, because not creating #Bean leads to unproxied objects. The solution would be to #Autowire it.
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;
}
}
I am developing an JavaFx application with spring boot,JPA, and H2. I have a user entity when I try to add a new user into the DB it throws NPE in the controller on the button's click action. As it is seen I use only autowire notation. I researched
but findings did not help out. Any help please?
package com.core;
#SpringBootApplication
#Import(SharedSpringConfiguration.class)
public class Runner extends Application {
private ConfigurableApplicationContext context;
public static void main(String[] args) {
launch(args);
}
#Override
public void init() {
context = SpringApplication.run(Runner.class);
}
}
package com.dao;
#Entity
#Table(name = "user")
public class User {
#Id
#Column(name = "id", updatable = false, nullable = false)
private long ID;
#Column(nullable = false)
private String userName;
#Column(nullable = false)
private String userPass;
public User() {
}
public User(long ID, String userName, String userPass) {
this.ID = ID;
this.userName = userName;
this.userPass = userPass;
}
}
package com.service;
#Service
public class UserService {
#Autowired
private UserRepository userRepository;
public UserService() {
}
public void saveUser(User user) {
userRepository.save(user);
}
}
package com.repository;
public interface UserRepository extends CrudRepository<User, Long> {}
package com.controller
#Controller
public class MethodController implements Initializable {
#Autowired
private UserService userService;
#FXML
void methodSave(MouseEvent event) {
userService.saveUser(new User(11, "TestUser", "noPass")); //Throws NPE. Indicates that userService is null. But I autowire the userService.
}
}
I don't know what's in SharedSpringConfiguration, but you probably need #EnableJpaRepositories on one of your configuration classes. #Repository on the CrudRepo should be unnecessary.
Change your SpringBootApplication package from com.core to com
because SpringBootApplication by default will scan only that packages and sub packages.
else
add #ComponentScan annotation in SpringBootApplication and scan the packages.
I am having some trouble with Spring Boot, Spring Data and having Entities in an external jar. Any help would be greatly appreciated!
My Sprint Data repository looks like this:
#Repository
public interface MyFileRepository extends PagingAndSortingRepository<MyFile, Long> {
#Modifying
#Transactional
#Query("Delete from MyFile f where f.created < ?1")
long deleteOldEntities(Date cutoffDate);
}
My entity, which is in another jar entirely looks like this:
#Entity
#SequenceGenerator(
name = "SequenceIdGenerator",
sequenceName = "SEQ_ID_MY_FILE",
allocationSize = 20
)
#Table(
name = "MYFILE_TABLE"
)
public class MyFile extends BaseEntity {
private long id;
private byte[] data;
[...]
public MyFile() {}
#Id
#Column(
name = "id",
nullable = false
)
#GeneratedValue(
generator = "SequenceIdGenerator"
)
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
[...]
}
And the BaseEntity looks like this:
#MappedSuperclass
public abstract class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
private static final Charset UTF_8 = Charset.forName("UTF-8");
private Date created = null;
private Date updated = null;
public BaseEntity() {}
#Column(
name = "created"
)
#Temporal(TemporalType.TIMESTAMP)
public Date getCreated() {
return this.created == null?null:new Date(this.created.getTime());
}
public void setCreated(Date created) {
if(created != null) {
this.created = new Date(created.getTime());
}
}
So, when I try to run this code I get a long stacktrace which basically ends with:
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: MyFile is not mapped [Delete from MyFile f where f.created < ?1]
I believe that this may have something to do with the Spring Boot Configuration. The external jar does not have and #SpringBootApplication anywhere. It is basically just a jar with all my Entities.
My application jar however has this:
#SpringBootApplication
#EntityScan("myapp.service.dao.entity") --> This is the package where all my entities are located.
public class CommonApplication {
}
What is my error?
To scan entities residing in jar, you have to set packagesToScan field of LocalSessionFactory.
#Bean
public LocalSessionFactoryBean sessionFactory(DataSource dataSource) {
LocalSessionFactoryBean localSessionFactory = new LocalSessionFactoryBean();
localSessionFactory.setDataSource(dataSource);
localSessionFactory
.setPackagesToScan(new String[]{"myapp.service.dao.entity", "com.application.entity"});
return localSessionFactory;
}
I got this working using by using the following bean to set the packages scan:
#Bean
public EntityManagerFactory entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(false);
vendorAdapter.setShowSql(false);
vendorAdapter.setDatabase(Database.MYSQL);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan("add packages here");
return factory.getObject();
}