sharding using jpa repository dynamically - java

I have 3 shards(3 different Ips, usernames and passwords). I am using spring boot, spring jpa to save the data in DB. Let's say Customer is my entity. Let's assume, based on some organizationId, I need to insert/update/select the data from different databases.
Current approach :
#Getter
#Setter
#Entity
#Table
#NoArgsConstructor
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long customerId;
private String firstName;
private String lastName;
private Long organizationId;
}
package lane1;
#Repository
public interface CustomerRepo1 extends CrudRepository<Customer,Long> {
}
package lane2;
#Repository
public interface CustomerRepo2 extends CrudRepository<Customer,Long> {
}
package lane3;
#Repository
public interface CustomerRepo3 extends CrudRepository<Customer,Long> {
}
I am creating 3 datasources(DatasourceLane1, DatasourceLane2, DatasourceLane3) and setting the base packages in the configuration class.
#Configuration
#ConfigurationProperties("DatasourceLane1")
#EnableJpaRepositories(basePackages = "lane1", entityManagerFactoryRef = "lane1EntityManagerFactory",
transactionManagerRef = "lane1TransactionManager")
public class DBConfigurationLane1 {
#NotNull
private String username;
#NotNull
private String password;
#NotNull
private String url;
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setUrl(String url) {
this.url = url;
}
//#Primary
#Bean
DataSource dataSourceLane1() throws SQLException {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setJdbcUrl(url);
return dataSource;
}
//#Primary
#Bean(name = "lane1EntityManagerFactory")
public EntityManagerFactory propEntityManagerFactory() throws SQLException {
Properties properties = new Properties();
// properties.put("hibernate.hbm2ddl.auto", "update");
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setPersistenceUnitName("lane1");
em.setDataSource(dataSourceLane1());
em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
em.setPackagesToScan("lane1");
em.setJpaProperties(properties);
em.afterPropertiesSet();
return em.getObject();
}
//#Primary
#Bean(name = "lane1TransactionManager")
public PlatformTransactionManager propTransactionManager(
#Qualifier("lane1EntityManagerFactory") EntityManagerFactory barEntityManagerFactory) {
return new JpaTransactionManager(barEntityManagerFactory);
}
}
Similarly I have datasource2 and datasource3.
My goal is to have single repository class and dynamically use different datasource based on organizationId.
NOTE : I want to reuse all SimpleJpaRepository(CrudRepository) methods too.
It would be great if someone helps me with code examples. Thank you in advance.

Related

Export from Oracle and Import to Azure SQL reusing Entities Spring Boot

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!

Is there a way to map mutliple databases with same table names in a dynamic way?

