how to generate tables from spring components with spring-mybatis? - mysql

I'm using spring-mybatis and I have configured many components and mappers.
My services are working without any problems but i want to know if is it possible to generate tables from spring component (using annotations #Component) or if I have to Create tables manually so I can execute my mappers methods ?

I've used the following method to do this, but maybe it's a bit hacky.
Create your mapper as normal, but add another interface method to create the table:
#Insert("create table if not exists students (name integer)")
public void createTable();
Then, in your service class, create a method with the #PostContructannotation so that it gets called at startup:
#Autowired
private StudentDao studentMapper;
#PostConstruct
private void setup() {
// setup stuff goes here.
studentMapper.createTable();
// include any other mappers you have...
}
I tried annotating the createTable() method with #PostConstructbut it wasn't executed. Probably because it is an interface.
Hope that helps
Owen

Related

Override primary key generator strategy in Spring Boot when deployed to MySQL

In my application I want to support more databases to which it can be loaded, and for MS SQL Server I have set the identity generator to SEQUENCE
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
Because it can be deployed to MySQL as well, I need somehow to change the generator to IDENTITY since SEQUENCE is not available for MySQL, is there a way to do it programatically ?
The simplest solution would probably be to create your schema without the help of Hibernate (e.g. manage your schema using a tool like Liquibase) and use the DB capabilities for assigning the ids. In your specific scenario, you could probably use strategy = IDENTITY for both DBs (just so that Hibernate delegates the id column management to the DB) and then create an INSTEAD OF INSERT trigger for SQL Server. I'm not sure about performance, though.
If you still want Hibernate to do the job - it's not going to be super easy, but I can think of one option you could try:
Use the #GeneratedValue.name property with your id field:
#Id
#GeneratedValue(name = "my-entity-generator")
private Long id;
Declare two versions of my-entity-generator for the two databases. You need to put them on something that is optional for the entity scan. #GenericGenerator can be put on a package, for instance, so you can use two empty packages and declare the generators corresponding to the two strategies in their respective package-infos (never tried it with Spring, though, so I'm not sure if that will get picked up). You could probably also use two dummy #MappedSuperclasses in two different packages.
So, let's say you end up with one mapped superclass called com.example.mssql.Generators:
#MappedSuperclass
#GenericGenerator(name = "my-entity-generator", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", ...)
public abstract class Generators {}
And another one called com.example.mysql.Generators:
#MappedSuperclass
#GenericGenerator(name = "my-entity-generator", strategy = "org.hibernate.id.IdentityGenerator")
public abstract class Generators {}
Conditionally include the two packages in the entity scan:
#Profile("mysql") // or #ConditionalOnProperty, or your own custom condition
#Configuration
#EntityScan(basePackages = "com.example.mysql")
public class MySqlConfig {}
#Profile("mssql") // or #ConditionalOnProperty, or your own custom condition
#Configuration
#EntityScan(basePackages = "com.example.mssql")
public class MsSqlConfig {}
(of course, for this to work, you need an unconditional #EntityScan on top of e.g. your application class that does not cover the two packages)
Another possible option could be to only have one generator declaration but instead use your own custom generator implementation that detects the DB and then delegates to either an IdentityGenerator or a SequenceStyleGenerator. Thus, you have one option that involves configuration magic and another that involves heavy coding.
(finally, I think you could also use Hibernate mapping XML files and - again - conditionally include them in your mapping, but it's an ancient technique and the documentation is not great)

Spring Data JPA - Pessimistic Locking Not Working

Using: Spring Boot 2.3.3, MySQL 5.7(currently via TestContainers), JUnit 5
I have a JpaRepository inside a Spring MVC application that has a method set to be #Lock(LockModeType.PESSIMISTIC_WRITE) and, while I do see the SELECT ... FOR UPDATE coming up in the resulting SQL, it doesn't seem to do much of anything.
I'll put the code below, but, if I try to spin up multiple threads that make the same call, each thread is able to read the same initial value in question and nothing ever seems to block/wait. And my understanding is that any "additionally" called methods that are also #Transactional (from the org.springframework.transaction namespace) are made part of the original transaction.
I can't figure out what I'm doing wrong. Any assistance would be appreciated, even if it means pointing out that my understanding/expectations are flawed.
Repository
public interface AccountDao extends JpaRepository<Account, Long> {
#Lock(LockModeType.PESSIMISTIC_WRITE)
public Optional<Account> findById(Long id);
}
Services
Account Service
#Service
public class AccountServiceImpl implements AccountService {
#Autowired
private FeeService feeService;
#Override
#Transactional // have also tried this with REQUIRES_NEW, but the same results occur
public void doTransfer(Long senderId, Long recipientId, TransferDto dto) {
// do some unrelated stuff
this.feeService.processFees(recipientId);
}
}
Fee Service
#Service
public class FeeServiceImpl implements FeeService {
#Autowired
private AccountDao accountDao;
#Override
#Transactional // have also tried removing this
public void processFees(Long recipientId) {
// this next line is actually done through another service with a #Transactional annotation, but even without that annotation it still doesn't work
Account systemAccount = this.accountDao.findById(recipientId);
System.out.println("System account value: " + systemAccount.getFunds());
systemAccount.addToFunds(5);
System.out.println("Saving system account value: " + systemAccount.getFunds());
}
}
Test
public class TheTest {
// starts a #SpringBootTest with ```webEnvironment = WebEnvironment.RANDOM_PORT``` so it should start up a dedicated servlet container
// also auto configures a WebTestClient
#Test
#Transactional
public void testLocking() {
// inserts a bunch of records to have some users and accounts to test with and does so via JPA, hence the need for #Transactional
// code here to init an ExecutorService and a synchronized list
// code here to create a series of threads via the ExecutorService that uses different user IDs as the sender, but the same ID for the recipient, hence the need for pessimistic locking
}
}
I can put in the testing code if necessary, but, I'm not sure what other details are necessary.
The resulting output (especially from the System.out.println calls in FeeServiceImpl) shows that the same "system account" value is read in across all threads, and the saved value is, therefore, also always the same.
When the application starts up, that value is 0, and all threads read that 0, with no apparent locking or waiting. I can see multiple transactions starting up and committing (I increased the logging level on Hibernate's TransactionImpl), but, it doesn't seem to matter.
Hopefully I'm overlooking or doing something dumb, but, I can't quite figure out what it is.
Thank you!
Of course, it was something buried that I wasn't expecting.
It turns out my tables had been created using MyISAM instead of InnoDB, oddly, since that hasn't been the default for table creation in MySQL in a long time.
So, here's what I did:
I thought I was using MySQL 8.0. Turns out TestContainers defaults (to 5.7.22 in my case) when using a JDBC connection string that doesn't specifically name the version. So I fixed that.
This still didn't fix things as MyISAM was still being used. It turns out this was because I had a legacy dialect setting in my configuration. Updating that to something like MySQL57Dialect corrected that.
This actually also explains the "weird" behaviour I was seeing in my JUnit tests, as values were popping into the DB right away and not rolling back, etc.
I hope this helps someone else in the future!

With jUnit 4, can I parameterize #BeforeClass?

I am using jUnit to manage integration tests for an application that accesses a database. Because setting up the test data is a time-consuming operation, I have been doing that in the #BeforeClass method, which is executed only once per test class (as opposed to the #Before method, which is run once per test method).
Now I want to try a few different permutations for the configuration of the data layer, running all of my tests on each different configuration. This seems like a natural use of the Parameterized test runner. Problem is, Parameterized supplies parameters to the class constructor, and the #BeforeClass method is abstract and is called before the class constructor.
A few questions,
Does Parameterized call the #BeforeClass method for each permutation of parameters, or does it only call once?
If the #BeforeClass method is called repeatedly, is there some way to access the parameter values from inside of it?
If none of these, what do people suggest as the best alternative approach to this problem?
I think you are going to need a custom test runner. I'm having the same issue you are having (needing to run the same tests using multiple, expensive configurations). You'd need a way to parameterize the set up, perhaps using #Parameter annotations similar to those used by the Parameterized runner but on static member fields instead of instance fields. The custom runner would have to find all static member fields with the #Parameter annotation and then run the test class (probably using the basic BlockJunit4ClassRunner) once per static #Parameter field. The #Parameter field should probably be a #ClassRule.
Andy on Software has done a good job of developing custom test runners, and he explains so pretty clearly in these blog posts here and here.
#BeforeClass is only called once in your example. Which makes sense given the name - before class!
If your tests require different data, there are two choices I can think of:
Set up that data in #Before so it is test specific
Group the tests that you want to run with the same data into separate test classes and use #BeforeClass for each one.
You can call this initialization logic in the constructor of your test class. Keep track of the last parameter used in a static variable. When it changes, set up the class for the new parameter.
I can't think of an equivalent for AfterClass.
This is an old question, but I just had to solve a probably similar problem. I went with the solution below for now, which essentially is an implementation of TREE's (updated) answer with using a generic abstract base class in order to avoid duplication whenever you need this mechanism.
Concrete tests would provide a #Parameters method that return an iterable of single-element arrays containing a Supplier< T > each. Those suppliers are then executed exactly once per actual input needed by the concrete test methods.
#RunWith(Parameterized.class)
public class AbstractBufferedInputTest<T> {
private static Object INPUT_BUFFER;
private static Object PROVIDER_OF_BUFFERED_INPUT;
private T currentInput;
#SuppressWarnings("unchecked")
public AbstractBufferedInputTest(Supplier<T> inputSuppler) {
if (PROVIDER_OF_BUFFERED_INPUT != inputSuppler) {
INPUT_BUFFER = inputSuppler.get();
PROVIDER_OF_BUFFERED_INPUT = inputSuppler;
}
currentInput = (T) INPUT_BUFFER;
}
/**
*
* #return the input to be used by test methods
*/
public T getCurrentInput() {
return currentInput;
}
}
You could do your initialization in a #Before method, writing to an instance variable but testing for null.
#RunWith(value = Parameterized.class)
public class BigThingTests {
private BigThing bigThing;
#Before
public void createBitThing() {
if (bigThing == null) {
bigThing = new BigThing();
}
}
...
}
A new instance of BigThingTests is created for each set of parameters, and bigThing is set to null with each new instance. The Parameterized runner is single-threaded, so you don't have to worry about multiple initializations.

How can I make JUnit let me set variables in one test case and access them in other if they are in the same class

Let say I have a test class called MyTest.
In it I have three tests.
public class MyTest {
AnObject object;
#Before
public void setup(){
object = new AnObject();
object.setSomeValue(aValue);
}
#Test
public void testMyFirstMethod(){
object.setAnotherValue(anotherValue);
// do some assertion to test that the functionality works
assertSomething(sometest);
}
#Test
public void testMySecondMethod(){
AValue val = object.getAnotherValue();
object.doSomethingElse(val);
// do some assertion to test that the functionality works
assertSomething(sometest);
}
Is there any way I can use the value of anotherValue, which is set with its setter in the first test, in the second test. I am using this for testing database functionality. When I create an object in the DB I want to get its GUID so I can use this to do updates and deletes in later test methods, without having to hardcode the GUID and therefore making it irrelevant for future use.
You are introducing a dependency between two tests. JUnit deliberately does not support dependency between tests, and you can't guarantee the order of execution (except for test classes in a test suite, see my answer to Has JUnit4 begun supporting ordering of test? Is it intentional?). So you really want to have dependencies between two test methods:
you have to use an intermediate static value
as Cedric suggests, use TestNG, which specifically supports dependencies
in this case, you can create a method to create the line, and call it from both methods.
I would personally prefer 3, because:
I get independent tests, and I can run just the second test (in Eclipse or such like)
In my teardown in the class, I can remove the line from the database, the cleanup. This means that whichever test I run, I always start off with the same (known) database state.
However, if your setup is really expensive, you can consider this to be an integration test and just accept the dependency, to save time.
You should use TestNG if you need this (and I agree it's fairly common in integration testing). TestNG uses the same instance to run your tests, so values stored in fields are preserved between tests, which is very useful when your objects are expensive to create (JUnit forces you to use statics to achieve the same effect, which should be avoided).
First off, make sure your #Test 's run in some kind of defined order
i.e. #FixMethodOrder(MethodSorters.NAME_ASCENDING)
In the example below, I'm assuming that test2 will run after test1
To share a variable between them, use a ThreadLocal (from java.lang).
Note that the scope of the ThreadLocal variable is to the thread, so if you are running multiple threads, each will have a copy of 'email' (the static in this case implies that its only global to the thread)
private static ThreadLocal<String> email = new ThreadLocal<String>();
#Test
public void test1 {
email.set("hchan#apache.org);
}
#Test
public void test2 {
System.out.println(email.get());
}
You should not do that. Tests are supposed to be able to run in random order. If you want to test things that depend on one value in the database, you can do that in the #Before code, so it's not all repeated for each test case.
I have found nice solution, just add Before annotation to the previous test!
private static String email = null;
#Before
#Test
public void test1 {
email = "test#google.com"
}
#Test
public void test2 {
System.out.println(email);
}
If you, like me, googled until here and the answer didn't serve to you, I'll just leave this: Use #BeforeEach

How can I defind that object set was already created?

I'm working with entity framework and mysql. We created a class
public class DataBaseContext : ObjectContext, IDbContext
There is a method
public IEnumerable<T> Find<T>(Func<T, bool> whereClause) where T : class
{
return CreateObjectSet<T>().Where(whereClause);
}
Is there a way not to create ObjectSet every time when I call the method? Can I check that it is already exists?
Whooooo. That is so bad method. You are passing Func<>, not Expression<Func<>>. It means that every time you execute your method EF will pull all records from database table mapped to T and execute your filtering in memory of your application - creating object set is the last thing you should be afraid of.
Anyway creating object set should not be expensive operation and if you don't want to create it every time you need to implement some "local caching" inside your object context instance.