First time I encounter this problem. The situation is:
I have more than 100 SQL databases, each one correspond to a different company and each one have the same three tables (same table names, same column names, same column data type).
Is there some way to map all these databases dynamically?
With dynamically I mean to have one class to which I can refer and make any CRUD operation.
After some research I could see what I wanted to do:
Basically I needed to change my data source in run time, for that I used a spring framework interface called AbstracRoutingDataSource.
Example:
Implementing AbstractRoutingDataSource:
public class MultiRoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return DBContextHolder.getCurrentDb();
}
}
DatabaseContextHolder:
public class DBContextHolder {
private static final ThreadLocal<DBTypeEnum> contextHolder = new ThreadLocal<>();
public static void setCurrentDb(DBTypeEnum dbType) {
contextHolder.set(dbType);
}
public static DBTypeEnum getCurrentDb() {
return contextHolder.get();
}
public static void clear() {
contextHolder.remove();
}
}
Database type enum:
public enum DBTypeEnum{
DATASOURCE1("DATASOURCE1"),
DATASOURCE2("DATASOURCE2");
DBTypeEnum(final String dbTypeEnum){
this.dbTypeEnum = dbTypeEnum;
}
private String dbTypeEnum;
public String dbTypeEnum(){
return dbTypeEnum;
}
}
Persistence configuration:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
basePackages = "base.packages.path",
entityManagerFactoryRef = "multiEntityManager",
transactionManagerRef = "multiTransactionManager"
)
public class PersistenceConfiguration {
private final String PACKAGE_SCAN = "base.package.path";
#Bean(name = "dataSource1")
#ConfigurationProperties("spring.datasource1")
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
#Bean(name = "dataSource2")
#ConfigurationProperties("spring.datasource2")
public DataSource dataSource2() {
return DataSourceBuilder.create().build();
}
#Bean(name = "multiRoutingDataSource")
public DataSource multiRoutingDataSource() {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DBTypeEnum.DATASOURCE1, dataSource1());
targetDataSources.put(DBTypeEnum.DATASOURCE2, dataSource2());
MultiRoutingDataSource multiRoutingDataSource = new MultiRoutingDataSource();
multiRoutingDataSource.setDefaultTargetDataSource(dataSource1());
multiRoutingDataSource.setTargetDataSources(targetDataSources);
return multiRoutingDataSource;
}
#Bean(name = "multiEntityManager")
public LocalContainerEntityManagerFactoryBean multiEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(multiRoutingDataSource());
em.setPackagesToScan(PACKAGE_SCAN);
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(hibernateProperties());
return em;
}
#Bean(name = "multiTransactionManager")
public PlatformTransactionManager multiTransactionManager() {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
multiEntityManager().getObject());
return transactionManager;
}
#Bean(name = "dbSessionFactory")
public LocalSessionFactoryBean dbSessionFactory() {
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(multiRoutingDataSource());
sessionFactoryBean.setPackagesToScan(PACKAGE_SCAN);
sessionFactoryBean.setHibernateProperties(hibernateProperties());
return sessionFactoryBean;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.show_sql", true);
properties.put("hibernate.format_sql", true);
return properties;
}
}
Then you need to have all of your database information in a .properties file:
spring.datasource1.jdbcUrl=jdbc:sql:sql-url:3306/datasource1
spring.datasource1.username=username
spring.datasource1.password=password
spring.datasource1.driver-class-name= Driver
spring.datasource2.jdbcUrl=jdbc:sql:sql-url:3306/datasource2
spring.datasource2.username=username
spring.datasource2.password=password
spring.datasource2.driver-class-name= Driver
Then you need to map your entity:
#Entity
#Table(name = "table_name")
#Getter
#Setter
public class MyEntity implements Serializable {
#Id
#Column(name = "ID", columnDefinition = "varchar(17)")
private String id;
//more fields...
}
I used spring CrudRepositories interface for this entity
public interface IMyEntityRepository extends CrudRepository<MyEntity, String> {
}
Finally my controller is prepared to change datasource depending on a JSON field in my request.
JSON:
{
"dataSource":"DATASOURCE1"
//more fields ...
}
RESTController:
#PutMapping("/url/{id}")
public ResponseEntity<?> editMyEntity(#RequestBody RequestObject request #PathVariable String id){
DBContextHolder.setCurrentDb(DBTypeEnum.valueOf(request.getDataSource);
iMyEntitiRepository.getMyEntity(id);
//...
}

What is wrong of this code? (Multiple Datasource Oracle)

Currently I have to connect two Oracle Datasources in my Spring Boot proyect. I think that I did well the configuration because Spring validated my connection but when I call a service this always response me "Empty". I think is because the TransactionManager don't change.
This is my project structure:
- project
⊢---- config
⊢---- controllers
∟---- models
⊢---- dao
| ⊢---- db1
| ∟---- db2
⊢---- entity
| ⊢---- db1
| ∟---- db2
⊢---- services
⊢---- db1
∟---- db2
This are my entities classes:
package project.models.entity.db1;
//// IMPORTS
#Entity(name = "User")
#Table(name = "T_USER")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private Long id;
private String name;
private String surname;
private String username;
private String password;
////// CONSTRUCT
////// GETTERS & SETTERS
////// HASCODE, EQUALS AND TOSTRING
}
package project.models.entity.db2;
//// IMPORTS
#Entity(name = "Shareholder")
#Table(name = "Shareholder")
public class Shareholder implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private Integer id;
private Integer numberTitles;
////// CONSTRUCT
////// GETTERS & SETTERS
////// HASCODE, EQUALS AND TOSTRING
}
This are my repositories classes:
package project.models.dao.db1;
//// IMPORTS
#PersistenceContext(name = "db1")
#Repository
public interface IUserDao extends JpaRepository<User, Long> {
}
package project.models.dao.db2;
//// IMPORTS
#PersistenceContext(name = "db2")
#Repository
public interface IShareholderDao extends JpaRepository<Shareholder, Integer> {
}
This are my services classes:
package project.models.services.db1.impl;
//// IMPORTS
#Service
public class UserServiceImpl implements IUserService {
#Autowired
private IUserDao userDao;
#Override
#Transactional("transactionManager")
public List<User> findAll() {
return userDao.findAll();
}
#Override
#Transactional("transactionManager")
public User findById(Long id) {
return userDao.findById(id).orElse(null);
}
package project.models.services.db2.impl;
//// IMPORTS
#Service
public class ShareholderServiceImpl implements IShareholderService {
#Autowired
private IShareholderDao shareholderDao;
#Override
#Transactional("db2TransactionManager")
public List<Shareholder> findAll() {
return shareholderDao.findAll();
}
#Override
#Transactional("db2TransactionManager")
public Shareholder findById(Integer id) {
return shareholderDao.findById(id).orElse(null);
}
}
And this are my config classes:
package project.config;
//// IMPORTS
#Configuration
#ConfigurationProperties(prefix = "oracle1")
#EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager", basePackages = {
"project.models.dao.db1" })
#EnableTransactionManagement
#Validated
public class Db1Config {
#NotNull
private String username;
#NotNull
private String password;
#NotNull
private String url;
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws SQLException {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] { "project.models.entity.db1" });
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(hibernateProperties());
em.setPersistenceUnitName("db1");
return em;
}
#Bean(name = "dataSource")
DataSource dataSource() throws SQLException {
OracleDataSource dataSource = new OracleDataSource();
dataSource.setUser(username);
dataSource.setPassword(password);
dataSource.setURL(url);
dataSource.setImplicitCachingEnabled(true);
dataSource.setFastConnectionFailoverEnabled(true);
return dataSource;
}
#Primary
#Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager() throws SQLException {
return new JpaTransactionManager(entityManagerFactory().getObject());
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.Oracle10gDialect");
properties.put("hibernate.show_sql", "true");
properties.put("hibernate.hbm2ddl.auto", "validate");
return properties;
}
}
package project.config;
//// IMPORTS
#Configuration
#ConfigurationProperties(prefix = "oracle2")
#EnableJpaRepositories(entityManagerFactoryRef = "db2EntityManagerFactory", transactionManagerRef = "db2TransactionManager", basePackages = {
"project.models.dao.db2" })
#EnableTransactionManagement
#Validated
public class Db2Config {
#NotNull
private String username;
#NotNull
private String password;
#NotNull
private String url;
#Bean(name = "db2EntityManagerFactory")
public LocalContainerEntityManagerFactoryBean db2EntityManagerFactory() throws SQLException {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(db2DataSource());
em.setPackagesToScan(new String[] { "project.models.entity.db2" });
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(hibernateProperties());
em.setPersistenceUnitName("db2");
return em;
}
#Bean(name = "db2DataSource")
DataSource db2DataSource() throws SQLException {
OracleDataSource dataSource = new OracleDataSource();
dataSource.setUser(username);
dataSource.setPassword(password);
dataSource.setURL(url);
dataSource.setImplicitCachingEnabled(true);
dataSource.setFastConnectionFailoverEnabled(true);
return dataSource;
}
#Bean(name = "db2TransactionManager")
public PlatformTransactionManager db2TransactionManager() throws SQLException {
return new JpaTransactionManager(db2EntityManagerFactory().getObject());
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.Oracle10gDialect");
properties.put("hibernate.show_sql", "true");
properties.put("hibernate.hbm2ddl.auto", "validate");
return properties;
}
}
And my main application:
package project;
//// IMPORTS
#SpringBootApplication
#EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class })
#EnableCaching
public class ProjectApplication {
#Autowired
private IUserService userService;
#Autowired
private IShareholderService shareholderService;
public static void main(String[] args) {
SpringApplication.run(ProjectApplication.class, args);
}
#PostConstruct
void init() {
List<User> user = userService.findAll();
List<Shareholder> shareholder = shareholderService.findAll();
System.out.println();
System.out.println(user);
System.out.println(shareholder);
System.out.println();
}
}
And always response me:
[User [id=39, name=Jack, surname=Sparrow, ...], [...], ...]
[]
What is the error? Because I don't see it.
Thank you very much
In Db2Config class, package being scanned is different than Db1Config.
Db1Config:
em.setPackagesToScan(new String[] { "project.models.entity.db1" });
Db2Config
em.setPackagesToScan(new String[] { "project.models.dao.db2" });

How to configure and get session in Hibernate using .properties

All examples that I found contains information about Hibernate.cfg.xml, but I'm using hibernate.properties instead of it.
In resources folder:
hibernate.dialet=org.hibernate.dialect.PostgreSQL9Dialect
hibernate.show_sql=true
hibernate.hbm2ddl.auto=update
I'm using properties here:
#Configuration
#EnableJpaRepositories("com.some.server.repository")
#EnableTransactionManagement
#PropertySource("classpath:db.properties")
#ComponentScan("com.some.server")
public class DataBaseConfig {
#Resource
private Environment env;
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(env.getRequiredProperty("db.entity.package"));
em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
em.setJpaProperties(getHibernateProperties());
return em;
}
#Bean
public DataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setUrl(env.getRequiredProperty("db.url"));
ds.setDriverClassName(env.getRequiredProperty("db.driver"));
ds.setUsername(env.getRequiredProperty("db.username"));
ds.setPassword(env.getRequiredProperty("db.password"));
ds.setInitialSize(Integer.valueOf(env.getRequiredProperty("db.initialSize")));
ds.setMinIdle(Integer.valueOf(env.getRequiredProperty("db.minIdle")));
ds.setMaxIdle(Integer.valueOf(env.getRequiredProperty("db.maxIdle")));
ds.setTimeBetweenEvictionRunsMillis(Long.valueOf(env.getRequiredProperty("db.timeBetweenEvictionRunsMillis")));
ds.setMinEvictableIdleTimeMillis(Long.valueOf(env.getRequiredProperty("db.minEvictableIdleTimeMillis")));
ds.setTestOnBorrow(Boolean.valueOf(env.getRequiredProperty("db.testOnBorrow")));
ds.setValidationQuery(env.getRequiredProperty("db.validationQuery"));
return ds;
}
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager manager = new JpaTransactionManager();
manager.setEntityManagerFactory(entityManagerFactory().getObject());
return manager;
}
public Properties getHibernateProperties() {
try {
Properties properties = new Properties();
InputStream is = getClass().getClassLoader().getResourceAsStream("hibernate.properties");
properties.load(is);
return properties;
} catch (IOException e) {
throw new IllegalArgumentException("Can't find 'hibernate.properties' in classpath!", e);
}
}
}
Is there any chance to get session (for fetch.LAZY), using hibernate.properties?
UPDATE:
I have JPA, but still need session.
In my entity I have:
#Entity
#Table(name = "users")
public class User {
...
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.PERSIST})
#JoinColumn(name = "id_city", nullable = true)
private City city;
public City getCity() {
return city;
}
public void setCity(City city) {
this.city = city;
}
...
}
AND:
#Entity
#Table(name = "city")
public class City {
....
#OneToMany(fetch = FetchType.LAZY, mappedBy = "city")
#JsonIgnore
private Set<User> users;
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
...
}
So, when I try to get:
#RestController
#RequestMapping("/user")
public class UserController {
#Autowired
private UserRepository userRepository;
#Autowired
private ProfessionRepository profRepository;
#RequestMapping(value = "", method = RequestMethod.GET)
public List<User> getAllUsers() {
// List<User> list = userRepository.findAll();
return userRepository.findAll();
}
From:
public interface UserRepository extends JpaRepository<User, Long>{
}
I've got an error, if id_city is not empty:
And if id_city is empty (Fetch.LAZY is not using), everything is ok:

Java + Spring MVC + Hibernate Persistence Error - Cant Persist User [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
Ok so I am building an ecommerce site using java spring mvc, spring security, maven, hibernate, and some other technologies however those should be the only ones relevant to my problem. I am doing all java config with no XML by the way (which I am currently considering revising since I am running into situations where xml just seems easier).
I would like to know why I am getting an no exceptions upon maven install but getting exceptions on server restart (tomcat 7).
I have tried to get this down to as simple as possible while still having a user and a role. When I am looking at the code, it really is pretty straight forward stuff - I am dying to find out why it errors out everytime.
Main Appconfig class
#EnableWebMvc
#Configuration
#ComponentScan({ "com.crutchsf.*" })
#EnableTransactionManagement
#Import({ SecurityConfig.class })
public class AppConfig {
#Bean
public SessionFactory sessionFactory() {
LocalSessionFactoryBuilder builder = new LocalSessionFactoryBuilder(dataSource());
builder
.scanPackages("com.crutchsf.users.model")
.addProperties(getHibernateProperties());
return builder.buildSessionFactory();
}
private Properties getHibernateProperties() {
Properties prop = new Properties();
prop.put("hibernate.format_sql", "true");
prop.put("hibernate.show_sql", "true");
prop.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
return prop;
}
#Bean(name = "dataSource")
public BasicDataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/test");
ds.setUsername("bobby");
ds.setPassword("password");
return ds;
}
#Bean
public HibernateTransactionManager txManager() {
return new HibernateTransactionManager(sessionFactory());
}
#Bean
public UserService userService() {
UserService userService = new MyUserDetailsService();
userService.setUserDao(new UserDaoImpl());
return userService;
}
#Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("validation");
return messageSource;
}
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/pages/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/img/**").addResourceLocations("/img/").setCachePeriod(31556926);
}
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
SecurityConfig.java
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("userDetailsService")
UserDetailsService userDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/admin/**")
.access("hasRole('ROLE_ADMIN')").and().formLogin()
.loginPage("/login").failureUrl("/login?error")
.usernameParameter("username")
.passwordParameter("password")
.and().logout().logoutSuccessUrl("/login?logout")
.and().csrf()
.and().exceptionHandling().accessDeniedPage("/403");
}
}
User class
#Entity
#Table(name = "users", catalog = "test")
public class User {
private Integer user_id;
private String username;
private String password;
private String passwordConf;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
#Id
#Column(name = "user_id")
#GeneratedValue
public Integer getUserId() {
return this.user_id;
}
#Column(name = "username", unique = true, nullable = false, length = 45)
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
#Column(name = "password", nullable = false, length = 60)
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
#Transient
public String getPasswordConf() {
return this.passwordConf;
}
public void setPasswordConf(String passwordConf) {
this.passwordConf = passwordConf;
}
}
UserRole class
#Entity
#Table(name = "user_roles", catalog = "test",
uniqueConstraints = #UniqueConstraint(
columnNames = { "role", "username" }))
public class UserRole{
private Integer userRoleId;
private User user;
private String role;
public UserRole() {
}
public UserRole(User user, String role) {
this.user = user;
this.role = role;
}
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "user_role_id",
unique = true, nullable = false)
public Integer getUserRoleId() {
return this.userRoleId;
}
public void setUserRoleId(Integer userRoleId) {
this.userRoleId = userRoleId;
}
public User getUser() {
return this.user;
}
public void setUser(User user) {
this.user = user;
}
#Column(name = "role", nullable = false, length = 45)
public String getRole() {
return this.role;
}
public void setRole(String role) {
this.role = role;
}
}
UserDao Interface
public interface UserDao {
User findByUserName(String username);
List<User> findAllUsers();
void addUser(User user);
}
UserDaoImpl
#Repository
public class UserDaoImpl implements UserDao {
#Autowired
private SessionFactory sessionFactory;
#SuppressWarnings("unchecked")
public User findByUserName(String username) {
List<User> users = new ArrayList<User>();
users = sessionFactory.getCurrentSession().createQuery("from User where username=?").setParameter(0, username)
.list();
if (users.size() > 0) {
return users.get(0);
} else {
return null;
}
}
#Override
#Transactional
public void addUser(User user) {
sessionFactory.getCurrentSession().persist(user);
}
UserService Interface
public interface UserService {
UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException;
void setUserDao(UserDao userDao);
void addUser(com.crutchsf.model.users.User user);
}
User Service Impl
#Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService, UserService {
#Autowired
private UserDao userDao;
#Transactional(readOnly=true)
#Override
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
com.crutchsf.model.users.User user = userDao.findByUserName(username);
List<GrantedAuthority> authorities = buildUserAuthority(user.getUserRole());
return buildUserForAuthentication(user, authorities);
}
private User buildUserForAuthentication(com.crutchsf.model.users.User user, List<GrantedAuthority> authorities) {
return new User(user.getUsername(), user.getPassword(), user.isEnabled(), true, true, true, authorities);
}
private List<GrantedAuthority> buildUserAuthority(Set<UserRole> userRoles) {
Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
// Build user's authorities
for (UserRole userRole : userRoles) {
setAuths.add(new SimpleGrantedAuthority(userRole.getRole()));
}
List<GrantedAuthority> Result = new ArrayList<GrantedAuthority>(setAuths);
return Result;
}
#Override
#Transactional
public void addUser(com.crutchsf.model.users.User user) {
this.userDao.addUser(user);
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
Database SQL used
CREATE TABLE users (
user_id int(11) NOT NULL AUTO_INCREMENT,
username VARCHAR(45) NOT NULL UNIQUE,
PRIMARY KEY (user_id));
Ok I shortened this quite a bit to the simple task of persisting a user. I cant get the server to startup without errors. There has to be something wrong in the code above.
In AppConfig.java if I change the line "scanPackages...." to the actual class name
com.crutchsf.users.model.User
then my server starts up fine but when it reaches the line in my userDao to be persisted
sessionFactory.getCurrentSession().persist(user);
I get a different error from hibernate "Cant find entity com.crutchsf.users.model.User"
Here is the main error I get. Basically I can choose which error I want to get now.
[public org.hibernate.SessionFactory com.crutchsf.config.AppConfig.sessionFactory()] threw exception; nested exception is java.lang.NullPointerException
thank you
On the surface, this doesn't appear to be anything to do with Spring MVC or Hibernate.
The error message is:
Factory method [public org.hibernate.SessionFactory com.crutchsf.config.AppConfig.sessionFactory()] threw exception; nested exception is java.lang.NullPointerException
If you have a stacktrace, you'll be able to see exactly what line the null reference occurred on. If not, you'll have to fire up a debugger (or insert println statements) to find out.
Either way, it appears that your AppConfig class has a bug in it, or you are not initialising it correctly. Look at the sessionConfig() method to see why it NPEs, and ensure that the appropriate references are not null before calling it.

Categories

Resources